SeaWolf-AI's picture
Open Materials Challenge season 1 - solid-state electrolyte
3825ff2 verified
Raw
History Blame Contribute Delete
3.91 kB
# -*- coding: utf-8 -*-
"""μ ‘μˆ˜ 단계 검사.
μ—¬κΈ°μ„œ λ³΄λŠ” 것은 **ν˜•μ‹κ³Ό 자격**뿐이닀. μ‹€μ œ 채점(μ „κΈ°ν™”ν•™μ°½Β·λ¦¬νŠ¬ 이동)은 μ›Œμ»€κ°€ ν•œλ‹€.
μŠ€νŽ˜μ΄μŠ€λŠ” MP 데이터도 GPU 도 μ—†μœΌλ―€λ‘œ, μ—¬κΈ°μ„œ 물성을 νŒμ •ν•˜λŠ” μ²™ν•˜λ©΄ μ•ˆ λœλ‹€.
κ±°μ ˆμ€ μ°Έκ°€μžκ°€ **κ³ μΉ  수 μžˆλŠ” 것**μ—λ§Œ μ“΄λ‹€. 값을 μ–»μ§€ λͺ»ν•œ ν•­λͺ©μ€ 거절이 μ•„λ‹ˆλΌ 보λ₯˜λ‘œ λ‘”λ‹€.
"""
import re
# 채점 λŒ€μƒ μ›μ†Œ.
SUPPORTED = set("""
H Li Be B C N O F Na Mg Al Si P S Cl K Ca Sc Ti V Cr Mn Fe Co Ni Cu Zn
Ga Ge As Se Br Rb Sr Y Zr Nb Mo Tc Ru Rh Pd Ag Cd In Sn Sb Te I Cs Ba
La Ce Pr Nd Pm Sm Eu Gd Tb Dy Ho Er Tm Yb Lu Hf Ta W Re Os Ir Pt Au Hg
Tl Pb Bi Ac Th Pa U Np Pu
""".split())
_CIF_HINT = ("data_", "_cell_length_a", "_atom_site")
def parse_formula(text):
"""화학식을 μ‘°μ„±μœΌλ‘œ. **κ΄„ν˜Έλ₯Ό λ°˜λ“œμ‹œ μ²˜λ¦¬ν•œλ‹€.**
직접 μ •κ·œμ‹μœΌλ‘œ ν›‘μœΌλ©΄ `LiZr2(PO4)3` κ°€ P1O4 κ°€ λ˜μ–΄ 쑰성이 ν†΅μ§Έλ‘œ ν‹€λ¦°λ‹€.
Materials Project μžμ‹ μ΄ LGPS λ₯Ό `Li10Ge(PS6)2` 둜 μ μœΌλ―€λ‘œ 이건 μ˜ˆμ™Έκ°€ μ•„λ‹ˆλΌ 기본이닀.
"""
from pymatgen.core import Composition
c = Composition(str(text).strip())
return {str(k): float(v) for k, v in c.get_el_amt_dict().items()}
def check(text, cif=None, require_elements=("Li",), max_atoms=60,
supported=None, max_elements=6):
"""μ ‘μˆ˜ νŒμ •. (ok, reason, info) λ₯Ό λŒλ €μ€€λ‹€."""
supported = supported or SUPPORTED
raw = (text or "").strip()
if not raw:
return False, "화학식을 μž…λ ₯ν•΄ μ£Όμ„Έμš”.", {}
if len(raw) > 120:
return False, "화학식이 λ„ˆλ¬΄ κΉλ‹ˆλ‹€.", {}
if not re.match(r"^[A-Za-z0-9()\[\]\.\s]+$", raw):
return False, "화학식에 μ“Έ 수 μ—†λŠ” λ¬Έμžκ°€ μžˆμŠ΅λ‹ˆλ‹€.", {}
try:
comp = parse_formula(raw)
except Exception:
return False, "화학식을 ν•΄μ„ν•˜μ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€. 예: Li3YCl6, LiZr2(PO4)3", {}
if not comp:
return False, "화학식을 ν•΄μ„ν•˜μ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€.", {}
for el in require_elements:
if el not in comp:
return False, "리튬 μ „ν•΄μ§ˆ μ‹œμ¦Œμž…λ‹ˆλ‹€. %s λ₯Ό 포함해야 ν•©λ‹ˆλ‹€." % el, {}
bad = sorted(e for e in comp if e not in supported)
if bad:
return False, ("계산 κΈ°μ€€ μƒνƒœκ°€ μ—†λŠ” μ›μ†Œμž…λ‹ˆλ‹€: %s" % ", ".join(bad)), {}
if len(comp) > max_elements:
return False, "μ›μ†Œκ°€ %d 쒅을 λ„˜μŠ΅λ‹ˆλ‹€ (ν˜„μž¬ %d μ’…)." % (max_elements, len(comp)), {}
# **κΈ°μ•½ μ‘°μ„±**을 κΈ°μ€€μœΌλ‘œ μ‚ΌλŠ”λ‹€. Li3YCl6 와 Li9Y3Cl18 은 같은 λ¬Όμ§ˆμ΄λ―€λ‘œ 같은 κ²ƒμœΌλ‘œ
# μ„Έμ•Ό ν•œλ‹€. 원본 μ‘°μ„±μœΌλ‘œ μ—΄μ‡ λ₯Ό λ§Œλ“€λ©΄ 배수만 λ°”κΏ” 같은 λ¬Όμ§ˆμ„ λͺ‡ 번이고 올릴 수 μžˆλ‹€.
from math import gcd
from functools import reduce
integral = all(abs(v - round(v)) < 1e-6 for v in comp.values())
if integral:
ints = {k: int(round(v)) for k, v in comp.items()}
g = reduce(gcd, ints.values()) or 1
reduced = {k: v // g for k, v in ints.items()}
else:
reduced = dict(comp) # λΉ„μ •μˆ˜ μ‘°μ„±(도핑 λ“±)은 κ·ΈλŒ€λ‘œ λ‘”λ‹€
n_atom = sum(reduced.values())
if n_atom > max_atoms:
return False, ("κΈ°μ•½ μ‘°μ„±μ˜ μ›μž μˆ˜κ°€ %d 개둜 μƒν•œ %d λ₯Ό λ„˜μŠ΅λ‹ˆλ‹€."
% (n_atom, max_atoms)), {}
info = {"composition": comp, "reduced": reduced,
"n_atoms": n_atom, "n_elements": len(comp)}
if cif:
c = str(cif)
if len(c) > 400_000:
return False, "ꡬ쑰 파일이 λ„ˆλ¬΄ ν½λ‹ˆλ‹€ (400KB μƒν•œ).", {}
if not any(h in c for h in _CIF_HINT):
return False, "ꡬ쑰 파일이 CIF ν˜•μ‹μœΌλ‘œ 보이지 μ•ŠμŠ΅λ‹ˆλ‹€.", {}
info["has_structure"] = True
return True, "", info