Open
Description
I try to call atomate2 via jobflow-remote to compute the 3rd-order elastic tensor with the following code:
import numpy as np
from jobflow import run_locally
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
from atomate2.vasp.flows.elastic import ElasticMaker
from atomate2.vasp.powerups import (
update_user_incar_settings,
update_user_potcar_functional,
update_user_kpoints_settings,
)
from pymatgen.io.vasp.inputs import Kpoints
from pymatgen.core import Structure
from mp_api.client import MPRester
from atomate2.vasp.powerups import add_metadata_to_flow
#https://github.com/materialsproject/atomate2/issues/1014#issuecomment-2454955891
from atomate2.vasp.flows.mp import MPGGADoubleRelaxMaker, MPGGAStaticMaker
from jobflow import Flow
from jobflow_remote import submit_flow
# Based on the following checking:
#werner@x13dai-t:~/Public/hpc/vasp/pot$ ug -r 'TITEL = PAW_PBE Pt ' -g POTCAR
#potpaw_PBE.54/Pt/POTCAR: TITEL = PAW_PBE Pt 04Feb2005
#potpaw_PBE.64/Pt/POTCAR: TITEL = PAW_PBE Pt 04Feb2005
#potpaw_PBE/Pt/POTCAR: TITEL = PAW_PBE Pt 05Jan2001
#potpaw_PBE/Pt_new/POTCAR: TITEL = PAW_PBE Pt 04Feb2005
#potpaw_PBE/Pt_ZORA/POTCAR: TITEL = PAW_PBE Pt 05Jan2001
#potpaw_PBE.52/Pt/POTCAR: TITEL = PAW_PBE Pt 04Feb2005
#potpaw_LDA_PBE_52_54_orig/potpaw_PBE.54/Pt/POTCAR: TITEL = PAW_PBE Pt 04Feb2005
#potpaw_LDA_PBE_52_54_orig/potpaw_PBE.52/Pt/POTCAR: TITEL = PAW_PBE Pt 04Feb2005
#So, all the following versions are the same one in this case:
#user_potcar_functional = "PBE_64"
#user_potcar_functional = "PBE_52"
#user_potcar_functional = "PBE_54"
#user_potcar_functional = "PBE_52_W_HASH"
#user_potcar_functional = "PBE_54_W_HASH"
#要复现MP,根据下面的检查,应该设置potcar版本与MP官方使用的一致。
#In [4]: from mp_api.client import MPRester
# ...:
# ...: material_id = "mp-126"
# ...: with MPRester() as mpr:
# ...: # 获取elastic数据来获取task id
# ...: elasticity_doc = mpr.materials.elasticity.search(material_ids=[material_id])
# ...: opt_id = elasticity_doc[0].fitting_data.optimization_task.string
# ...:
# ...: # 使用task id获取详细信息
# ...: opt_doc = mpr.materials.tasks.search([opt_id],
# ...: fields=["input", "calcs_reversed"])
# ...:
# ...: # 提取 potcar_spec
# ...: potcar_spec = opt_doc[0].calcs_reversed[0].input.potcar_spec
# ...: print(f"POTCAR specifications: {potcar_spec}")
# ...:
#Retrieving ElasticityDoc documents: 100%|█████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 7543.71it/s]
#Retrieving TaskDoc documents: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 9892.23it/s]
#POTCAR specifications: [PotcarSpec(titel='PAW_PBE Pt 05Jan2001', hash='a604ea3c6a9cc23c739b762f625cf449', summary_stats=None)]
#In [4]: opt_doc[0].calcs_reversed[0].input.potcar_spec
#Out[4]: [PotcarSpec(titel='PAW_PBE Pt 05Jan2001', hash='a604ea3c6a9cc23c739b762f625cf449', summary_stats=None)]
#https://github.com/materialsproject/api/issues/944#issuecomment-2503063154
user_potcar_functional = "PBE"
# 实际测试表明,不同的vasp版本的计算结果是一致的(差别很小,可以忽略):
#In [5]: # MP所使用的vasp版本:
# ...: for calc in opt_doc[0].calcs_reversed:
# ...: print(calc.vasp_version)
# ...:
#5.4.4
#5.4.4
material_id = "mp-126"
with MPRester() as mpr:
# Obtain elastic data and the related task IDs.
elasticity_doc = mpr.materials.elasticity.search(material_ids=[material_id])
opt_id = elasticity_doc[0].fitting_data.optimization_task.string
first_deform_id = str(elasticity_doc[0].fitting_data.deformation_tasks[0])
opt_doc = mpr.materials.tasks.search([opt_id],
fields=["input", "orig_inputs", "calcs_reversed"])
deform_dep_doc = mpr.materials.tasks.search([first_deform_id],
fields=["input", "orig_inputs", "calcs_reversed"])
structure = mpr.materials.get_structure_by_material_id(material_id)
#ElasticMaker的flow中有 structure_to_conventional的job,所以下面的步骤应该不是必需的:
structure = SpacegroupAnalyzer(structure).get_conventional_standard_structure()
# Process kpoints settings
#opt_kpoints_settings = opt_doc[0].orig_inputs.kpoints
#deform_kpoints_settings = deform_dep_doc[0].orig_inputs.kpoints
# Use calcs-reversed to do the trick:
#https://materialsproject.github.io/atomate2/user/docs_schemas_emmet.html#calcs-reversed
opt_kpoints_settings = opt_doc[0].calcs_reversed[0].input.kpoints
deform_kpoints_settings = deform_dep_doc[0].calcs_reversed[0].input.kpoints
# Process INCAR settings
# Adopt the following strategy: based on the keys in `orig_inputs.incar`, use the corresponding values from `input.parameters`.
# This can be simplified into a one-liner dictionary comprehension:
#opt_incar_settings = {key: opt_doc[0].input.parameters[key] for key in opt_doc[0].orig_inputs.incar.keys() if key in opt_doc[0].input.parameters}
#deform_incar_settings = {key: deform_dep_doc[0].input.parameters[key] for key in deform_dep_doc[0].orig_inputs.incar.keys() if key in deform_dep_doc[0].input.parameters}
# Or, for better readability, you can first define variables and then achieve it as following:
#orig_incar = opt_doc[0].orig_inputs.incar
#input_params = opt_doc[0].input.parameters
#opt_incar_settings = {key: input_params[key] for key in orig_incar.keys() if key in input_params}
#orig_incar = deform_dep_doc[0].orig_inputs.incar
#input_params = deform_dep_doc[0].input.parameters
#deform_incar_settings = {key: input_params[key] for key in orig_incar.keys() if key in input_params}
# Use calcs-reversed to do the trick:
#https://materialsproject.github.io/atomate2/user/docs_schemas_emmet.html#calcs-reversed
opt_incar_settings = opt_doc[0].calcs_reversed[0].input.incar
deform_incar_settings = deform_dep_doc[0].calcs_reversed[0].input.incar
# Handle MAGMOM settings correctly.
if isinstance(opt_incar_settings.get("MAGMOM", []), list):
magmoms = {}
for idx, site in enumerate(structure):
if idx < len(opt_incar_settings.get("MAGMOM", [])):
magmoms[site.species_string] = opt_incar_settings["MAGMOM"][idx]
opt_incar_settings["MAGMOM"] = magmoms
if isinstance(deform_incar_settings.get("MAGMOM", []), list):
magmoms = {}
for idx, site in enumerate(structure):
if idx < len(deform_incar_settings.get("MAGMOM", [])):
magmoms[site.species_string] = deform_incar_settings["MAGMOM"][idx]
deform_incar_settings["MAGMOM"] = magmoms
#Re: The meaning of "GGA = --".
#https://www.vasp.at/forum/viewtopic.php?p=29678#p29678
#If the GGA tag is not explicitly specified in INCAR, then the functional from the POTCAR (usually PBE) is used. With older VASP versions, this was indicated with "GGA = --" in OUTCAR. Since VASP.6.4.3 this is properly indicated with "GGA = PE".
#In [3]: opt_doc[0].input.parameters['GGA']
#Out[3]: '--'
#In [34]: opt_doc[0].calcs_reversed[0].input.potcar
#Out[34]: ['Pt']
#In [35]: opt_doc[0].calcs_reversed[0].input.potcar_spec
#Out[35]: [PotcarSpec(titel='PAW_PBE Pt 05Jan2001', hash='a604ea3c6a9cc23c739b762f625cf449', summary_stats=None)]
#In [36]: opt_doc[0].calcs_reversed[0].input.potcar_type
#Out[36]: ['PAW_PBE']
flow = ElasticMaker(
# 展示3阶弹性张量的计算:
order=3, # Order of the tensor expansion to be determined. Can be either 2 or 3. default value is 2
bulk_relax_maker=MPGGADoubleRelaxMaker(), # 使用MP的双重优化设置
elastic_relax_maker=MPGGAStaticMaker(), # 使用MP的静态计算设置
).make(structure, conventional=True)
flow = update_user_potcar_functional(flow, user_potcar_functional)
flow = update_user_kpoints_settings(flow, opt_kpoints_settings, name_filter="MP GGA relax")
flow = update_user_kpoints_settings(flow, deform_kpoints_settings, name_filter="MP GGA static")
flow = update_user_incar_settings(flow, opt_incar_settings, name_filter="MP GGA relax" )
flow = update_user_incar_settings(flow, deform_incar_settings, name_filter="MP GGA static")
# Add metadata.
#flow = add_metadata_to_flow(
# flow=flow,
# additional_fields={
# "mp_id": "mp-126",
# "submission_label": "mp-126_elastic_tensor-GGA-PE"
# },
#)
# Run the flow
#responses = run_locally(flow, create_folders=True, ensure_success=True)
#elastic_output = responses[flow.jobs[-1].uuid][1].output
#matrix = np.array(elastic_output.elastic_tensor.ieee_format)
#np.set_printoptions(precision=4, suppress=True)
#print(matrix)
# 基于jobflow-remote:
jf_flow = Flow(flow, name='reproduce-mp-elastic')
# 提交计算
#https://github.com/Matgenix/jobflow-remote/issues/277#issuecomment-2759941413
#$ cd "/home/werner/Public/repo/github.com/Matgenix/qtoolkit.git" && ug -i 'SBATCH --mem'
#src/qtoolkit/io/slurm.py
# 157: #SBATCH --mem=$${mem}
# 158: #SBATCH --mem-per-cpu=$${mem_per_cpu}
resources = {
"nodes": 1,
"ntasks": 16,
"mem": "80G",
"partition": "batch",
"time": "2400:00:00"
}
#https://github.com/Matgenix/jobflow-remote/issues/277#issuecomment-2759918475
print(submit_flow(jf_flow, project='std', worker="vasp_slurm", resources=resources, exec_config="vasp_slurm"))
But the job fit_elastic_tensor
in the flow failed as shown below:
In [11]: ! jf -p std job info 211f2834-2c16-4ed4-be09-6c59085abdfd
The selected project is std from config file /home/werner/.jfremote/std.yaml
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ db_id = '390' │
│ uuid = '211f2834-2c16-4ed4-be09-6c59085abdfd' │
│ index = 1 │
│ name = 'fit_elastic_tensor' │
│ state = 'FAILED' │
│ error = Traceback (most recent call last): │
│ File "/home/werner/Public/repo/github.com/Matgenix/jobflow-remote.git/src/jobflow_remote/jobs/run.py", line 69, in run_remote_job │
│ response = job.run(store=store) │
│ ^^^^^^^^^^^^^^^^^^^^ │
│ File "/home/werner/.pyenv/versions/3.11.1/envs/datasci/lib/python3.11/site-packages/jobflow/core/job.py", line 604, in run │
│ response = function(*self.function_args, **self.function_kwargs) │
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ │
│ File "/home/werner/Public/repo/github.com/materialsproject/atomate2.git/src/atomate2/common/jobs/elastic.py", line 245, in fit_elastic_tensor │
│ return ElasticDocument.from_stresses( │
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ │
│ File "/home/werner/Public/repo/github.com/materialsproject/atomate2.git/src/atomate2/common/schemas/elastic.py", line 267, in from_stresses │
│ raw=result.voigt.tolist(), ieee_format=ieee.voigt.tolist() │
│ ^^^^^^^^^^^^^^^^^^^ │
│ AttributeError: 'list' object has no attribute 'tolist' │
│ remote = {'step_attempts': 0, 'process_id': '998', 'prerun_cleanup': False} │
│ created_on = '2025-06-06 13:21' │
│ updated_on = '2025-06-06 14:08' │
│ start_time = '2025-06-06 14:08' │
│ end_time = '2025-06-06 14:08' │
│ metadata = {} │
│ run_dir = '/home/werner/Desktop/std_vasp_slurm/21/1f/28/211f2834-2c16-4ed4-be09-6c59085abdfd_1' │
│ parents = ['485e627d-8e43-412a-994d-3fe6572b8d8d', '2615a673-a0c5-4805-9cc9-3acb49570594', '804865f9-d32e-4268-a623-e12d34425c59'] │
│ priority = 0 │
│ worker = 'vasp_slurm' │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
See below for the related discussions:
https://www.vasp.at/forum/viewtopic.php?p=31597#p31597
Neraaz/HTESP#1 (comment)
Regards,
Zhao
Metadata
Metadata
Assignees
Labels
No labels