materialsproject / atomate2

atomate2 is a library of computational materials science workflows
https://materialsproject.github.io/atomate2/
Other
169 stars 96 forks source link

`ElasticMaker` flow fails with `MAGMOM` settings when using MP API data. #1049

Open hongyi-zhao opened 2 weeks ago

hongyi-zhao commented 2 weeks ago

The testing code below is adapted from my example here:

from mp_api.client import MPRester
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
from atomate2.vasp.flows.mp import MPGGADoubleRelaxMaker
from atomate2.vasp.powerups import (
    update_user_incar_settings,
    update_user_potcar_functional,
)
from jobflow import run_locally

# Set POTCAR functional
user_potcar_functional = "PBE_64"

# Get structure from MP
material_id = "mp-126"
with MPRester() as mpr:
    # First get the task id from the materials doc
    materials = mpr.materials.search(material_ids=[material_id], fields=["task_ids"])
    task_id = materials[0].task_ids[0]

    # Now get the calculation that includes MAGMOM settings
    tasks = mpr.materials.tasks.search(task_ids=[task_id], 
        fields=["input.incar", "calcs_reversed"])

    # Get the INCAR settings from the calculation
    incar_settings = tasks[0].calcs_reversed[0].input.incar

    # Get the structure and convert to conventional cell
    structure = mpr.materials.get_structure_by_material_id(material_id)
    structure = SpacegroupAnalyzer(structure).get_conventional_standard_structure()

# Convert MAGMOM list to dictionary format
#if "MAGMOM" in incar_settings:
#    print("Original MAGMOM settings:", incar_settings["MAGMOM"])
#    print("Number of sites in structure:", len(structure))
#    
#    # Get unique species list
#    species_list = [site.species_string for site in structure]
#    unique_species = sorted(set(species_list))
#    print("Unique species in structure:", unique_species)
#    
#    # Set all species to the first MAGMOM value from the original calculation
#    magmom_dict = {specie: incar_settings["MAGMOM"][0] for specie in unique_species}
#    incar_settings["MAGMOM"] = magmom_dict
#    print("MAGMOM dictionary:", magmom_dict)

# Create a simple flow with just the relax
flow = MPGGADoubleRelaxMaker().make(structure)

# Update POTCAR settings
flow = update_user_potcar_functional(flow, user_potcar_functional)

# Update INCAR settings
flow = update_user_incar_settings(flow, incar_settings)

# Run the flow
responses = run_locally(flow, create_folders=True, ensure_success=True)

But the above code will trigger the following error:

