fairchem
This commit is contained in:
@@ -1,6 +1,13 @@
|
|||||||
TOPK_RESULT = 5
|
TOPK_RESULT = 5
|
||||||
TIME_OUT = 30
|
TIME_OUT = 30
|
||||||
|
|
||||||
|
# MP Configuration
|
||||||
MP_API_KEY = "gfBp2in8qxm9Xm2SwLKFwNxDyZvNTAEt"
|
MP_API_KEY = "gfBp2in8qxm9Xm2SwLKFwNxDyZvNTAEt"
|
||||||
|
MP_ENDPOINT = "https://api.materialsproject.org/"
|
||||||
|
|
||||||
|
# Model
|
||||||
|
FAIRCHEM_MODEL_PATH = "/home/ubuntu/sas0/LYT/mars1215/mars_toolkit/model/eqV2_86M_omat_mp_salex.pt"
|
||||||
|
FMAX = 0.05
|
||||||
|
|
||||||
# MinIO configuration
|
# MinIO configuration
|
||||||
MINIO_ENDPOINT = "https://s3-api.siat-mic.com"
|
MINIO_ENDPOINT = "https://s3-api.siat-mic.com"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import logging
|
|||||||
from mp_api.client import MPRester
|
from mp_api.client import MPRester
|
||||||
from multiprocessing import Process, Manager
|
from multiprocessing import Process, Manager
|
||||||
from typing import Dict, Any, List
|
from typing import Dict, Any, List
|
||||||
from constant import MP_API_KEY, TIME_OUT, TOPK_RESULT
|
from constant import MP_ENDPOINT, MP_API_KEY, TIME_OUT, TOPK_RESULT
|
||||||
|
|
||||||
router = APIRouter(prefix="/mp", tags=["Material Project"])
|
router = APIRouter(prefix="/mp", tags=["Material Project"])
|
||||||
|
|
||||||
@@ -156,7 +156,7 @@ async def execute_search(search_args: Dict[str, Any], timeout: int = TIME_OUT) -
|
|||||||
def _search_worker(queue, api_key, **kwargs):
|
def _search_worker(queue, api_key, **kwargs):
|
||||||
"""搜索工作线程"""
|
"""搜索工作线程"""
|
||||||
try:
|
try:
|
||||||
mpr = MPRester(api_key)
|
mpr = MPRester(api_key, endpoint=MP_ENDPOINT)
|
||||||
result = mpr.materials.summary.search(**kwargs)
|
result = mpr.materials.summary.search(**kwargs)
|
||||||
queue.put([doc.model_dump() for doc in result])
|
queue.put([doc.model_dump() for doc in result])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
7
main.py
7
main.py
@@ -8,6 +8,7 @@ from fastapi import FastAPI
|
|||||||
import logging
|
import logging
|
||||||
from database.material_project_router import router as material_project_router
|
from database.material_project_router import router as material_project_router
|
||||||
from database.oqmd_router import router as oqmd_router
|
from database.oqmd_router import router as oqmd_router
|
||||||
|
from model.fairchem_router import router as fairchem_router, init_model
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.INFO,
|
level=logging.INFO,
|
||||||
@@ -16,5 +17,11 @@ logging.basicConfig(
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
@app.on_event("startup")
|
||||||
|
def startup_event():
|
||||||
|
init_model()
|
||||||
|
|
||||||
app.include_router(material_project_router)
|
app.include_router(material_project_router)
|
||||||
app.include_router(oqmd_router)
|
app.include_router(oqmd_router)
|
||||||
|
app.include_router(fairchem_router)
|
||||||
|
|||||||
@@ -1,74 +1,165 @@
|
|||||||
|
from fastapi import APIRouter, Body, Query
|
||||||
from fairchem.core import OCPCalculator
|
from fairchem.core import OCPCalculator
|
||||||
from ase.optimize import FIRE # Import your optimizer of choice
|
from ase.optimize import FIRE
|
||||||
from ase.filters import FrechetCellFilter # to include cell relaxations
|
from ase.filters import FrechetCellFilter
|
||||||
from ase.io import read
|
from ase.atoms import Atoms
|
||||||
from pymatgen.core import Structure
|
from ase.io import read, write
|
||||||
from pymatgen.ext.matproj import MPRester
|
from pymatgen.core.structure import Structure
|
||||||
from pymatgen.analysis.phase_diagram import PhaseDiagram, PDEntry
|
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
|
||||||
from pymatgen.entries.computed_entries import ComputedStructureEntry
|
from pymatgen.io.cif import CifWriter
|
||||||
from pymatgen.entries.mixing_scheme import MaterialsProjectDFTMixingScheme
|
import tempfile
|
||||||
|
import os
|
||||||
|
import boto3
|
||||||
|
from constant import FAIRCHEM_MODEL_PATH, MINIO_ENDPOINT, MINIO_ACCESS_KEY, MINIO_SECRET_KEY, MINIO_BUCKET, INTERNEL_MINIO_ENDPOINT, FMAX
|
||||||
|
from typing import Optional
|
||||||
|
import logging
|
||||||
|
import datetime
|
||||||
|
|
||||||
# 创建相图并计算形成能与 above hull energy 的函数
|
router = APIRouter(prefix="/fairchem", tags=["fairchem"])
|
||||||
def calculate_phase_diagram_properties(structure, total_energy, api_key):
|
logger = logging.getLogger(__name__)
|
||||||
"""
|
|
||||||
计算化合物的形成能和 above hull energy
|
# 初始化模型
|
||||||
参数:
|
calc = None
|
||||||
- formula (str): 化学式 (如 "CsPbBr3")
|
|
||||||
- total_energy (float): 化合物的总能量 (eV)
|
def init_model():
|
||||||
- mpr (MPRester): MPRester 实例
|
global calc
|
||||||
|
calc = OCPCalculator(checkpoint_path=FAIRCHEM_MODEL_PATH)
|
||||||
|
|
||||||
|
def convert_structure(input_format: str, content: str) -> Optional[Atoms]:
|
||||||
|
"""将输入内容转换为Atoms对象"""
|
||||||
|
try:
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=f".{input_format}", mode="w", delete=False) as tmp_file:
|
||||||
|
tmp_file.write(content)
|
||||||
|
tmp_path = tmp_file.name
|
||||||
|
|
||||||
|
atoms = read(tmp_path)
|
||||||
|
os.unlink(tmp_path)
|
||||||
|
return atoms
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to convert structure: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def generate_symmetry_cif(structure: Structure) -> str:
|
||||||
|
"""生成对称性CIF"""
|
||||||
|
analyzer = SpacegroupAnalyzer(structure)
|
||||||
|
structure = analyzer.get_refined_structure()
|
||||||
|
|
||||||
返回:
|
with tempfile.NamedTemporaryFile(suffix=".cif", mode="w+", delete=False) as tmp_file:
|
||||||
- formation_energy (float): 每个原子的形成能 (eV/atom)
|
cif_writer = CifWriter(structure, symprec=0.1, refine_struct=True)
|
||||||
- e_above_hull (float): 每个原子的 above hull energy (eV/atom)
|
cif_writer.write_file(tmp_file.name)
|
||||||
"""
|
tmp_file.seek(0)
|
||||||
chemsys = structure.chemical_system.split("-")
|
return tmp_file.read()
|
||||||
formula = structure.reduced_formula
|
|
||||||
with MPRester(api_key) as mpr:
|
|
||||||
# 获取化学系统中所有的相
|
|
||||||
# entries = mpr.get_entries_in_chemsys(elements=chemsys, additional_criteria={"thermo_types": ["GGA_GGA+U"]})
|
|
||||||
entries = mpr.get_entries_in_chemsys(elements=chemsys, additional_criteria={"thermo_types": ["GGA_GGA+U", "R2SCAN"]})
|
|
||||||
|
|
||||||
# 创建新计算结构的 PDEntry
|
def upload_to_minio(file_path: str, file_name: str) -> str:
|
||||||
pd_entry = PDEntry(composition=formula, energy=total_energy)
|
"""上传文件到MinIO并返回预签名URL"""
|
||||||
# entries.append(pd_entry)
|
try:
|
||||||
|
minio_client = boto3.client(
|
||||||
|
's3',
|
||||||
|
endpoint_url=MINIO_ENDPOINT if INTERNEL_MINIO_ENDPOINT == "" else INTERNEL_MINIO_ENDPOINT,
|
||||||
|
aws_access_key_id=MINIO_ACCESS_KEY,
|
||||||
|
aws_secret_access_key=MINIO_SECRET_KEY
|
||||||
|
)
|
||||||
|
|
||||||
|
bucket_name = MINIO_BUCKET
|
||||||
|
minio_client.upload_file(file_path, bucket_name, file_name, ExtraArgs={"ACL": "private"})
|
||||||
|
|
||||||
|
# 生成预签名 URL
|
||||||
|
url = minio_client.generate_presigned_url(
|
||||||
|
'get_object',
|
||||||
|
Params={'Bucket': bucket_name, 'Key': file_name},
|
||||||
|
ExpiresIn=3600
|
||||||
|
)
|
||||||
|
return url.replace(INTERNEL_MINIO_ENDPOINT, MINIO_ENDPOINT)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to upload to MinIO: {str(e)}")
|
||||||
|
raise RuntimeError(f"MinIO upload failed: {str(e)}") from e
|
||||||
|
|
||||||
scheme = MaterialsProjectDFTMixingScheme()
|
from io import StringIO
|
||||||
entries = scheme.process_entries(entries)
|
import sys
|
||||||
|
|
||||||
|
def optimize_structure(atoms: Atoms, output_format: str):
|
||||||
|
"""优化晶体结构"""
|
||||||
|
atoms.calc = calc
|
||||||
|
|
||||||
# 创建相图
|
# 捕获优化日志
|
||||||
pd = PhaseDiagram(entries + [pd_entry])
|
old_stdout = sys.stdout
|
||||||
|
sys.stdout = log_capture = StringIO()
|
||||||
|
|
||||||
# 计算形成能和 above hull energy
|
try:
|
||||||
formation_energy = pd.get_form_energy_per_atom(pd_entry)
|
dyn = FIRE(FrechetCellFilter(atoms))
|
||||||
e_above_hull = pd.get_e_above_hull(pd_entry)
|
dyn.run(fmax=FMAX)
|
||||||
|
total_energy = atoms.get_total_energy()
|
||||||
|
optimization_log = log_capture.getvalue()
|
||||||
|
finally:
|
||||||
|
sys.stdout = old_stdout
|
||||||
|
|
||||||
|
# 处理对称性
|
||||||
|
if output_format == "cif":
|
||||||
|
optimized_structure = Structure.from_ase_atoms(atoms)
|
||||||
|
content = generate_symmetry_cif(optimized_structure)
|
||||||
|
else:
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=f".{output_format}", mode="w+", delete=False) as tmp_file:
|
||||||
|
write(tmp_file.name, atoms)
|
||||||
|
tmp_file.seek(0)
|
||||||
|
content = tmp_file.read()
|
||||||
|
|
||||||
return formation_energy, e_above_hull
|
# 保存优化结果到临时文件
|
||||||
|
timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
||||||
|
file_name = f"optimized_structure_{timestamp}.{output_format}"
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=f".{output_format}", mode="w", delete=False) as tmp_file:
|
||||||
|
tmp_file.write(content)
|
||||||
|
tmp_path = tmp_file.name
|
||||||
|
try:
|
||||||
|
# 上传到MinIO
|
||||||
|
url = upload_to_minio(tmp_path, file_name)
|
||||||
|
return total_energy, content, url, optimization_log
|
||||||
|
finally:
|
||||||
|
os.unlink(tmp_path)
|
||||||
|
|
||||||
|
@router.post("/optimize_structure")
|
||||||
|
async def optimize_structure_endpoint(
|
||||||
|
content: str = Body(..., description="Input structure content"),
|
||||||
|
input_format: str = Query("cif", description="Input format (cif, poscar, json, xyz)"),
|
||||||
|
output_format: str = Query("cif", description="Output format (cif, poscar, json, xyz)")
|
||||||
|
):
|
||||||
|
# 转换输入结构
|
||||||
|
atoms = convert_structure(input_format, content)
|
||||||
|
if atoms is None:
|
||||||
|
return {
|
||||||
|
"status": "error",
|
||||||
|
"data": f"Invalid {input_format} content"
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 优化结构
|
||||||
|
total_energy, optimized_content, download_url, optimization_log = optimize_structure(atoms, output_format)
|
||||||
|
|
||||||
|
# 格式化返回结果
|
||||||
|
format_result = f"""
|
||||||
|
The following is the optimized crystal structure information:
|
||||||
|
|
||||||
atoms = read("/home/ubuntu/sas0/LYT/paper_dataset/mp_cif/CsPbBr3.cif") # Read in an atoms object or create your own structure
|
### Optimization Results (using FIRE(eqV2_86M) algorithm):
|
||||||
calc = OCPCalculator(checkpoint_path="/home/ubuntu/sas0/LYT/paper_dataset/mp_cif/meta_fairchem/eqV2_86M_omat_mp_salex.pt") # Path to downloaded checkpoint
|
```text
|
||||||
atoms.calc = calc
|
{optimization_log}
|
||||||
dyn = FIRE(FrechetCellFilter(atoms))
|
```
|
||||||
dyn.run(fmax=0.01)
|
Finally, the Total Energy is: {total_energy} eV
|
||||||
|
Due to length limitations, the complete {output_format.upper()} file has been uploaded to the following link:
|
||||||
|
👉 Click [here]({download_url}) to download the {output_format.upper()} file
|
||||||
|
|
||||||
total_energy = atoms.get_potential_energy()
|
Please ensure that the Optimization Results and download link are fully conveyed to the user, as this is very important for them.
|
||||||
print("Predicted Total Energy: ", total_energy)
|
"""
|
||||||
|
print(format_result)
|
||||||
|
return {
|
||||||
|
"status": "success",
|
||||||
|
"data": format_result
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Optimization failed: {str(e)}")
|
||||||
|
return {
|
||||||
|
"status": "error",
|
||||||
|
"data": str(e)
|
||||||
|
}
|
||||||
|
|
||||||
# 保存优化后的结构
|
if __name__ == "__main__":
|
||||||
atoms.write("optimized_structure.cif") # 保存为 CIF 文件
|
init_model()
|
||||||
print("Geometry optimization completed. Optimized structure saved as 'optimized_structure.cif'.")
|
|
||||||
|
|
||||||
# 从 ASE 转换为 Pymatgen 结构
|
|
||||||
optimized_structure = Structure.from_file("optimized_structure.cif")
|
|
||||||
|
|
||||||
api_key = "gfBp2in8qxm9Xm2SwLKFwNxDyZvNTAEt"
|
|
||||||
mpr = MPRester(api_key)
|
|
||||||
print(f"Chemical Formula: {optimized_structure .composition.reduced_formula}")
|
|
||||||
formation_energy, e_above_hull = calculate_phase_diagram_properties(
|
|
||||||
structure=optimized_structure,
|
|
||||||
total_energy=total_energy,
|
|
||||||
api_key=api_key
|
|
||||||
)
|
|
||||||
|
|
||||||
print(formation_energy, e_above_hull)
|
|
||||||
print()
|
|
||||||
|
|||||||
Reference in New Issue
Block a user