Source code for apps.activityMaps

# -*- coding: utf-8
import copy
import json
import os
import os.path
import pprint
import zipfile
import time
import datetime


# workaround to work on both Python 2 and Python 3
try:
    import io as StringIO
except ImportError:
    import StringIO

import numpy as np

import requests
import flask

import ase.atoms
import ase.io
import ase.build

activityMaps = flask.Blueprint('activityMaps', __name__)

GRAPHQL_ROOT = 'http://api.catalysis-hub.org/graphql'
ROOT = 'http://api.catalysis-hub.org/'


[docs]class ReactionModel(object): def __init__(self, xlabel=None, ylabel=None, zlabel=None, reference=''): self.xlabel = xlabel self.ylabel = ylabel self.zlabel = zlabel self.reference = reference
[docs] def get_raw_systems(self, filters): pass
[docs] def get_xyz(self, systems): pass
[docs]def graphql_query(products='products: "O"', reactants='', facet='', limit=5000): query = {'query': """{{ reactions(first: {limit}, {reactants}{products}{facet}) {{ edges {{ node {{ reactionEnergy sites facet chemicalComposition reactionSystems {{ name aseId }} }} }} }} }}""".format(**locals()).replace('\n', '')} response = requests.get(GRAPHQL_ROOT, query).json() return response
[docs]@activityMaps.route('/systems/', methods=['GET', 'POST']) def systems(request=None): """ GET: Get systems for given reactions Args: activityMap(str): request Map like OER, NRR, HER. Defaults to CO_Hydrogenation_111. Returns: dict: The corresponding systems in the database. * reference(str): Reference for activity map. * systems(list): Corresponding systems. Examples: .. code-block:: bash curl {ROOT}/apps/activityMaps/systems/?activityMap=OER curl {ROOT}/apps/activityMaps/systems/?activityMap=CO_Hydrogenation_111 .. code-block:: json { "reference": "[1] Friebel, Daniel, Mary W. Louie, Michal Bajdich, Kai E. Sanwald, Yun Cai, Anna M. Wise, Mu-Jeng Cheng et al. \"Identification of highly active Fe sites in (Ni, Fe) OOH for electrocatalytic water splitting.\" Journal of the American Chemical Society 137, no. 3 (2015): 1305-1313. DOI: 10.1021/ja511559d [2] Man, Isabela C., Hai\u2010Yan Su, Federico Calle\u2010Vallejo, Heine A. Hansen, Jos\u00e9 I. Mart\u00ednez, Nilay G. Inoglu, John Kitchin, Thomas F. Jaramillo, Jens K. N\u00f8rskov, and Jan Rossmeisl. \"Universality in oxygen evolution electrocatalysis on oxide surfaces.\" ChemCatChem 3, no. 7 (2011): 1159-1165. DOI: 10.1002/cctc.201000397", "systems": [ { "facet": "3ML", "formula": "Ir16Sr4O51", "uid": "5b0b436e4d3d07c3fb7a4cee6d5975f1", "x": 1.5028540934200003, "y": 1.3901226701799998, "z": -0.3208143060400004 }, { "facet": "100", "formula": "Ir24O53", "uid": "b33747e9868b9514639752f1b58e2f03", "x": 1.4204331210799999, "y": 0.44616836241, "z": -0.4164322559 }, ] } """ request = flask.request if request is None else request if isinstance(request.args, str): request.args = json.loads(request.args) # unpack arguments activityMap = str(request.args.get('activityMap', 'OER')) CACHE_FILE = 'reaction_systems_{activityMap}.json'.format(**locals()) short_systems = [] raw_systems = {} labels = {} if activityMap == 'OER': def overpotential(doh, do, dooh=None): def ooh_oh_scaling(doh): # like ambars # dooh=0.5*doh + 3.0 #O # normal one dooh = doh + 3.2 return dooh if dooh is None: dooh = ooh_oh_scaling(doh) dg14 = [doh, do - doh, dooh - do, -dooh + 4.92] m = max(dg14) return m - 1.23 reactants = ['OOH', 'OH', 'O', ] if os.path.exists(CACHE_FILE): with open(CACHE_FILE) as infile: raw_systems = json.loads(infile.read()) else: raw_systems = {} for reactant in reactants: raw_systems[reactant] = graphql_query( products='products: "' + reactant + '", ') with open(CACHE_FILE, 'w') as outfile: outfile.write(json.dumps(raw_systems, )) systems = {} for reactant in raw_systems: for edge in raw_systems[reactant]['data']['reactions']['edges']: star_list = list(filter(lambda x: x['name'] == 'star', edge[ 'node']['reactionSystems'])) if len(star_list) == 0: continue star = star_list[0] uniqueId = star['aseId'] systems.setdefault(uniqueId, {})['facet'] = edge[ 'node']['facet'] systems.setdefault(uniqueId, {})['chemicalComposition'] = edge[ 'node']['chemicalComposition'] systems.setdefault( uniqueId, {}).setdefault( 'reactants', {})[reactant] = { 'systems': edge['node']['reactionSystems'], 'energy': edge['node']['reactionEnergy'], } for uid in systems: if len(systems[uid]['reactants'].keys()) == len(reactants): energies = {} formula = systems[uid]['chemicalComposition'] facet = systems[uid]['facet'] for reactant in systems[uid]['reactants']: star = list(filter(lambda x: x['name'] == 'star', systems[ uid]['reactants'][reactant]['systems']))[0] energy = systems[uid]['reactants'][reactant]['energy'] energies[reactant] = energy error_correction = +1 # to be fixed in API dE_OH = error_correction * energies['OH'] dE_O = error_correction * energies['O'] dE_OOH = error_correction * energies['OOH'] # cf. https://pubs.acs.org/doi/pdfplus/10.1021/jacs.7b02622 dG_OH = dE_OH + 0.30225 dG_O = dE_O + (-0.0145) dG_OOH = dE_OOH + 0.34475 dG_O__dG_OH = dG_O - dG_OH system_name = '{formula:20s}{facet:20s}'.format(**locals()) short_systems.append({ 'uid': uid, 'formula': formula, 'facet': facet, 'x': dG_O__dG_OH, 'y': dG_OH, 'z': - overpotential(dG_OH, dG_O, dG_OOH), }) labels.update({ 'xlabel': 'ΔG(O) - ΔG(OH) [eV]', 'ylabel': 'ΔG(OH) [eV]', 'zlabel': 'Overpotential [eV]', 'reference': ('[1] Friebel, Daniel, Mary W. Louie,' ' Michal Bajdich, Kai E. Sanwald, Yun Cai,' ' Anna M. Wise, Mu-Jeng Cheng et al.' ' "Identification of highly active Fe sites' ' in (Ni, Fe) OOH for electrocatalytic water' ' splitting." Journal of the American Chemical' ' Society 137, no. 3 (2015): 1305-1313.' ' DOI: 10.1021/ja511559d [2] Man, Isabela C.,' ' Hai‐Yan Su, Federico Calle‐Vallejo,' ' Heine A. Hansen, José I. Martínez,' ' Nilay G. Inoglu, John Kitchin,' ' Thomas F. Jaramillo, Jens K. Nørskov,' ' and Jan Rossmeisl. "Universality in' ' oxygen evolution electrocatalysis on' ' oxide surfaces." ChemCatChem 3, no. 7 (2011):' ' 1159-1165. DOI: 10.1002/cctc.201000397') }) elif activityMap == 'NRR': def limiting_potential(dG_NNH, dG_NH2__dG_NH): return -max(dG_NNH, dG_NH2__dG_NH) raw_systems = {} raw_systems['NNH'] = list(map( lambda x: x['node'], graphql_query( reactants='reactants: "star+H2gas+N2gas",', products='products: "NNHstar" ,', facet='facet: "' + '~111' + '", ', )['data']['reactions']['edges'] )) raw_systems['NH2'] = list(map( lambda x: x['node'], graphql_query( reactants='reactants: "star+H2gas+N2gas",', products=' products: "NH2star", ', facet='facet: "' + '~111' + '", ', )['data']['reactions']['edges'] )) raw_systems['NH'] = list(map( lambda x: x['node'], graphql_query( reactants='reactants: "star+H2gas+N2gas",', products='products: "NHstar", ', facet='facet: "' + '~111' + '", ', )['data']['reactions']['edges'] )) systems = {} for reactant in raw_systems: for raw_system in raw_systems[reactant]: for geometry in raw_system.get('reactionSystems', {}): if geometry['name'] == 'star': site = list(json.loads( raw_system['sites'] ).values())[0] formula = raw_system['chemicalComposition'] # skip unstable sites by now # to be removed. if (reactant, formula, site) in [ ('NNH', 'Au16', 'ontop'), ('NNH', 'Au16', 'fcc'), ('NNH', 'Au16', 'hollow'), ('NNH', 'Pd16', 'ontop'), ('NNH', 'Pt16', 'ontop'), ('NNH', 'Rh16', 'ontop'), ]: continue system = systems.setdefault(geometry['aseId'], {}) energy = system.get('E', {}) energy.setdefault(reactant, {}) \ .setdefault(site, raw_system['reactionEnergy']) system.update({ 'formula': raw_system['chemicalComposition'], 'facet': raw_system['facet'], 'uid': geometry['aseId'], 'E': energy, }) for uid in systems: system = systems[uid] dE_NNH = sorted(list(system['E']['NNH'].values()))[0] dE_NH2 = sorted(list(system['E']['NH2'].values()))[0] dE_NH = sorted(list(system['E']['NH'].values()))[0] # free energy corrections from Aayush Singh dG_NNH = dE_NNH + 0.763 # 0.763 eV, free energy correction dG_NH2__dG_NH = dE_NH2 - dE_NH + 0.330 # 0.330 eV, free energy correction U_L = limiting_potential(dG_NNH, dG_NH2__dG_NH) # system.pop('E') system.update({ 'x': dG_NNH, 'y': dG_NH2__dG_NH, 'z': U_L, }) short_systems = [ system for system in systems.values() ] labels.update({ 'xlabel': 'ΔG(NNH) [eV]', 'ylabel': 'ΔG(NH2) - ΔG(NH) [eV]', 'zlabel': 'U(L) [V s. RHE]', 'reference': ('Montoya, Joseph H., Charlie Tsai,' ' Aleksandra Vojvodic, and Jens K. Nørskov.' ' "The challenge of electrochemical ammonia' ' synthesis: A new perspective on the role of' ' nitrogen scaling relations." ChemSusChem 8,' ' no. 13 (2015): 2180-2186.' ' DOI: 10.1002/cssc.201500322.' ' Free energy corrections correspond to' ' Ru(111) surface and N2/H2 gas phase at 300K' ' and standard pressure.'), }) elif activityMap == 'ORR': labels.update({ 'xlabel': 'ΔG(OH) [eV]', 'ylabel': 'ΔG(OOH) [eV]', 'zlabel': 'Overpotential [eV]', 'reference': ('Kulkarni, Ambarish, Samira Siahrostami,' ' Anjli Patel, and Jens K. Nørskov. "Understanding' ' Catalytic Activity Trends in the Oxygen Reduction' ' Reaction." Chemical reviews (2018).' ' DOI: 10.1021/acs.chemrev.7b00488'), }) elif activityMap == 'CO_Hydrogenation_111': raw_systems = {} raw_systems['COstar'] = list(map( lambda x: x['node'], graphql_query( products='products: "' + 'COstar' + '", ', reactants='reactants: "' + 'COgas' + '", ', facet='facet: "' + '111' + '", ', )['data']['reactions']['edges'] )) raw_systems['OHstar'] = list(map( lambda x: x['node'], graphql_query( products='products: "' + 'H2gas+OHstar' + '", ', reactants='reactants: "' + 'H2Ogas' + '", ', facet='facet: "' + '111' + '", ', )['data']['reactions']['edges'] )) systems = {} for reactant in raw_systems: for raw_system in raw_systems[reactant]: for geometry in raw_system.get('reactionSystems', {}): if geometry['name'] == 'star': system = systems.setdefault(geometry['aseId'], {}) system.update({ 'formula': raw_system['chemicalComposition'], 'facet': raw_system['facet'], 'uid': geometry['aseId'], }) if reactant == 'COstar': system.update({ 'x': raw_system['reactionEnergy'], 'z': 0.0, }) elif reactant == 'OHstar': system.update({ 'y': raw_system['reactionEnergy'], 'z': 0.0, }) short_systems = [ system for system in systems.values() if system.get('x', None) and system.get('y', None) ] labels.update({ 'xlabel': 'ΔE(CO) [eV]', 'ylabel': 'ΔE(OH) [eV]', 'zlabel': 'TOF [1/s]', 'reference': ('Schumann, Julia, Andrew J. Medford,' ' Jong Suk Yoo, Zhi-Jian Zhao, Pallavi Bothra,' ' Ang Cao, Felix Studt, Frank Abild-Pedersen,' ' and Jens K. Nørskov. "Selectivity of synthesis' ' gas conversion to C2+ oxygenates on fcc (111)' ' transition metal surfaces." ACS Catalysis (2018).' ' DOI: 10.1021/acscatal.8b00201.'), }) elif activityMap == 'CO2RR': labels.update({ 'xlabel': 'ΔE(CO*) [eV]', 'ylabel': 'ΔE(H-CO) [eV]', 'zlabel': 'log(TOF) [1/s]', 'reference': ('Liu, Xinyan, Jianping Xiao, Hongjie Peng,' ' Xin Hong, Karen Chan, and Jens K. Nørskov.' ' "Understanding trends in electrochemical' ' carbon dioxide reduction rates."' ' Nature Communications 8 (2017): 15438.'), }) # sort for top systems list short_systems = sorted( short_systems, key=lambda _x: - _x.get('z', 0.0) ) return flask.jsonify({ 'systems': short_systems, 'xlabel': labels.get('xlabel', ''), 'ylabel': labels.get('ylabel', ''), 'zlabel': labels.get('zlabel', ''), 'reference': labels.get('reference', ''), })