Hi all!
I posted a similar question on the atomate discussion group, but I got no answer yet and think that it might be better suited here anyway.
My situation is this: I want to write a complex workflow for interfaces, where I want to download bulk structures from an online repository (e.g. using MPRester) turn them into slabs and than combine the slabs into interfaces. On the way I want to relax the slabs and get some information from them, e.g. surface energies.
My Problem now is that I want to run previously defined Fireworks (like the OptimizeFW from atomate), but I have to pass a structure to them when I put them in my Workflow, which I of course do not have at that time.
A minimal example would be this (full example with all imports and definitions of FireTasks and functions is at the end of this post):
#input dictionary to be set as part of the spec:
material = {'formula': 'FeCo', 'miller': [0, 0, 1], 'min_thickness': 10}
#first Firework fetches the structure from the MaterialProject database and puts it in the fw_spec
#under fw_spec['structures'][material['formula']]
fw_1 = Firework(FT_FetchStructureFromInput(input_dict_name='material'),
spec={'material': material})
#second Firework relaxes the structure (not really necessary here, other than for the example)
fw_2 = OptimizeFW(fw_spec['structures'][material['formula']], parents=[fw_1])
#Define the Workflow
wf = Workflow([fw_1, fw_2])
#finally, set up the LaunchPad and add the workflow to it
lpad = LaunchPad.auto_load() # loads this based on the FireWorks configuration
lpad.add_wf(wf)
#Run the workflow
rapidfire(lpad)
Of course I get the error that fw_spec is not defined:
[autoreload of Example_for_Forum failed: Traceback (most recent call last):
File “/home/mwo/FireWorks/atomate_env/lib/python3.7/site-packages/IPython/extensions/autoreload.py”, line 245, in check
superreload(m, reload, self.old_objects)
File “/home/mwo/FireWorks/atomate_env/lib/python3.7/site-packages/IPython/extensions/autoreload.py”, line 394, in superreload
module = reload(module)
File “/home/mwo/FireWorks/atomate_env/lib/python3.7/imp.py”, line 314, in reload
return importlib.reload(module)
File “/home/mwo/FireWorks/atomate_env/lib/python3.7/importlib/init.py”, line 169, in reload
_bootstrap._exec(spec, module)
File “”, line 630, in _exec
File “”, line 728, in exec_module
File “”, line 219, in _call_with_frames_removed
File “/home/mwo/FireWorks/FireFlow/fireflow/Example_for_Forum.py”, line 105, in
fw_2 = OptimizeFW(fw_spec[‘structures’][material[‘formula’]], parents=[fw_1])
NameError: name ‘fw_spec’ is not defined
]
Traceback (most recent call last):File “/home/mwo/FireWorks/FireFlow/fireflow/Example_for_Forum.py”, line 105, in
fw_2 = OptimizeFW(fw_spec[‘structures’][material[‘formula’]], parents=[fw_1])NameError: name ‘fw_spec’ is not defined
Is there a simple way to get access to the fw_spec? Or am I thinking in completely the wrong direction and using the fw_spec wrong? I was previously working with AiiDA, where there each workflow has a ‘context’, which is a dictionary that is accessible for all methods in the workflow. Just to illustrate why my first idea was to do something like this.
I would be very greatful if you could help me in this matter,
Michael
Here is the full example code:
from fireworks import FWAction, FiretaskBase, Firework, Workflow, LaunchPad
from fireworks.utilities.fw_utilities import explicit_serialize
from atomate.vasp.fireworks import OptimizeFW
from fireworks.core.rocket_launcher import rapidfire
def GetLowEnergyStructure(chem_formula, MP_ID=None, PrintInfo=False):
"""
A function that searches the MaterialsProject Database
for structures that match the given chemical formula
and selcts the one with the lowest formation energy
per atom. If several
Inputs:
chem_formula (str): Required input of a chemical formula
e.g.: NaCl, Fe2O3, SiO, FeCW
MP_ID (str): Optional Input of Materials Project ID
of the exact desired structure
e.g. 'mp-990448'
PrintInfo (bool): Optional variable defaults to "False".
if set to "True", will print some
information about how many structures
have been found and the ID of the selcted
one.
Returns: pymatgen Structure object and associated MP_ID
"""
from pymatgen import MPRester
# load structure from MaterialsProjct Website
with MPRester() as mpr:
if MP_ID:
struct = mpr.get_structure_by_material_id(MP_ID)
return struct, MP_ID
else:
id_list = mpr.query(criteria={'pretty_formula': chem_formula,
'e_above_hull': 0.0},
properties=['material_id'])
if id_list == []:
raise NameError('{} has not been found in the MaterialsProject'
'database'.format(chem_formula))
else:
MP_ID = id_list[0]['material_id']
struct = mpr.get_structure_by_material_id(MP_ID)
return struct, MP_ID
@explicit_serialize
class FT_FetchStructureFromInput(FiretaskBase):
"""Fetches a structure from the MP database and puts it in the spec.
Uses the helper function GetLowEnergyStructure to get the structure for a
given chemical formula with the lowest formation energy. If a MP_ID is
also given, this exact structure will be fetched. The 'structures' key of
the spec is updated to include the fetched structur under the formula key.
Parameters
----------
input_dict_name: str
Required input dictionary where a chemical formula for the structure
is given under the key 'formula'. The input key 'MP_ID' must also be
present but might be 'None' and will then not be used.
---------
Yields
------
updated structures dictionary in the spec.
------
"""
_fw_name = 'Fetch Structure From Input'
required_params = ['input_dict_name']
def run_task(self, fw_spec):
input_dict = fw_spec[self['input_dict_name']]
if 'mp_id' in input_dict:
mp_id = input_dict['mp_id']
else:
mp_id = None
structure, MP_ID = GetLowEnergyStructure(input_dict['formula'],
MP_ID=mp_id,
PrintInfo=False)
if 'structures' in fw_spec:
structures = fw_spec['structures']
else:
structures = {}
structures[input_dict['formula']] = structure
spec = fw_spec
spec.update({'structures': structures})
return FWAction(update_spec = spec)
material = {'formula': 'FeCo', 'miller': [0, 0, 1], 'min_thickness': 10}
fw_1 = Firework(FT_FetchStructureFromInput(input_dict_name='material'),
spec={'material': material})
fw_2 = OptimizeFW(fw_spec['structures'][material['formula']], parents=[fw_1])
wf = Workflow([fw_1, fw_2])
#finally, instatiate the LaunchPad and add the workflow to it
lpad = LaunchPad.auto_load() # loads this based on the FireWorks configuration
lpad.add_wf(wf)
rapidfire(lpad)