Retrieving MaterialsDoc documents: 100%|████████████████████████████████| 1/1 [00:00<00:00, 12865.96it/s]
Retrieving TaskDoc documents: 100%|█████████████████████████████████████| 1/1 [00:00<00:00, 11715.93it/s]
Retrieving MaterialsDoc documents: 100%|████████████████████████████████| 1/1 [00:00<00:00, 11848.32it/s]
/home/werner/.pyenv/versions/3.11.1/envs/datasci/lib/python3.11/site-packages/pymatgen/io/vasp/sets.py:288: BadInputSetWarning: Overriding the POTCAR functional is generally not recommended  as it significantly affects the results of calculations and compatibility with other calculations done with the same input set. Note that some POTCAR symbols specified in the configuration file may not be available in the selected functional.
  warnings.warn(
2024-11-10 08:41:06,924 INFO Started executing jobs locally
2024-11-10 08:41:06,926 INFO Starting job - MP GGA relax 1 (ecdb12aa-5e73-4d2b-97b3-d6deb29ce066)
2024-11-10 08:41:06,928 INFO MP GGA relax 1 failed with exception:
Traceback (most recent call last):
  File "/home/werner/.pyenv/versions/3.11.1/envs/datasci/lib/python3.11/site-packages/jobflow/managers/local.py", line 114, in _run_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 600, in run
    response = function(*self.function_args, **self.function_kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/werner/Public/repo/github.com/materialsproject/atomate2.git/src/atomate2/vasp/jobs/base.py", line 219, in make
    write_vasp_input_set(
  File "/home/werner/Public/repo/github.com/materialsproject/atomate2.git/src/atomate2/vasp/files.py", line 190, in write_vasp_input_set
    vis = input_set_generator.get_input_set(
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/werner/.pyenv/versions/3.11.1/envs/datasci/lib/python3.11/site-packages/pymatgen/io/vasp/sets.py", line 476, in get_input_set
    incar=self.incar,
          ^^^^^^^^^^
  File "/home/werner/.pyenv/versions/3.11.1/envs/datasci/lib/python3.11/site-packages/pymatgen/io/vasp/sets.py", line 572, in incar
    if uic_magmom := self.user_incar_settings.get("MAGMOM", {}).get(site.species_string):
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'list' object has no attribute 'get'

2024-11-10 08:41:06,928 INFO Finished executing jobs locally
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
Cell In[6], line 56
     53 flow = update_user_incar_settings(flow, incar_settings)
     55 # Run the flow
---> 56 responses = run_locally(flow, create_folders=True, ensure_success=True)

File ~/.pyenv/versions/3.11.1/envs/datasci/lib/python3.11/site-packages/jobflow/managers/local.py:181, in run_locally(flow, log, store, create_folders, root_dir, ensure_success, allow_external_references, raise_immediately)
    178 logger.info("Finished executing jobs locally")
    180 if ensure_success and not finished_successfully:
--> 181     raise RuntimeError("Flow did not finish running successfully")
    183 return dict(responses)

RuntimeError: Flow did not finish running successfully

Is there a proper way to handle MAGMOM settings when copying from MP calculations without requiring further user adjustment, say, the one done by the code snippet Convert MAGMOM list to dictionary format shown above?

Regards, Zhao

esoteric-ephemera commented 5 days ago

To update the MAGMOMs via user_incar_settings, you have to specify it as a dict (doesn't make the most sense I know). The dict should be {species: magmom}, like {"Zn2+": 0.5}

The alternative is to add the magmoms to the site properties of a structure, and atomate2 will automatically read those in as MAGMOM:

structure.add_site_property("magmom", incar_settings.pop("magmom") )
hongyi-zhao commented 4 days ago

In general, it should be used this way:

def apply_magmoms_from_incar(structure, incar_settings):
    """
    Apply magnetic moments from INCAR settings to structure, handling multiple species.

    Args:
        structure: pymatgen Structure object
        incar_settings: dict containing INCAR settings including MAGMOM
    """
    if "MAGMOM" not in incar_settings:
        print("No MAGMOM settings found in INCAR")
        return structure

    magmoms = incar_settings.pop("MAGMOM")

    # Check if number of MAGMOMs matches number of sites
    if len(magmoms) == len(structure):
        # Direct 1:1 mapping case
        structure.add_site_property("magmom", magmoms)
    else:
        # Need to expand magmoms based on unique species
        # Get unique species and their counts
        species_counts = structure.composition.as_dict()

        # Create expanded magmom list matching structure sites
        site_magmoms = []
        magmom_idx = 0

        # Iterate through sites and assign magmoms
        for site in structure:
            site_magmoms.append(magmoms[magmom_idx])
            # Check if we need to move to next magmom value
            next_species_sites = structure.indices_from_symbol(site.species_string)
            if len(site_magmoms) == max(next_species_sites) + 1:
                magmom_idx += 1

        structure.add_site_property("magmom", site_magmoms)

    return structure

# 使用示例:
structure = apply_magmoms_from_incar(structure, incar_settings)

# 验证结果
print("Site properties:", structure.site_properties)
print("\nDetailed site information:")
for i, site in enumerate(structure):
    print(f"Site {i}: {site.species_string} at {site.frac_coords}, "
          f"magmom = {site.properties.get('magmom')}")

The output:

No MAGMOM settings found in INCAR
Site properties: {'magmom': [-0.002, -0.002, -0.002, -0.002]}

Detailed site information:
Site 0: Pt at [0. 0. 0.], magmom = -0.002
Site 1: Pt at [0.  0.5 0.5], magmom = -0.002
Site 2: Pt at [0.5 0.  0.5], magmom = -0.002
Site 3: Pt at [0.5 0.5 0. ], magmom = -0.002

The updated result of structure.site_properties:

In [24]: structure.site_properties
Out[24]: {'magmom': [-0.002, -0.002, -0.002, -0.002]}