From 16ca472d245da6e6f94aba651806b85d9a8c1fd2 Mon Sep 17 00:00:00 2001 From: Raul Flores Date: Wed, 25 Dec 2019 20:48:27 -0800 Subject: [PATCH 01/10] Update plottly methods file --- plotting/my_plotly.py | 387 ++++++++++++++++++++++++++++-------------- 1 file changed, 256 insertions(+), 131 deletions(-) diff --git a/plotting/my_plotly.py b/plotting/my_plotly.py index c795b0a..7f48707 100644 --- a/plotting/my_plotly.py +++ b/plotting/my_plotly.py @@ -6,18 +6,271 @@ """ #| - Import Modules +import os + +import copy + +# Plotly imports import plotly -import os -# import plotly.plotly as py import chart_studio.plotly as py import plotly.graph_objs as go from plotly import io as pyio #__| +def get_xy_axis_info(fig): + """ + """ + #| - get_xy_axis_info + xy_axis_list = [i for i in fig.layout._props if "axis" in i] + + x_axis_list = [i for i in xy_axis_list if i[0] == "x"] + y_axis_list = [i for i in xy_axis_list if i[0] == "y"] + + num_of_xaxis = len(x_axis_list) + num_of_yaxis = len(y_axis_list) + + # print("x_axis_list:", x_axis_list) + # print("y_axis_list:", y_axis_list) + + # print("") + + x_axis_num_list = [] + for axis_i in x_axis_list: + if axis_i == "xaxis" or axis_i == "yaxis": + x_axis_num_list.append("") + else: + x_axis_num_list.append(axis_i[5:]) + + y_axis_num_list = [] + for axis_i in x_axis_list: + if axis_i == "xaxis" or axis_i == "yaxis": + y_axis_num_list.append("") + else: + y_axis_num_list.append(axis_i[5:]) + + # print("x_axis_num_list:", x_axis_num_list) + # print("y_axis_num_list:", y_axis_num_list) + + out_dict = dict( + num_of_xaxis=num_of_xaxis, + num_of_yaxis=num_of_yaxis, + x_axis_list=x_axis_list, + y_axis_list=y_axis_list, + x_axis_num_list=x_axis_num_list, + y_axis_num_list=y_axis_num_list, + ) + + return(out_dict) + #__| + + +def add_minor_ticks( + fig, + axis="x", # 'x', 'y', or 'both' + ticks_props_new_x=None, + ticks_props_new_y=None, + ): + """ + """ + #| - add_minor_ticks + dummy_trace = go.Scatter( + x=[0], + y=[0], + # xaxis="x2", + xaxis="x", + yaxis="y", + opacity=0., + name="TEMP|8asdf", + ) + + xaxis_orig = copy.deepcopy(fig.layout.xaxis) + yaxis_orig = copy.deepcopy(fig.layout.yaxis) + + + if axis == "x" or axis == "both": + for trace in fig.data: + if trace.xaxis == None or trace.xaxis == "x": + xaxis_new = "x2" + else: + tmp = "x2" + xaxis_old = trace.xaxis + tmp = int(xaxis_old[1:]) + xaxis_new = "x" + str(tmp) + + fig.add_scatter( + **trace.update(dict1=dict(xaxis=xaxis_new), overwrite=True).to_plotly_json(), + ) + + + + + + if axis == "y" or axis == "both": + for trace in fig.data: + if trace.yaxis == None or trace.yaxis == "y": + yaxis_new = "y2" + else: + tmp = "y2" + yaxis_old = trace.yaxis + tmp = int(axis_old[1:]) + yaxis_new = "y" + str(tmp) + + fig.add_scatter( + **trace.update(dict1=dict(yaxis=yaxis_new), overwrite=True).to_plotly_json(), + ) + + + + + # ######################################################################### + # global_axis_props = go.layout.XAxis( + + global_axis_props = dict( + showticklabels=False, + # title=go.layout.xaxis.Title( + title=dict( + font=None, + standoff=None, + # text="d", + text="", + ), + ) + + new_xaxis = xaxis_orig.update( + # global_axis_props.to_plotly_json(), + global_axis_props, + overwrite=True) + new_xaxis = new_xaxis.update(ticks_props_new_x) + + new_yaxis = yaxis_orig.update( + global_axis_props, + overwrite=True) + # new_yaxis = new_yaxis.update(ticks_props_new_y.to_plotly_json()) + new_yaxis = new_yaxis.update(ticks_props_new_y) + + tmp = fig.update_layout( + xaxis2=new_xaxis, + yaxis2=new_yaxis, + ) + + + + fig.add_scatter(**dummy_trace.to_plotly_json()) + + return(fig) + + #__| + + +def my_plotly_plot( + figure=None, + plot_name="TEMP_PLOT_NAME", + write_html=False, + write_png=False, + png_scale=6., + write_pdf=False, + write_svg=False, + + try_orca_write=False, + ): + """ + Returns: Plotly figure object + + TODO The upload to plotly functionality is not that useful anymore, probably remove + + TODO Add functionality to display image using Ipython.display instead of displaying the full plotly html (interactive), this will save space + + from IPython.display import Image + Image("out_plot/" + plot_name_i + ".png") + + Args: + --------------------------------------------------------------------------- + layout: + plotly layout + layout_override: + Dictionary to override layout + plot_name: + Plot name (used both for plot upload and local save) + data: + plotly data object + + """ + #| - my_plotly_plot + assert figure is not None, "Must pass a plot.ly figure object" + fig = figure + + + # ######################################################################### + plot_dir = "out_plot" + if not os.path.exists(plot_dir): + os.makedirs(plot_dir) + + + #| - Local write to HTML + if write_html: + pyio.write_html( + fig, + os.path.join(plot_dir, plot_name + ".html"), + # config=None, + # auto_play=True, + # include_plotlyjs=True, + # include_mathjax=False, + # post_script=None, + # full_html=True, + # animation_opts=None, + # validate=True, + # default_width='100%', + # default_height='100%', + # auto_open=False, + ) + #__| + + #| - Write pdf and svg (if ORCA is installed and working) + # Getting the hostname of computer + import socket + hostname = socket.gethostbyaddr(socket.gethostname())[0] + + # Requires ORCA installation + if ( + os.environ.get("USER", "") == "raul-ubuntu-desktop" or + hostname == "raul-ubuntu-vb" or + hostname == "DESKTOP-37GUFJ5" or + hostname == "raul-dell-ubuntu" or + hostname == "raul-dell-latitude" or + try_orca_write + ): + print("Writing pdf with ORCA") + + prepath = os.path.join(plot_dir, plot_name) + print("prepath:", prepath) + + if write_pdf: + try: + fig.write_image(prepath + ".pdf") + except: + print("Couldn't write pdf") + if write_svg: + try: + fig.write_image(prepath + ".svg") + except: + print("Couldn't write svg") + if write_png: + try: + fig.write_image(prepath + ".png", scale=png_scale) + except: + print("Couldn't write png") + + #__| + + + return(fig) + #__| + + + -#| - Plotly def reapply_colors(data): """Redefines the line colors of a plotly data series. @@ -144,131 +397,3 @@ def plot_layout( return(layout) #__| - -#__| - - - -def my_plotly_plot( - figure=None, - plot_name="TEMP_PLOT_NAME", - online_save_dir=None, - write_html=False, - write_png=False, - png_scale=6., - write_pdf=False, - write_svg=False, - upload_plot=False, - - # layout=None, - # layout_override=None, - # data=None, - # write_pdf_svg=True, - ): - """ - TODO: - Remove layout override functionality, this should be done before calling - the method - - Returns: Plotly figure object - - Args: - --------------------------------------------------------------------------- - layout: - plotly layout - layout_override: - Dictionary to override layout - plot_name: - Plot name (used both for plot upload and local save) - online_save_dir: - Plot.ly folder to save figure into (Not used for local save) - data: - plotly data object - upload_plot: - Upload plot to plotly servers - - """ - #| - my_plotly_plot - # if layout is None: - # layout = go.Layout() - - if figure is not None: - fig = figure - else: - print("NOOOOOOOOOOOOOOOOOOO!!!") - # fig = go.Figure(data=data, layout=layout) - - - # fig.layout.update(layout_override) - - - #| - Upload to plot.ly website - # ######################################################################### - if upload_plot: - plotly_filename = os.path.join( - online_save_dir, - # "02_oer_analysis", - # "oer_2d_volcano_plot", - plot_name) - tmp = py.iplot(fig, filename=plotly_filename) - print(plotly_filename) - #__| - - - # ######################################################################### - plot_dir = "out_plot" - if not os.path.exists(plot_dir): - os.makedirs(plot_dir) - - - #| - Local write to HTML - if write_html: - pyio.write_html( - fig, - os.path.join(plot_dir, plot_name + ".html"), - # config=None, - # auto_play=True, - # include_plotlyjs=True, - # include_mathjax=False, - # post_script=None, - # full_html=True, - # animation_opts=None, - # validate=True, - # default_width='100%', - # default_height='100%', - # auto_open=False, - ) - #__| - - - #| - Write pdf and svg (if ORCA is installed and working) - import socket - hostname = socket.gethostbyaddr(socket.gethostname())[0] - - # Requires ORCA installation - if ( - os.environ["USER"] == "raul-ubuntu-desktop" or - hostname == "raul-ubuntu-vb" or - hostname == "DESKTOP-37GUFJ5" or - hostname == "raul-dell-latitude" - # write_pdf_svg is True - ): - print("Writing pdf with ORCA") - - if write_pdf: - fig.write_image( - os.path.join(plot_dir, plot_name + ".pdf")) - if write_svg: - fig.write_image( - os.path.join(plot_dir, plot_name + ".svg")) - if write_png: - fig.write_image( - os.path.join(plot_dir, plot_name + ".png"), - scale=png_scale, - ) - - #__| - - - return(fig) - #__| From c4f3f297f5570c92416095ccc87e6fcd59dab20c Mon Sep 17 00:00:00 2001 From: Raul Flores Date: Mon, 30 Dec 2019 22:59:13 -0800 Subject: [PATCH 02/10] Updates to plotting scripts, new add_duplicate_axes method --- plotting/my_plotly.py | 152 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 139 insertions(+), 13 deletions(-) diff --git a/plotting/my_plotly.py b/plotting/my_plotly.py index 7f48707..eafbbf8 100644 --- a/plotting/my_plotly.py +++ b/plotting/my_plotly.py @@ -33,39 +33,166 @@ def get_xy_axis_info(fig): # print("x_axis_list:", x_axis_list) # print("y_axis_list:", y_axis_list) - # print("") x_axis_num_list = [] for axis_i in x_axis_list: if axis_i == "xaxis" or axis_i == "yaxis": - x_axis_num_list.append("") + x_axis_num_list.append(1) else: - x_axis_num_list.append(axis_i[5:]) + x_axis_num_list.append(int(axis_i[5:])) y_axis_num_list = [] for axis_i in x_axis_list: if axis_i == "xaxis" or axis_i == "yaxis": - y_axis_num_list.append("") + y_axis_num_list.append(1) else: - y_axis_num_list.append(axis_i[5:]) + y_axis_num_list.append(int(axis_i[5:])) # print("x_axis_num_list:", x_axis_num_list) # print("y_axis_num_list:", y_axis_num_list) out_dict = dict( - num_of_xaxis=num_of_xaxis, - num_of_yaxis=num_of_yaxis, - x_axis_list=x_axis_list, - y_axis_list=y_axis_list, - x_axis_num_list=x_axis_num_list, - y_axis_num_list=y_axis_num_list, + x=dict( + num_of_axis=num_of_xaxis, + axis_list=x_axis_list, + axis_num_list=x_axis_num_list, + ), + + y=dict( + num_of_axis=num_of_yaxis, + axis_list=y_axis_list, + axis_num_list=y_axis_num_list, + ), ) return(out_dict) #__| + +def add_duplicate_axes( + fig, + axis_type="x", # 'x' or 'y' + axis_data=dict(), + axis_num_list=None, + ): + """ + """ + #| - add_duplicate_axes + + # This is necessary to make sure that the original traces are still visible after adding the new traces + fig.update_layout( + # paper_bgcolor="white", + plot_bgcolor="rgba(255,255,255,0.)", + ) + + axis_info_dict = get_xy_axis_info(fig)[axis_type] + num_of_axis = axis_info_dict["num_of_axis"] + + # ######################################################################### + if axis_num_list is None: + # axis_list = axis_info_dict["axis_list"] + axis_num_list = axis_info_dict["axis_num_list"] + + + # axis_num_list_new = [i + len(axis_num_list) for i in axis_num_list] + axis_num_list_new = [i + num_of_axis + 1 for i, j in enumerate(axis_num_list)] + + + # print("num_of_axis:", num_of_axis) + # print("axis_num_list_new:", axis_num_list_new) + + # [(i, j) for i, j in enumerate(mylist)] + + iterator = enumerate(zip(axis_num_list, axis_num_list_new)) + for i_cnt, (old_index, new_index) in iterator: + old_Axis = fig.layout[axis_type + "axis" + str(old_index)] + + new_axis = copy.deepcopy(old_Axis) + new_axis = new_axis.update( + # dtick=0.1, + showticklabels=False, + title=dict( + font=None, + standoff=None, + text="", + ), + ) + + new_axis = new_axis.update(**axis_data) + + axis_key = axis_type + "axis" + str(new_index) + new_layout = go.Layout({ + axis_key: new_axis, + }) + + fig.update_layout(new_layout) + + fig.add_scatter( + **go.Scatter({ + axis_type + "axis": axis_type + str(new_index) + }).to_plotly_json()) + + #__| + + +#| - OLD | add_duplicate_axes +# def add_duplicate_axes( +# fig, +# axis_type="x", # 'x' or 'y' +# axis_data=dict(), +# ): +# """ +# """ +# #| - add_duplicate_axes +# +# # This is necessary to make sure that the original traces are still visible after adding the new traces +# fig.update_layout( +# # paper_bgcolor="white", +# plot_bgcolor="rgba(255,255,255,0.)", +# ) +# +# # ######################################################################### +# axis_info_dict = get_xy_axis_info(fig)[axis_type] +# +# num_of_axis = axis_info_dict["num_of_axis"] +# axis_list = axis_info_dict["axis_list"] +# axis_num_list = axis_info_dict["axis_num_list"] +# +# axis_num_list_new = [i + num_of_axis for i in axis_num_list] +# iterator = enumerate(zip(axis_num_list, axis_num_list_new)) +# for i_cnt, (old_index, new_index) in iterator: +# old_Axis = fig.layout[axis_type + "axis" + str(old_index)] +# +# new_axis = copy.deepcopy(old_Axis) +# new_axis = new_axis.update( +# # dtick=0.1, +# showticklabels=False, +# title=dict( +# font=None, +# standoff=None, +# text="", +# ), +# ) +# +# new_axis = new_axis.update(**axis_data) +# +# axis_key = axis_type + "axis" + str(new_index) +# new_layout = go.Layout({ +# axis_key: new_axis, +# }) +# +# fig.update_layout(new_layout) +# +# fig.add_scatter( +# **go.Scatter({ +# axis_type + "axis": axis_type + str(new_index) +# }).to_plotly_json()) +# #__| +#__| + + def add_minor_ticks( fig, axis="x", # 'x', 'y', or 'both' @@ -178,7 +305,6 @@ def my_plotly_plot( """ Returns: Plotly figure object - TODO The upload to plotly functionality is not that useful anymore, probably remove TODO Add functionality to display image using Ipython.display instead of displaying the full plotly html (interactive), this will save space @@ -265,7 +391,7 @@ def my_plotly_plot( #__| - return(fig) + # return(fig) #__| From 3498c317d3e27e05a82aa39eef51cdadd915d0dd Mon Sep 17 00:00:00 2001 From: Raul Flores Date: Mon, 30 Dec 2019 22:59:26 -0800 Subject: [PATCH 03/10] __misc__ --- .../oxr_plotting_classes/oxr_plot_scaling.py | 14 ++++---------- .../oxr_plotting_classes/oxr_plot_volcano.py | 4 ++-- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/oxr_reaction/oxr_plotting_classes/oxr_plot_scaling.py b/oxr_reaction/oxr_plotting_classes/oxr_plot_scaling.py index 2379bc9..ed37fcd 100644 --- a/oxr_reaction/oxr_plotting_classes/oxr_plot_scaling.py +++ b/oxr_reaction/oxr_plotting_classes/oxr_plot_scaling.py @@ -5,19 +5,17 @@ Author: Raul A. Flores """ -#| - IMPORT MODULES +# | - IMPORT MODULES import numpy as np -import pandas as pd +# import pandas as pd from sklearn.linear_model import LinearRegression import plotly.graph_objs as go -# pd.options.mode.chained_assignment = None - from oxr_reaction.oxr_series import ORR_Free_E_Series from oxr_reaction.adsorbate_scaling import lim_U_i -#__| +# __| # ███████ ██████ ██████ ██ ██████ ████████ @@ -543,6 +541,7 @@ def get_plotly_layout(self, x_ax_spec: title: showlegend: + layout_dict: """ #| - create_layout # if x_ax_spec == "" @@ -686,11 +685,6 @@ def get_plotly_layout(self, if layout_dict is not None: layout.update(layout_dict) - # if layout_dict is not None: - # from misc_modules.misc_methods import dict_merge - # dict_merge(layout_i, layout_dict) - # # layout_i = {**layout_i, **layout_dict} - return(layout) #__| diff --git a/oxr_reaction/oxr_plotting_classes/oxr_plot_volcano.py b/oxr_reaction/oxr_plotting_classes/oxr_plot_volcano.py index 4ce84e7..0771caa 100644 --- a/oxr_reaction/oxr_plotting_classes/oxr_plot_volcano.py +++ b/oxr_reaction/oxr_plotting_classes/oxr_plot_volcano.py @@ -343,9 +343,9 @@ def create_volcano_lines(self, hoverinfo="skip", line=dict( color=line_color, - width=2, + width=1.5, # dash="dash", - dash="5px,2px,5px,2px", + # dash="5px,2px,5px,2px", ) ) From 1a5a988161e0c31d51bd0d9219620b93a8976aba Mon Sep 17 00:00:00 2001 From: Raul Flores Date: Mon, 30 Dec 2019 23:01:07 -0800 Subject: [PATCH 04/10] Changed #| - to # | - and #__| to # __| --- __misc__/sc_temp_methods.py | 4 +- ase_modules/add_adsorbate.py | 16 +- ase_modules/adsorbates.py | 32 +- ase_modules/ase_methods.py | 436 +++++++++--------- ase_modules/dft_params.py | 126 ++--- ase_modules/get_G.py | 88 ++-- atoms_objects/slab_generation.py | 16 +- aws/aws_class.py | 112 ++--- bader_charge/bader.py | 68 +-- classical_methods/lennard_jones.py | 36 +- colors/colors.py | 20 +- compute_envs/nersc.py | 120 ++--- dft_job_automat/__old__.py | 80 ++-- dft_job_automat/compute_env.py | 412 ++++++++--------- dft_job_automat/job_analysis.py | 316 ++++++------- dft_job_automat/job_dependencies.py | 120 ++--- dft_job_automat/job_manager.py | 104 ++--- dft_job_automat/job_setup.py | 280 +++++------ .../job_types_classes/data_frame_methods.py | 16 +- .../job_types_classes/dft_methods.py | 80 ++-- .../job_types_classes/raman_methods.py | 16 +- dft_post_analysis/bands.py | 76 +-- dft_post_analysis/charge_density.py | 88 ++-- dft_post_analysis/dos.py | 84 ++-- dft_post_analysis/dos_bands_combined.py | 8 +- dft_post_analysis/rapiDOS/rapiDOS.py | 34 +- dft_post_analysis/wf.py | 12 +- energetics/dft_energy.py | 68 +-- energetics/formation_energy.py | 28 +- misc_modules/image_processing.py | 8 +- misc_modules/misc_methods.py | 36 +- misc_modules/numpy_methods.py | 20 +- misc_modules/pandas_methods.py | 12 +- misc_modules/plotly_methods.py | 12 +- oxr_reaction/__old__/old.py | 260 +++++------ oxr_reaction/adsorbate_scaling.py | 80 ++-- oxr_reaction/oxr_methods.py | 128 ++--- .../oxr_plot_2d_volcano.py | 80 ++-- .../oxr_plotting_classes/oxr_plot_fed.py | 140 +++--- .../oxr_plotting_classes/oxr_plot_scaling.py | 116 ++--- .../oxr_plotting_classes/oxr_plot_volcano.py | 96 ++-- oxr_reaction/oxr_rxn.py | 32 +- oxr_reaction/oxr_series.py | 112 ++--- plotting/my_plotly.py | 64 +-- plotting/plotly_layout_template.py | 24 +- pourbaix_pymatgen/data_exp_form_e.py | 16 +- pourbaix_pymatgen/element_list.py | 16 +- pourbaix_pymatgen/energy_scheme.py | 40 +- pourbaix_pymatgen/entry_methods.py | 56 +-- pourbaix_pymatgen/find_closest_point_pd.py | 20 +- pourbaix_pymatgen/heat_map.py | 72 +-- pourbaix_pymatgen/pd_make.py | 36 +- pourbaix_pymatgen/pd_screen_tools.py | 24 +- pourbaix_pymatgen/read_coord_data.py | 4 +- pourbaix_pymatgen/save_data.py | 4 +- pourbaix_pymatgen/stability_crit.py | 24 +- .../lennard_jones_regress/lj_regression.py | 88 ++-- .../old.lj_regression_180724.py | 80 ++-- quantum_espresso/qe_methods.py | 54 +-- raman_dft/vasp_raman_job_methods.py | 68 +-- readthedocs.py | 4 +- surface_energy/surface_energy.py | 174 +++---- .../surface_energy_pourbaix_plot.py | 24 +- vasp/vasp_methods.py | 44 +- xrd_spectra/xrd.py | 120 ++--- 65 files changed, 2542 insertions(+), 2542 deletions(-) diff --git a/__misc__/sc_temp_methods.py b/__misc__/sc_temp_methods.py index be06545..80c5a47 100644 --- a/__misc__/sc_temp_methods.py +++ b/__misc__/sc_temp_methods.py @@ -14,7 +14,7 @@ def color_scale_interp( ): """ """ - #| - color_scale_interp + # | - color_scale_interp # cl.scales["8"]["seq"]["Purples"] black_white_cs = [ @@ -69,4 +69,4 @@ def clamp(x): return(color_out) - #__| + # __| diff --git a/ase_modules/add_adsorbate.py b/ase_modules/add_adsorbate.py index 361544e..83f581f 100644 --- a/ase_modules/add_adsorbate.py +++ b/ase_modules/add_adsorbate.py @@ -2,7 +2,7 @@ """Methods to add atoms/adsorbates to surfaces.""" -#| - Import Modules +# | - Import Modules import numpy as np import copy import math @@ -10,7 +10,7 @@ from operator import itemgetter from ase.build import add_adsorbate from misc_modules.numpy_methods import angle_between -#__| +# __| def add_adsorbate_centered(active_element, slab, adsorbate, ads_height=2.5): """Add adsorbate to surface. @@ -25,7 +25,7 @@ def add_adsorbate_centered(active_element, slab, adsorbate, ads_height=2.5): adsorbate: ASE atoms object ads_height: Float """ - #| - add_adsorbate_centered + # | - add_adsorbate_centered center = (sum(slab.cell)) / 2 act_metals = [] for atom in slab: @@ -40,7 +40,7 @@ def add_adsorbate_centered(active_element, slab, adsorbate, ads_height=2.5): add_adsorbate(slab, adsorbate, ads_height, position=ads_pos) return slab - #__| + # __| def add_graphene_layer( @@ -58,7 +58,7 @@ def add_graphene_layer( graph_surf_d: graph_bond_d_real: """ - #| - add_graphene_layer + # | - add_graphene_layer slab = copy.deepcopy(slab) # num_graph_units = graphene_units + 1 @@ -91,11 +91,11 @@ def add_graphene_layer( y_unit_v = y_unit_v / np.linalg.norm(y_unit_v) - #| - STRAIN + # | - STRAIN tmp = mag1 / num_graph_bond_lengths strain = 100. * (graph_bond_d_real - tmp) / graph_bond_d_real print("Strain: " + str(strain)) - #__| + # __| patt_cnt_x = 0 patt_cnt_y = 0 @@ -144,4 +144,4 @@ def add_graphene_layer( C_pos_lst = np.array(C_pos_lst) return(slab) - #__| + # __| diff --git a/ase_modules/adsorbates.py b/ase_modules/adsorbates.py index 29c3c6e..2119f28 100644 --- a/ase_modules/adsorbates.py +++ b/ase_modules/adsorbates.py @@ -2,23 +2,23 @@ """Adsorbate related methods.""" -#| - IMPORT MODULES +# | - IMPORT MODULES from ase import Atoms from ase.build import molecule import numpy as np -#__| +# __| class Adsorbate: """Adsorbate atoms object class.""" - #| - Adsorbate ************************************************************ + # | - Adsorbate ************************************************************ def __init__(self): """Initialize Adsorbate class instance.""" - #| - __init__ + # | - __init__ self.tmp = 42 - #__| + # __| def ooh(self, OO_bl=1.359, @@ -36,7 +36,7 @@ def ooh(self, OO_angle: H_up_down: """ - #| - ooh + # | - ooh O_1_x = OO_bl * np.sin(np.radians(OO_angle)) O_1_y = OO_bl * np.cos(np.radians(OO_angle)) @@ -62,11 +62,11 @@ def ooh(self, ) return(ooh_mol) - #__| + # __| def o(self): """*O adsorbate.""" - #| - o + # | - o o_mol = Atoms(['O'], positions=[ (0, 0, 0), @@ -74,7 +74,7 @@ def o(self): ) return(o_mol) - #__| + # __| def oh(self, OH_bl=0.978, @@ -85,7 +85,7 @@ def oh(self, OH_bl: O-H bond length """ - #| - oh + # | - oh oh_mol = Atoms(["O", "H"], positions=[ (0, 0, 0), @@ -93,7 +93,7 @@ def oh(self, ] ) return(oh_mol) - #__| + # __| def h2o(self, H_up_down="up", @@ -104,7 +104,7 @@ def h2o(self, H_up_down: Hydrogen atom pointing up or down """ - #| - h2o + # | - h2o h2o_mol = molecule("H2O") if H_up_down == "up": @@ -113,7 +113,7 @@ def h2o(self, pass return(h2o_mol) - #__| + # __| def get_adsorbate(self, adsorbate, **kwargs): """ @@ -123,10 +123,10 @@ def get_adsorbate(self, adsorbate, **kwargs): adsorbate: kwargs: """ - #| - get_adsorbate + # | - get_adsorbate ads = getattr(self, adsorbate)(**kwargs) return(ads) - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** diff --git a/ase_modules/ase_methods.py b/ase_modules/ase_methods.py index 527c913..61347e4 100644 --- a/ase_modules/ase_methods.py +++ b/ase_modules/ase_methods.py @@ -11,7 +11,7 @@ TODO Add work function analysis (Ask Colin about Karen's method) """ -#| - IMPORT MODULES +# | - IMPORT MODULES import sys import os import glob @@ -50,9 +50,9 @@ import shutil import random import string -#__| +# __| -#| - METHODS +# | - METHODS def update_FINISHED(text, filename=".FINISHED.new"): """Update finished job/processes log file with message. @@ -64,7 +64,7 @@ def update_FINISHED(text, filename=".FINISHED.new"): text: filename: """ - #| - update_FINISHED + # | - update_FINISHED if os.path.exists("./" + filename): append_write = "a" # append if already exists else: @@ -73,11 +73,11 @@ def update_FINISHED(text, filename=".FINISHED.new"): with open(filename, append_write) as fle: fle.write(text) fle.write("\n") - #__| + # __| -#__| +# __| -#| - Parse DFT Job Parameters ************************************************* +# | - Parse DFT Job Parameters ************************************************* def set_QE_calc_params( atoms=None, @@ -96,7 +96,7 @@ def set_QE_calc_params( init_inst: Whether or not to institiate an espresso instance """ - #| - set_QE_calc_params + # | - set_QE_calc_params from espresso import espresso mess = "Loading QE Parameters From File " @@ -127,11 +127,11 @@ def set_QE_calc_params( # atoms.set_calculator(calc=calc) return(calc, espresso_params) - #__| + # __| -#__| ************************************************************************** +# __| ************************************************************************** -#| - Ionic Optimization ******************************************************* +# | - Ionic Optimization ******************************************************* def ionic_opt( atoms, @@ -168,7 +168,7 @@ def ionic_opt( Attempts to run Beef-vdW ensemble of energies Must have ran calculation with appropriate paramters to begin with """ - #| - ionic_opt + # | - ionic_opt from espresso import espresso from ase.optimize import QuasiNewton @@ -179,7 +179,7 @@ def ionic_opt( # TODO Skip calculation if previously converged # COMBAK Uncomment this section - #| - Checking if Previous Calculation Has Been Completed + # | - Checking if Previous Calculation Has Been Completed # filename = ".FINISHED.new" # if os.path.exists("./" + filename): # with open(filename, "r") as fle: @@ -189,9 +189,9 @@ def ionic_opt( # "(Running a single-point calculation)" # ) # mode = "sp" - #__| + # __| - #| - Setting Optimization Specific Espresso Parameters + # | - Setting Optimization Specific Espresso Parameters # espresso_params_copy = copy.deepcopy(espresso_params) params_opt = { @@ -215,12 +215,12 @@ def ionic_opt( calc_opt = espresso(**espresso_params_opt) atoms.set_calculator(calc_opt) - #__| + # __| reduce_magmoms(atoms) if mode == "opt": - #| - Regular Optimization + # | - Regular Optimization mess = "Running regular optimization " print(mess); sys.stdout.flush() @@ -255,14 +255,14 @@ def ionic_opt( fmax=fmax, steps=maxsteps, ) - #__| + # __| elif mode == "easy_opt": - #| - Easy Optimization -> Full Optimization + # | - Easy Optimization -> Full Optimization mess = "Running easy optimization scheme" print(mess); sys.stdout.flush() - #| - Easy Optimization Settings + # | - Easy Optimization Settings espresso_params_copy = copy.deepcopy(espresso_params) easy_params = { @@ -284,7 +284,7 @@ def ionic_opt( espresso_params_copy.update(easy_params) easy_calc = espresso(**espresso_params_copy) - #__| + # __| #TODO: Find a way to freeze all atoms but adsorbates # SIMPLE RELAXATION ################# @@ -330,16 +330,16 @@ def ionic_opt( qn.replay_trajectory("prev.traj") qn.run(fmax=fmax) - #__| + # __| elif mode == "sp": - #| - Single Point Calculation + # | - Single Point Calculation mess = "Running Single-Point Calculation" print(mess); sys.stdout.flush() atoms.get_potential_energy() write("out_opt.traj", atoms) - #__| + # __| #TEMP @@ -363,11 +363,11 @@ def ionic_opt( fle.write(str(elec_e) + "\n") # if mode != "sp": - # #| - Always Run Single-Point Calculation with Full IO + # # | - Always Run Single-Point Calculation with Full IO # mess = "Running Post-run Single-Point Calculation" # print(mess); sys.stdout.flush() # atoms.get_potential_energy() - # #__| + # # __| update_FINISHED("ionic_opt") @@ -376,7 +376,7 @@ def ionic_opt( if run_bader_an: - #| - Running initial single-point calculation + # | - Running initial single-point calculation params_bader = { "output": { "avoidio": False, @@ -399,14 +399,14 @@ def ionic_opt( print(mess); sys.stdout.flush() atoms.get_potential_energy() print("finished single-point"); sys.stdout.flush() - #__| + # __| bader(atoms, spinpol=espresso_params_opt["spinpol"], run_exec=True) - #__| + # __| -#__| ************************************************************************** +# __| ************************************************************************** -#| - Magnetic Moments ********************************************************* +# | - Magnetic Moments ********************************************************* def set_init_mag_moms( atoms, @@ -441,7 +441,7 @@ def set_init_mag_moms( convergence. This is best used when starting from previously converged magmoms. """ - #| - set_init_mag_moms + # | - set_init_mag_moms mess = "Setting Inital Magnetic Moments " mess += "**********************************************" print(mess); sys.stdout.flush() @@ -485,13 +485,13 @@ def set_init_mag_moms( if "spinpol" in list(espresso_params): spinpol_calc = espresso_params["spinpol"] - #| - Spin-polarization turned off, set magmoms to 0 + # | - Spin-polarization turned off, set magmoms to 0 if not spinpol_calc: print("set_init_mag_moms | Spin-polarization turned off") sys.stdout.flush() mag_mom_list = atoms.get_initial_magnetic_moments() magmoms_i = np.zeros(len(mag_mom_list)) - #__| + # __| # COMBAK This is a poor way of enforcing the analysis method preference # Assumes that there are only 2 analysis methods @@ -511,13 +511,13 @@ def set_init_mag_moms( magmoms_i = magmoms_master_dict[an_method] - #| - Use simple method + # | - Use simple method else: text = ("set_init_mag_moms | " "Using simple method for initial magnetic moments") print(text); sys.stdout.flush() magmoms_i = simple_mag_moms(atoms) - #__| + # __| if read_from_file: text = ("set_init_mag_moms | " @@ -529,31 +529,31 @@ def set_init_mag_moms( magmoms_i = magmoms_tmp - #| - Check That Length of Magmoms_list == len(atoms) + # | - Check That Length of Magmoms_list == len(atoms) if not len(magmoms_i) == len(atoms): text = ("Length of magmoms doesn't match the number of atoms!!!!!!!!!!!" "\n Will use simple method to assign initial magmoms") print(text); sys.stdout.flush() magmoms_i = simple_mag_moms(atoms) - #__| + # __| if inc_val_magmoms: magmoms_i = increase_abs_val_magmoms(atoms, magmoms_i) atoms.set_initial_magnetic_moments(magmoms_i) - #| - Printing Magnetic Moments + # | - Printing Magnetic Moments print("set_init_mag_moms | Initial Magnetic Moments:") for atom in atoms: elem = atom.symbol magmom = atom.magmom print(elem + ": " + str(magmom)) - #__| + # __| reduce_magmoms(atoms) - #__| + # __| def increase_abs_val_magmoms(atoms, magmoms_list, increase_amount=0.8): """Increase absolute value of magmoms for atoms object. @@ -570,7 +570,7 @@ def increase_abs_val_magmoms(atoms, magmoms_list, increase_amount=0.8): magmoms_list: increase_amount: """ - #| - increase_abs_val_magmoms + # | - increase_abs_val_magmoms inc = increase_amount light_atoms_list = { @@ -614,7 +614,7 @@ def increase_abs_val_magmoms(atoms, magmoms_list, increase_amount=0.8): return(new_magmom_list) - #__| + # __| def calc_spinpol(atoms): """Return whether spin polarization should be turned on or off. @@ -625,7 +625,7 @@ def calc_spinpol(atoms): Args: atoms: """ - #| - calc_spinpol + # | - calc_spinpol spinpol = True if hasattr(atoms, "calc"): if atoms.calc is not None: @@ -639,7 +639,7 @@ def calc_spinpol(atoms): # return(spinpol) return(spinpol) - #__| + # __| def simple_mag_moms(atoms): """Implement simple procedure to set initial guess for magnetic moments. @@ -647,9 +647,9 @@ def simple_mag_moms(atoms): Args: atoms """ - #| - simple_mag_moms + # | - simple_mag_moms - #| - High Spin Dictionary + # | - High Spin Dictionary master_dict_high_spin = { "Cr": 5, "Mn": 5, @@ -667,23 +667,23 @@ def simple_mag_moms(atoms): "C": 0.2, "N": 0.2, } - #__| + # __| - #| - Find cations + # | - Find cations cations = [] for atom in atoms: if atom.symbol in master_dict_high_spin.keys(): # if master_dict_high_spin.has_key(atom.symbol): cations.append(atom.symbol) - #__| + # __| - #| - Cation Magnetic Moments Dictionary + # | - Cation Magnetic Moments Dictionary cation_magmom_dict = {} for cation in cations: cation_magmom_dict[cation] = master_dict_high_spin[cation] * 1.1 + 0.3 - #__| + # __| - #| - Setting Magnetic Moments of Atoms + # | - Setting Magnetic Moments of Atoms magmoms = atoms.get_initial_magnetic_moments() for atom in atoms: @@ -701,9 +701,9 @@ def simple_mag_moms(atoms): magmoms = np.array(magmoms) return(magmoms) - #__| + # __| - #__| + # __| def reduce_magmoms(atoms, ntypx=10): """Reduce number of unique magnetic moments of atoms object. @@ -713,7 +713,7 @@ def reduce_magmoms(atoms, ntypx=10): atoms objects with more than 10 types of magmom/symbol pairs because QE only accepts a maximum of 10 types of atoms. """ - #| - reduce_magmoms + # | - reduce_magmoms syms = set(atoms.get_chemical_symbols()) master_dict = {} @@ -781,7 +781,7 @@ def reduce_magmoms(atoms, ntypx=10): for magmom in master_dict[sym]: for index in master_dict[sym][magmom]: atoms[index].magmom = magmom - #__| + # __| def read_magmoms_from_file(file_name="magmom_init.in"): @@ -790,7 +790,7 @@ def read_magmoms_from_file(file_name="magmom_init.in"): Args: file_name: """ - #| - read_magmoms_from_file + # | - read_magmoms_from_file print(os.path.isfile(file_name)) magmoms = None if os.path.isfile(file_name): @@ -822,10 +822,10 @@ def read_magmoms_from_file(file_name="magmom_init.in"): # print("__SD-sfd-") return(magmoms) - #__| + # __| -#| - __old__ +# | - __old__ # def compare_magmoms(self): # def compare_magmoms(): # """Compare spin states of two atoms objects. @@ -839,10 +839,10 @@ def read_magmoms_from_file(file_name="magmom_init.in"): # # Author: Colin Dickens # """ -# #| - compare_magmoms +# # | - compare_magmoms # def nearest_atom(atoms, position): # """Returns atom nearest to position.""" -# #| - nearest_atom +# # | - nearest_atom # position = np.array(position) # dist_list = [] # for atom in atoms: @@ -850,7 +850,7 @@ def read_magmoms_from_file(file_name="magmom_init.in"): # dist_list.append(dist) # # return atoms[np.argmin(dist_list)] -# #__| +# # __| # # if len(self.ads_atoms) >= len(self.slab_atoms): # ads = self.ads_atoms @@ -900,13 +900,13 @@ def read_magmoms_from_file(file_name="magmom_init.in"): # print(common) # print("Magnetic moments only present in %s" % not_indexed_by) # print(uncommon + "\n") -# #__| -#__| +# # __| +# __| -#__| ************************************************************************** +# __| ************************************************************************** -#| - Density of States ******************************************************** +# | - Density of States ******************************************************** def an_pdos( atoms, # calc, @@ -922,7 +922,7 @@ def an_pdos( dos_kpts: espresso_params: """ - #| - an_pdos + # | - an_pdos from espresso import espresso mess = "Running PDOS Analysis " @@ -987,7 +987,7 @@ def an_pdos( atoms.write(outdir + "/out_pdos.traj") update_FINISHED("an_pdos") - #__| + # __| def spin_pdos( atoms, @@ -1021,14 +1021,14 @@ def spin_pdos( write_charges: kwargs: """ - #| - spin_pdos + # | - spin_pdos valence_dict = { "Cu": 11, "C": 4, "O": 6, "H": 1, "Li": 1, "Rh": 17, "Co": 9, "Pd": 10, "Pt": 10, "Ni": 1, "Fe": 16, "N": 5, "Ru": 16, } - #| - Reading PDOS File, Otherwise Creates It + # | - Reading PDOS File, Otherwise Creates It # FIXME I don't like that it can create PDOS file, this is handled by my # an_pdos method. @@ -1051,31 +1051,31 @@ def spin_pdos( else: # no single point calc, should take 1 or 2 minutes print("spin_pdos | TEMP2") pdos = atoms.calc.calc_pdos(**kwargs) - #__| + # __| - #| - Finding Index of Fermi Level + # | - Finding Index of Fermi Level for i_ind, e_i in enumerate(pdos[0]): if e_i > 0: fi = i_ind # index of fermi level break - #__| + # __| - #| - Analysing PDOS For Magnetic Moments and Charge of All Atoms + # | - Analysing PDOS For Magnetic Moments and Charge of All Atoms if spinpol: - #| - Spin Polarlized Calculation + # | - Spin Polarlized Calculation magmom_list = [] charge_list = [] for i, atom in enumerate(atoms): - #| - Integrating Up and Down Spin PDOS + # | - Integrating Up and Down Spin PDOS spin_up = 0; spin_down = 0 for sym in pdos[2][i]: spin_up += np.trapz(pdos[2][i][sym][0][:fi], x=pdos[0][:fi]) spin_down += np.trapz(pdos[2][i][sym][1][:fi], x=pdos[0][:fi]) - #__| + # __| - #| - Update Atoms Magmom and Charge Properties + # | - Update Atoms Magmom and Charge Properties ##Update magmom if np.abs(spin_up - spin_down) > 1e-4: magmom_i = spin_up - spin_down @@ -1098,7 +1098,7 @@ def spin_pdos( atom.charge = charge_i charge_list.append(charge_i) - #__| + # __| print("PDOS MAGMOMS: " + str(atoms.get_initial_magnetic_moments())) reduce_magmoms(atoms) @@ -1111,7 +1111,7 @@ def spin_pdos( pickle.dump(charge_list, open("%s/charge_list.pickle" % outdir, "w")) - #| - Writing atom objects with magmom and charges written to them + # | - Writing atom objects with magmom and charges written to them # Charges written to init_charges atoms_cpy1 = copy.deepcopy(atoms) atoms_cpy1.set_initial_charges(charge_list) @@ -1131,29 +1131,29 @@ def spin_pdos( "pdos_magmoms.traj", ), ) - #__| + # __| - #__| + # __| else: - #| - Non-Spin Polarized Calculation + # | - Non-Spin Polarized Calculation charge_list = [] for i, atom in enumerate(atoms): - #| - Integrating PDOS For Charges + # | - Integrating PDOS For Charges charge = 0 for sym in pdos[2][i]: charge += np.trapz(pdos[2][i][sym][0][:fi], x=pdos[0][:fi]) - #__| + # __| - #| - Update Atoms Charges + # | - Update Atoms Charges # Update charge charge_i = nvalence_dict.get(atom.symbol, 0.) - (charge) if write_charges: # atom.charge = nvalence_dict[atom.symbol] - (charge) atom.charge = charge_i charge_list.append(charge_i) - #__| + # __| atoms.info.update({"pdos_charges": charge_list}) @@ -1165,7 +1165,7 @@ def spin_pdos( ) ) - #| - Writing atom objects with magmom and charges written to them + # | - Writing atom objects with magmom and charges written to them # Charges written to init_charges atoms_cpy1 = copy.deepcopy(atoms) atoms_cpy1.set_initial_charges(charge_list) @@ -1175,14 +1175,14 @@ def spin_pdos( "pdos_charges.traj", ), ) - #__| + # __| - #__| + # __| print("PDOS CHARGES: " + str(atoms.get_initial_charges())) - #__| + # __| - #| - Writing Output To File + # | - Writing Output To File if outdir and not single_point_calc: #Save only the Lowdin charges in the pdos log file light_lines = [] @@ -1210,13 +1210,13 @@ def spin_pdos( if save_pkl: pickle.dump(pdos, open("pdos.pkl", "w")) - #__| + # __| - #__| + # __| -#__| ************************************************************************** +# __| ************************************************************************** -#| - Band Structure *********************************************************** +# | - Band Structure *********************************************************** def an_bands(atoms, bands_kpts, espresso_params): """Perform band analysis on atoms object. @@ -1230,7 +1230,7 @@ def an_bands(atoms, bands_kpts, espresso_params): bands_kpts: espresso_params: """ - #| - an_bands + # | - an_bands from espresso import espresso mess = "Executing Band Structure Analysis " @@ -1245,7 +1245,7 @@ def an_bands(atoms, bands_kpts, espresso_params): atoms.set_initial_magnetic_moments(np.zeros(len(atoms))) # set_mag_mom_to_0(atoms) # COMBAK Remove this if working - #| - Running initial single-point calculation + # | - Running initial single-point calculation params_bands = { "output": { "avoidio": False, @@ -1276,7 +1276,7 @@ def an_bands(atoms, bands_kpts, espresso_params): print(mess); sys.stdout.flush() atoms.get_potential_energy() print("finished single-point"); sys.stdout.flush() - #__| + # __| # # calc_spinpol(atoms) # COMBAK I don't think this is doing anything # espresso_params.update( @@ -1318,11 +1318,11 @@ def an_bands(atoms, bands_kpts, espresso_params): atoms.write("dir_bands/out_bands.traj") update_FINISHED("an_bands") - #__| + # __| -#__| ************************************************************************** +# __| ************************************************************************** -#| - Beef Ensemble of Energies ************************************************ +# | - Beef Ensemble of Energies ************************************************ # def an_beef_ensemble(atoms, xc): # COMBAK def an_beef_ensemble(atoms): """Perform BEEF ensemble of enery analysis. @@ -1336,7 +1336,7 @@ def an_beef_ensemble(atoms): atoms: xc: """ - #| - an_beef_ensemble + # | - an_beef_ensemble mess = "Executing BEEF Ensemble Analysis " mess += "*********************************************" print(mess); sys.stdout.flush() @@ -1375,7 +1375,7 @@ def an_beef_ensemble(atoms): pickle.dump(ens_e, fle) update_FINISHED("an_beef_ensemble") - #__| + # __| def plot_beef_ensemble( folder_dir="dir_beef_ensemble", @@ -1389,7 +1389,7 @@ def plot_beef_ensemble( file_name: file_out: """ - #| - plot_beef_ensemble + # | - plot_beef_ensemble file_loc = folder_dir + "/" + file_name data = pickle.load(open(file_loc, "r")) @@ -1407,11 +1407,11 @@ def plot_beef_ensemble( out_file = folder_dir + "/" + file_out plt.savefig(out_file) # plt.show() - #__| + # __| -#__| ************************************************************************** +# __| ************************************************************************** -#| - Vibrational Analysis ***************************************************** +# | - Vibrational Analysis ***************************************************** def an_ads_vib( atoms, espresso_params=None, @@ -1436,7 +1436,7 @@ def an_ads_vib( thermochem_corrections: remove_imag_modes: Removes imaginary modes """ - #| - an_ads_vib + # | - an_ads_vib # from espresso import espresso from espresso.vibespresso import vibespresso @@ -1444,7 +1444,7 @@ def an_ads_vib( mess += "************************************************" print(mess); sys.stdout.flush() - #| - Setting Adsorbate Index List + # | - Setting Adsorbate Index List if ads_index_list is not None: pass elif "adsorbates" in atoms.info.keys(): @@ -1454,23 +1454,23 @@ def an_ads_vib( print("an_ads_vib | Adsorbate index info couldn't be parsed from atoms") print("an_ads_vib | Will vibrate all atoms!!!!!!!! (Probably not good)") pass - #__| + # __| - #| - Removing Empty Pickle Files + # | - Removing Empty Pickle Files pckl_file_list = glob.glob("dir_vib/*.pckl*") + glob.glob("*.pckl*") for pckl_file in pckl_file_list: if os.stat(pckl_file).st_size == 0: os.remove(pckl_file) print("an_ads_vib | " + pckl_file + " empty, so removed") - #__| + # __| - #| - Copy vib.pckl files back to root dir (For restarting) + # | - Copy vib.pckl files back to root dir (For restarting) for fle in glob.glob("dir_vib/*.pckl*"): fle_name = fle.split("/")[-1] shutil.move(fle, fle_name) - #__| + # __| - #| - Running initial single-point calculation + # | - Running initial single-point calculation params_vib = { "output": { "avoidio": False, @@ -1502,7 +1502,7 @@ def an_ads_vib( # print(mess); sys.stdout.flush() # atoms.get_potential_energy() # print("finished single-point"); sys.stdout.flush() - #__| + # __| set_init_mag_moms( atoms, @@ -1520,7 +1520,7 @@ def an_ads_vib( vib.summary(log="vib_summ.out") - #| - Copy Files to dir_vib Folder + # | - Copy Files to dir_vib Folder if not os.path.exists("dir_vib"): os.makedirs("dir_vib") shutil.move("vib_summ.out", "dir_vib/vib_summ.out") @@ -1532,14 +1532,14 @@ def an_ads_vib( for fle in glob.glob(r'*out_vib*'): shutil.move(fle, dest_dir + "/" + fle) - #__| + # __| - #| - Remove Imaginary Modes + # | - Remove Imaginary Modes # Removes them, not just making them 0 if remove_imag_modes: vib_e_list = vib_e_list.real vib_e_list = [vib_i for vib_i in vib_e_list if vib_i != 0.] - #__| + # __| if thermochem_corrections == "harmonic": thermochem_harm_corr(vib_e_list) @@ -1560,7 +1560,7 @@ def an_ads_vib( with open("dir_vib/vib_modes.pickle", "w") as fle: pickle.dump(vib_e_list, fle) - #| - Saving Vibrations Class Instance + # | - Saving Vibrations Class Instance file_name = "dir_vib/vib_inst.pickle" if not os.path.exists(file_name): pass @@ -1572,13 +1572,13 @@ def an_ads_vib( with open(file_name, "w") as fle: pickle.dump(vib, fle) - #__| + # __| update_FINISHED("an_ads_vib") # return(vib_e_list) return(vib) - #__| + # __| def thermochem_harm_corr( vib_e_list, @@ -1591,7 +1591,7 @@ def thermochem_harm_corr( List of vibrational modes in eV Temperature: """ - #| - thermochem_harm_corr + # | - thermochem_harm_corr mess = "Starting thermochemical harmonic energy contributions " mess += "***********************" print(mess); sys.stdout.flush() @@ -1613,7 +1613,7 @@ def thermochem_harm_corr( with open("dir_vib/gibbs_corr.out", "w") as fle: fle.write(str(F_energy)) fle.write("\n") - #__| + # __| def thermochem_IG_corr( vib_energies, @@ -1636,7 +1636,7 @@ def thermochem_IG_corr( spin=None: atoms=None: """ - #| - thermochem_IG_corr + # | - thermochem_IG_corr mess = "Starting thermochemical ideal gas energy contributions " mess += "***********************" print(mess); sys.stdout.flush() @@ -1677,18 +1677,18 @@ def thermochem_IG_corr( with open("dir_vib/g_energy.out", "w") as fle: fle.write(str(G)) - #__| + # __| -#__| ************************************************************************** +# __| ************************************************************************** -#| - Job Cleanup +# | - Job Cleanup def clean_up_dft(): """Clean up files after DFT job. I'm using a lot of try statement because I dont want this to break anywhere """ - #| - clean_up_dft + # | - clean_up_dft with open(".FINISHED", "w") as fle: fle.write("\n") @@ -1699,7 +1699,7 @@ def clean_up_dft(): if not os.path.exists("__misc__"): os.makedirs("__misc__") - #| - Moving Sherlock Node Files to __misc__ folder + # | - Moving Sherlock Node Files to __misc__ folder # Sherlock didn't like this: # Open RTE was unable to open the hostfile: # /scratch/users/flores12/03_graph_N_Fe/01_opt_struct/N_doped_graph_Fe/1-att/__test__/1-att/_5/uniqnodefile.21286552 @@ -1715,9 +1715,9 @@ def clean_up_dft(): # # except: # pass - #__| + # __| - #| - Moving DFT Parameter Files to dir_dft_params + # | - Moving DFT Parameter Files to dir_dft_params try: if not os.path.exists("dir_dft_params"): os.makedirs("dir_dft_params") @@ -1728,13 +1728,13 @@ def clean_up_dft(): except: pass - #__| + # __| - #__| + # __| -#__| +# __| -#| - Atoms File Operations **************************************************** +# | - Atoms File Operations **************************************************** def read_atoms_from_file(filename=None, try_restart=True): """Read atoms object from file. @@ -1746,7 +1746,7 @@ def read_atoms_from_file(filename=None, try_restart=True): filename: optional atoms file, will attempt to read first. try_restart: Restart job by reading output atoms/trajectory object """ - #| - read_atoms_from_file + # | - read_atoms_from_file mess = "Reading Atoms Object From File " mess += "***********************************************" print(mess) @@ -1754,7 +1754,7 @@ def read_atoms_from_file(filename=None, try_restart=True): atoms = None traj = None - #| - Restart From Output Atoms Object + # | - Restart From Output Atoms Object restart_file_name_list = [ "out.traj", "out_opt.traj", @@ -1777,9 +1777,9 @@ def read_atoms_from_file(filename=None, try_restart=True): except: pass - #__| + # __| - #| - Starting From Fresh Atoms Object + # | - Starting From Fresh Atoms Object file_name_list = [ "init.traj", "init.POSCAR", @@ -1803,13 +1803,13 @@ def read_atoms_from_file(filename=None, try_restart=True): print(mess); sys.stdout.flush() break - #__| + # __| if atoms is None: raise IOError("No atoms file found") return(atoms, traj) - #__| + # __| def convert_atoms_object(atoms_filename, out_file): """Convert atoms objects to new file format. @@ -1818,26 +1818,26 @@ def convert_atoms_object(atoms_filename, out_file): atoms_filename: out_file: """ - #| - convert_atoms_object + # | - convert_atoms_object atoms = read(atoms_filename) - #| - Convert to New Trajectory File Format + # | - Convert to New Trajectory File Format if out_file.split(".")[-1] == "traj": write(out_file, atoms) - #| - Using Trajectory Class Directly (Not Right?) + # | - Using Trajectory Class Directly (Not Right?) # traj = Trajectory(out_file, mode="w", atoms=atoms) # traj.set_description({"author": "Created by Raul Flores"}) # traj.write() - #__| + # __| - #__| + # __| - #__| + # __| -#__| ************************************************************************** +# __| ************************************************************************** -#| - Atoms Geometry Methods *************************************************** +# | - Atoms Geometry Methods *************************************************** def angle_between_lattice_vectors(atoms, vector_0=0, vector_1=1): """Calculate angle between cell lattice vectors. @@ -1849,7 +1849,7 @@ def angle_between_lattice_vectors(atoms, vector_0=0, vector_1=1): vector_0: vector_1: """ - #| - angle_between_lattice_vectors + # | - angle_between_lattice_vectors v1 = atoms.cell[vector_0] v2 = atoms.cell[vector_1] @@ -1857,7 +1857,7 @@ def angle_between_lattice_vectors(atoms, vector_0=0, vector_1=1): angle = math.degrees(angle) return(angle) - #__| + # __| def magnitude_of_lattice_vectors(atoms): """Return magnitude of three lattice vectors. @@ -1865,7 +1865,7 @@ def magnitude_of_lattice_vectors(atoms): Args: atoms: """ - #| - magnitude_of_lattice_vectors + # | - magnitude_of_lattice_vectors v1 = atoms.cell[0] v2 = atoms.cell[1] v3 = atoms.cell[2] @@ -1877,7 +1877,7 @@ def magnitude_of_lattice_vectors(atoms): out_tup = (mag1, mag2, mag3) return(out_tup) - #__| + # __| def find_diff_between_atoms_objects(atoms_A, atoms_B): """Find indices of atoms that are unique to atoms_A and atoms_B. @@ -1898,27 +1898,27 @@ def find_diff_between_atoms_objects(atoms_A, atoms_B): atoms_A: atoms_B: """ - #| - find_diff_between_atoms_objects + # | - find_diff_between_atoms_objects - #| - __old__ + # | - __old__ - # #| - Import Modules + # # | - Import Modules # from ase import io - # #__| + # # __| # - # #| - Script Inputs + # # | - Script Inputs # atoms_A_filename = "A.traj" # atoms_B_filename = "B.traj" - # #__| + # # __| # - # #| - Reading Atoms Objects + # # | - Reading Atoms Objects # atoms_A = io.read(atoms_A_filename) # atoms_B = io.read(atoms_B_filename) - # #__| + # # __| - #__| + # __| - #| - Building the Identical Atom Index List for Both Atoms Objects + # | - Building the Identical Atom Index List for Both Atoms Objects atoms_A_ind_list = [] atoms_B_ind_list = [] for atom_A in atoms_A: @@ -1937,7 +1937,7 @@ def find_diff_between_atoms_objects(atoms_A, atoms_B): if all(pos_AB_comp) is True and elem_AB_comp is True: atoms_A_ind_list.append(atom_A.index) atoms_B_ind_list.append(atom_B.index) - #__| + # __| atoms_A_unique_ind_list = [] for atom_A in atoms_A: @@ -1951,11 +1951,11 @@ def find_diff_between_atoms_objects(atoms_A, atoms_B): return(atoms_A_unique_ind_list, atoms_B_unique_ind_list) - #__| + # __| -#__| ************************************************************************** +# __| ************************************************************************** -#| - Modify Atoms Object ****************************************************** +# | - Modify Atoms Object ****************************************************** def move_atoms_of_element_i(atoms, element, new_position, dim="z"): """Modify positions of all atoms of certain element. @@ -1966,20 +1966,20 @@ def move_atoms_of_element_i(atoms, element, new_position, dim="z"): new_position: dim: """ - #| - move_atoms_of_element_i + # | - move_atoms_of_element_i if type(atoms) == str: atoms = read(atoms) else: pass - #| - Converting Input Dimension to Integer Index + # | - Converting Input Dimension to Integer Index if dim == "x": dim = 0 elif dim == "y": dim = 1 elif dim == "z": dim = 2 - #__| + # __| elem_i_list = [] for atom in atoms: @@ -1989,7 +1989,7 @@ def move_atoms_of_element_i(atoms, element, new_position, dim="z"): elem_i_list.append(atom) # return(atoms) - #__| + # __| def displace_overlayer( atoms, @@ -2010,7 +2010,7 @@ def displace_overlayer( element: save_file: """ - #| - displace_overlayer + # | - displace_overlayer atoms = copy.deepcopy(atoms) x_frac = 1. * x_ind / mesh_size_x @@ -2035,7 +2035,7 @@ def displace_overlayer( atoms.write(fle_name) return(atoms) - #__| + # __| def change_vacuum(atoms, vacuum): """Change the amount of vacuum in a slab. @@ -2047,7 +2047,7 @@ def change_vacuum(atoms, vacuum): atoms: vacuum: """ - #| - change_vacuum + # | - change_vacuum if type(atoms) == str: atoms = read(atoms) else: @@ -2061,11 +2061,11 @@ def change_vacuum(atoms, vacuum): atoms.center() return(atoms) - #__| + # __| -#__| ************************************************************************** +# __| ************************************************************************** -#| - Atoms Information Methods ************************************************ +# | - Atoms Information Methods ************************************************ def number_of_constrained_atoms(atoms): @@ -2074,7 +2074,7 @@ def number_of_constrained_atoms(atoms): Args: atoms: """ - #| - number_of_constrained_atoms + # | - number_of_constrained_atoms if type(atoms) == str: atoms = read(atoms) else: @@ -2083,7 +2083,7 @@ def number_of_constrained_atoms(atoms): N_constraints = len(atoms.constraints) return(N_constraints) - #__| + # __| def highest_position_of_element(atoms, element_symbol): """Return highest z-value for given element type. @@ -2092,13 +2092,13 @@ def highest_position_of_element(atoms, element_symbol): atoms: element_symbol """ - #| - highest_position_of_element + # | - highest_position_of_element - #| - SCRIPT INPUTS + # | - SCRIPT INPUTS # element_symbol element_name = element_symbol # atoms_file_name = atoms - #__| + # __| if type(atoms) == str: atoms = read(atoms) @@ -2115,7 +2115,7 @@ def highest_position_of_element(atoms, element_symbol): highest_z_pos = elem_atom_list[-1] return(highest_z_pos) - #__| + # __| def number_of_atoms(atoms): """Return atom count dictionary. @@ -2126,7 +2126,7 @@ def number_of_atoms(atoms): Args: atoms """ - #| - number_of_atoms + # | - number_of_atoms atoms_sym_list = atoms.get_chemical_symbols() unique_atom_symbols = list(set(atoms_sym_list)) @@ -2142,7 +2142,7 @@ def number_of_atoms(atoms): print("THIS HAS BEEN DEPRECATED to create_species_element_dict") # return(atom_dict) - #__| + # __| def create_species_element_dict( atoms, @@ -2167,7 +2167,7 @@ def create_species_element_dict( the elements present in the atoms object. """ - #| - create_species_element_dict + # | - create_species_element_dict from misc_modules.misc_methods import merge_two_dicts all_elements = list(periodic_table_dict) @@ -2181,7 +2181,7 @@ def create_species_element_dict( species_elem_dict[elem_i] = num_elem_i - #| - Include All Elements in the periodic table + # | - Include All Elements in the periodic table if include_all_elems or elems_to_always_include is not None: all_non_occuring_elements = list( filter( @@ -2215,14 +2215,14 @@ def create_species_element_dict( non_occuring_species_elem_dict, species_elem_dict, ) - #__| + # __| return(species_elem_dict) - #__| + # __| -#__| ************************************************************************** +# __| ************************************************************************** -#| - Visualization ************************************************************ +# | - Visualization ************************************************************ def view_in_vesta( @@ -2240,7 +2240,7 @@ def view_in_vesta( Optional list of names for cif files, easier to id structures when opened in VESTA """ - #| - view_in_vesta + # | - view_in_vesta def randomString(stringLength=10): """Generate a random string of fixed length """ letters = string.ascii_lowercase @@ -2283,7 +2283,7 @@ def randomString(stringLength=10): # shutil.rmtree(dirpath) - #__| + # __| @@ -2303,14 +2303,14 @@ def create_gif_from_traj( delay: """ - #| - create_gif_from_traj + # | - create_gif_from_traj - #| - Method Parameters + # | - Method Parameters atoms_file_name = "out_movie" fold_name = "images" - #__| + # __| - #| - Creating png ********************************************************* + # | - Creating png ********************************************************* if image_range == "all": ind_range = ":" else: @@ -2341,9 +2341,9 @@ def create_gif_from_traj( os.remove(path_i + "/" + name_i + ".pov") os.remove(path_i + "/" + name_i + ".ini") - #__| ********************************************************************** + # __| ********************************************************************** - #| - Converting Images to GIF ********************************************* + # | - Converting Images to GIF ********************************************* root_dir = os.getcwd() os.chdir(path_i + "/" + fold_name) @@ -2357,9 +2357,9 @@ def create_gif_from_traj( os.system(bash_command) os.system("mv *.gif ..") os.chdir(root_dir) - #__| ********************************************************************** + # __| ********************************************************************** - #__| + # __| def create_gif_from_atoms_movies( atoms_file="Default", @@ -2376,13 +2376,13 @@ def create_gif_from_atoms_movies( path_i: delay: """ - #| - create_images_from_atoms_movies + # | - create_images_from_atoms_movies - #| - SCRIPT PARAMETERS + # | - SCRIPT PARAMETERS fold_name = "images" - #__| + # __| - #| - Read Atoms File with *.traj File Name + # | - Read Atoms File with *.traj File Name if atoms_file == "Default": filenames = next(os.walk(path_i))[2] @@ -2405,7 +2405,7 @@ def create_gif_from_atoms_movies( atoms_file_name = ".".join(atoms_file_name) alist[atoms_file] = read(atoms_file_path, index=":") - #__| + # __| folder_i = fold_name if not os.path.isdir(path_i + folder_i): @@ -2440,7 +2440,7 @@ def create_gif_from_atoms_movies( os.remove(path_i + "/" + name_i + ".ini") - #| - Converting Images to GIF + # | - Converting Images to GIF root_dir = os.getcwd() os.chdir(path_i + "/" + fold_name) @@ -2457,14 +2457,14 @@ def create_gif_from_atoms_movies( os.system("mv *.gif ..") os.chdir(root_dir) - #__| + # __| # TODO - Remove png files after creating gif !!!!! - #__| + # __| -#__| ************************************************************************** +# __| ************************************************************************** -#| - MISC +# | - MISC def max_force(atoms): """Return largest force on any atom. @@ -2473,7 +2473,7 @@ def max_force(atoms): atoms: """ - #| - max_force + # | - max_force from ase import Atoms from numpy import ndarray @@ -2505,6 +2505,6 @@ def max_force(atoms): largest = force return(largest, sum) - #__| + # __| -#__| +# __| diff --git a/ase_modules/dft_params.py b/ase_modules/dft_params.py index c1f7e7b..3717bde 100644 --- a/ase_modules/dft_params.py +++ b/ase_modules/dft_params.py @@ -13,27 +13,27 @@ """ -#| - Import Modules +# | - Import Modules import os import json # My Modules from dft_job_automat.compute_env import ComputerCluster -#__| +# __| class DFT_Params: """Base class for DFT parameters encapsulation.""" - #| - DFT_Params ************************************************************ + # | - DFT_Params ************************************************************ def __init__(self): """Initialize base class.""" - #| - __init__ + # | - __init__ self.file_name = "dft-params" self.compute_env = os.environ.get("COMPENV") self.submission_params = self.load_submission_params() - #__| + # __| # def load_submission_params(self, filename=".submission_params.json"): def load_submission_params(self, filename=".submission_params_2.json"): @@ -46,7 +46,7 @@ def load_submission_params(self, filename=".submission_params_2.json"): comp_env module when used in conjuction with the job submission script. """ - #| - load_submission_params + # | - load_submission_params bkp_filename = ".submission_params.json" submission_params = None @@ -61,7 +61,7 @@ def load_submission_params(self, filename=".submission_params_2.json"): print("Couldn't read submission_params file") return(submission_params) - #__| + # __| def load_params(self, dir=".", update_params=True): """Import JSON file containing parameters in dictionary. @@ -70,7 +70,7 @@ def load_params(self, dir=".", update_params=True): dir: update_params: """ - #| - load_params + # | - load_params try: data = open(os.path.join(dir, "dft-params.json")).read() data = json.loads(data) @@ -84,14 +84,14 @@ def load_params(self, dir=".", update_params=True): print("ase_modules/dft_params.py") print(" Couldn't read dft-params.json file") pass - #__| + # __| def PythonPath(self): """Return PYTHONPATH system variable_lst. Checks dir is checked for default_espresso_params file """ - #| - PythonPath + # | - PythonPath pyth_paths = os.environ["PYTHONPATH"].split(os.pathsep) @@ -104,7 +104,7 @@ def PythonPath(self): return pyth_path[0] - #__| + # __| def update_params(self, new_params, user_update=True): """Update parameter dict with new keys or new values for old keys. @@ -113,13 +113,13 @@ def update_params(self, new_params, user_update=True): new_params: user_update: """ - #| - update_params + # | - update_params self.params.update(new_params) if user_update: mod_d_new = {x: True for x in new_params} self.mod_dict.update(mod_d_new) - #__| + # __| def write_params(self, path_i=".", overwrite=False): """Write parameters to file in getcwd. @@ -130,12 +130,12 @@ def write_params(self, path_i=".", overwrite=False): path: overwrite:s """ - #| - write_params + # | - write_params def json_dump_command(params, file): - #| - json_dump_command + # | - json_dump_command json.dump(params, file, indent=2, skipkeys=True) - #__| + # __| num_files = [fle for fle in os.listdir(path_i) if "dft-params" in fle] num_files = len(num_files) @@ -152,21 +152,21 @@ def json_dump_command(params, file): else: with open(path_i + "/" + self.file_name + ".json", "w+") as fle: json_dump_command(self.params, fle) - #__| + # __| - #__| *********************************************************************** + # __| *********************************************************************** class VASP_Params(DFT_Params): """Useful method to define VASP parameters for DFT job.""" - #| - VASP_Params *********************************************************** + # | - VASP_Params *********************************************************** def __init__(self, load_defaults=True): """Class encapsulating VASP job parameters. Args: load_defaults: """ - #| - __init__ + # | - __init__ # self.pymoddir = self.PythonPath() DFT_Params.__init__(self) @@ -179,11 +179,11 @@ def __init__(self, load_defaults=True): # self.params = self.load_params(self.pymoddir, # "default_espresso_params.json") - #__| + # __| def default_params(self): """User-defined default DFT parameters.""" - #| - default_params + # | - default_params # Do calculation my_encut = 500 @@ -271,7 +271,7 @@ def default_params(self): params["lepsilon"] = False return(params) - #__| + # __| def create_mod_dict(self): """ @@ -281,27 +281,27 @@ def create_mod_dict(self): externally in script or if it kept at its default value. This is useful to know for the dependent-parameters. """ - #| - create_mod_dict + # | - create_mod_dict param_keys = list(self.params.keys()) mod_dict = dict((key, False) for key in param_keys) return(mod_dict) - #__| + # __| - #__| *********************************************************************** + # __| *********************************************************************** class Espresso_Params(DFT_Params): """Useful method to define quantum espresso parameters for DFT job.""" - #| - Espresso_Params ******************************************************* + # | - Espresso_Params ******************************************************* def __init__(self, load_defaults=True): """Class encapsulating quantum espresso job parameters. Args: load_defaults: """ - #| - __init__ + # | - __init__ DFT_Params.__init__(self) if load_defaults: @@ -310,11 +310,11 @@ def __init__(self, load_defaults=True): self.params = {} self.mod_dict = self.create_mod_dict() - #__| + # __| def default_params(self): # ********************************************** """User-defined default DFT parameters.""" - #| - default_params + # | - default_params params = {} params["pw"] = 500 # plane-wave cutoff @@ -326,13 +326,13 @@ def default_params(self): # ********************************************** # TODO Scale number of bands with system size params["nbands"] = -50 - #| - Spin & Magnitism + # | - Spin & Magnitism # Spin-polarized calculation params["spinpol"] = False # Non-collinear magnetism, magnetization in generic direction params["noncollinear"] = False - #__| + # __| params["sigma"] = 0.1 # Should be low for spin calculations @@ -341,7 +341,7 @@ def default_params(self): # ********************************************** # Parallelization <---------------------------------------------------- params["parflags"] = None - #| - Convergence Parameters + # | - Convergence Parameters params["convergence"] = { "energy": 1e-5, # convergence parameters @@ -352,9 +352,9 @@ def default_params(self): # ********************************************** "maxsteps": 500, "diag": "david", } - #__| + # __| - #| - File Output <----------------------------------------------------- + # | - File Output <----------------------------------------------------- params["output"] = { "avoidio": True, "removesave": True, @@ -363,10 +363,10 @@ def default_params(self): # ********************************************** } params["outdir"] = "calcdir" - #__| + # __| return(params) - #__| + # __| def create_mod_dict(self): """Create modification book-keepig dictionary. @@ -375,12 +375,12 @@ def create_mod_dict(self): externally in script or if it kept at its default value. This is useful to know for the dependent-parameters. """ - #| - create_mod_dict + # | - create_mod_dict param_keys = list(self.params.keys()) mod_dict = dict((key, False) for key in param_keys) return(mod_dict) - #__| + # __| def test_check(self): """Attempts to set params based on other dependent parameters. @@ -391,24 +391,24 @@ def test_check(self): Only set this dependent param if the user has not explicitly done so, is what the condition for setting a parameter automatically is. """ - #| - test_check + # | - test_check - #| - Setting dw to 10 * pw + # | - Setting dw to 10 * pw if self.mod_dict["dw"] is False: dw_i = 10. * self.params["pw"] self.update_params({"dw": dw_i}, user_update=False) - #__| + # __| - #| - Decreasing Sigma for Spin Polarization Calculations + # | - Decreasing Sigma for Spin Polarization Calculations if self.mod_dict["sigma"] is False: if self.params["spinpol"] is True: sigma_i = 0.02 self.update_params({"sigma": sigma_i}, user_update=False) - #__| + # __| - #| - BEEF Ensemble of Energies ========================================= + # | - BEEF Ensemble of Energies ========================================= - #| - Removing Beef-Ensemble if XC-Functional Not BEEF + # | - Removing Beef-Ensemble if XC-Functional Not BEEF xc_list = self.params["xc"] if "beefensemble" in self.params: if self.params["beefensemble"] is True and "BEEF" not in xc_list: @@ -417,9 +417,9 @@ def test_check(self): self.update_params({"printensemble": False}, user_update=False) else: pass - #__| + # __| - #| - Turn on printensemble Parameter for BEEF Ensemble of Energies + # | - Turn on printensemble Parameter for BEEF Ensemble of Energies xc_list = self.params["xc"] if "beefensemble" in self.params: if self.params["beefensemble"] is True and "BEEF" in xc_list: @@ -430,9 +430,9 @@ def test_check(self): else: pass - #__| + # __| - #| - Turn off BEEF on AWS + # | - Turn off BEEF on AWS # NOTE This is new (180412 - RF), check that it works CC = ComputerCluster() if CC.cluster_sys == "aws": @@ -454,11 +454,11 @@ def test_check(self): user_update=False, ) - #__| + # __| - #__| ================================================================== + # __| ================================================================== - #| - Parallelization + # | - Parallelization if self.mod_dict["parflags"] is False: if type(self.submission_params) == dict: if "nodes" in self.submission_params: @@ -472,17 +472,17 @@ def test_check(self): # TEMP_PRINT print("098sddfkfs--s-s-__-_") print("-npool " + str(int(num_nodes))) - #__| + # __| - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** -#| - __old__ | default_params with all the commented lines +# | - __old__ | default_params with all the commented lines # params = {} # # # params["mode"] = "ase3" @@ -498,13 +498,13 @@ def test_check(self): # # TODO Scale number of bands with system size # params["nbands"] = -50 # -# #| - Spin & Magnitism +# # | - Spin & Magnitism # # Spin-polarized calculation # params["spinpol"] = False # # # Non-collinear magnetism, magnetization in generic direction # params["noncollinear"] = False -# #__| +# # __| # # params["sigma"] = 0.1 # Should be low for spin calculations # @@ -517,7 +517,7 @@ def test_check(self): # # params["parflags"] = "-npool " # params["parflags"] = None # -# #| - Convergence Parameters +# # | - Convergence Parameters # params["convergence"] = { # "energy": 1e-5, # convergence parameters # @@ -528,9 +528,9 @@ def test_check(self): # "maxsteps": 500, # "diag": "david", # } -# #__| +# # __| # -# #| - File Output <----------------------------------------------------- +# # | - File Output <----------------------------------------------------- # # # output = {'disk_io':'default', # how often espresso writes wavefunctions to disk # # 'avoidio':False, # will overwrite disk_io parameter if True @@ -555,4 +555,4 @@ def test_check(self): # # params["outdir"] = "calcdir" # return(params) -#__| +# __| diff --git a/ase_modules/get_G.py b/ase_modules/get_G.py index 9364c40..5bea963 100644 --- a/ase_modules/get_G.py +++ b/ase_modules/get_G.py @@ -11,7 +11,7 @@ add Sr, Ru references """ -#| - Import Modules +# | - Import Modules import os import glob @@ -21,7 +21,7 @@ from ase.atoms import Atoms from ase.io import read -#__| +# __| class Get_G: """ @@ -29,7 +29,7 @@ class Get_G: missing between them to a number of gas-phase or aqueous references """ - #| - Get_G + # | - Get_G def __init__(self, slab, ads, @@ -42,7 +42,7 @@ def __init__(self, Get_G(ads,slab) where ads/slab are either paths to traj files or paths to directory containing traj files that have energy and forces. The directories that contain these traj files must also contain a calculation directory with pw.inp. """ - #| - __init__ + # | - __init__ self.default_vib_bool = default_vib_bool self.get_E = get_E self.quiet = quiet @@ -89,13 +89,13 @@ def __init__(self, # self.calc_G() # self.compare_wf() - #__| + # __| def read_atoms(self,path,**kwargs): """ Loads traj file. Path must be direct path to traj files or paths to directory containing them named as either qn.traj or qnXX.traj. """ - #| - read_atoms + # | - read_atoms if path.find('traj') != -1: #Checks if directory or filename has been specified atoms = read(path) if '/' in path: @@ -120,14 +120,14 @@ def read_atoms(self,path,**kwargs): if self.fmax(atoms) > 0.05: print("WARNING: fmax = %.2f for atoms in %s"%(self.fmax(atoms),atoms.PATH)) return atoms - #__| + # __| def update_params(self,atoms): """ Takes atoms object containing PATH attribute and adds PARAMS dict as attribute with keys pw, dw, xc, pp. Assumes PATH contains outdir or calcdir with pw.inp """ - #| - update_params + # | - update_params if os.path.isdir(atoms.PATH + '/outdir'): calcdir = atoms.PATH + '/outdir' elif os.path.isdir(atoms.PATH + '/calcdir'): @@ -160,14 +160,14 @@ def update_params(self,atoms): self.params[key] = synonyms[key][self.params[key]] atoms.PARAMS = self.params - #__| + # __| def update_delta_atoms(self): """ Update dictionary self.data_atoms with difference in chemical formulas between ads and slab. Positive numbers represent species that ads has and slab doesn't. """ - #| - update_delta_atoms + # | - update_delta_atoms self.delta_atoms = {} ads_syms = self.ads_atoms.get_chemical_symbols() slab_syms = self.slab_atoms.get_chemical_symbols() @@ -203,20 +203,20 @@ def update_delta_atoms(self): for key in list(self.delta_atoms): if self.delta_atoms[key] == 0: del self.delta_atoms[key] - #__| + # __| def vib_correction(self): """ Attempt to add explicitly calculated vibrational correction from vib directory within PATH of ads_atoms and slab_atoms. Otherwise, default vibrational corrections are used by calling default_vib() """ - #| - vib_correction + # | - vib_correction def vib_indices(atoms): """ Return a dict with symbol/count key/value pairs given an atoms object with PATH attribute """ - #| - vib_indices + # | - vib_indices dict = {} vib_pkls = glob.glob(atoms.PATH + '/vib/vib.*.pckl') @@ -235,13 +235,13 @@ def vib_indices(atoms): dict[sym] = 1 return dict - #__| + # __| def parse_corr(path): """ Return vibrational correction from myjob.out in path. Return -1 if myjob.out cannot be read (imag freqs?) """ - #| - parse_corr + # | - parse_corr file = open(path) lines = file.readlines() file.close() @@ -263,7 +263,7 @@ def parse_corr(path): except: return -1 """ - #__| + # __| if self.get_E: self.ads_corr = 0 @@ -315,13 +315,13 @@ def parse_corr(path): else: self.default_vib() return - #__| + # __| def default_vib(self): """ Calculate vibrational corrections using estimates based on atom identity. """ - #| - default_vib + # | - default_vib self.default_vib_bool = True self.ads_corr = 0 self.slab_corr = 0 @@ -330,13 +330,13 @@ def default_vib(self): self.ads_corr += default_vib_dict[sym]*self.delta_atoms[sym] else: self.slab_corr -= default_vib_dict[sym]*self.delta_atoms[sym] - #__| + # __| def set_refs(self): """ Formulate references for atoms in self.delta_atoms. Currently chooses reference indicated as default. """ - #| - set_refs + # | - set_refs self.references = {} for sym in self.delta_atoms: for atom in references: @@ -344,13 +344,13 @@ def set_refs(self): for ref in references[atom]: if len(references[atom][ref]) == 3: #use default reference self.references[sym] = references[atom][ref][:2] - #__| + # __| def calc_G(self): """ Perform free energy calculation at standard conditions and record thermodynamic dependencies on pressure, tempreature, potential, etc. """ - #| - calc_G + # | - calc_G self.G_std = self.ads_atoms.get_potential_energy() - self.slab_atoms.get_potential_energy() self.G_std += self.ads_corr - self.slab_corr self.G_thermo = {} @@ -364,13 +364,13 @@ def calc_G(self): self.G_thermo[thermo] -= n*self.delta_atoms[sym] else: self.G_thermo[thermo] = -n*self.delta_atoms[sym] - #__| + # __| def get_DFT_ref(self,ref): """ Pull appropriate DFT reference energy from database according to computational parameters """ - #| - get_DFT_ref + # | - get_DFT_ref xc = self.params['xc'] pwdw =(self.params['pw'],self.params['dw']) pp = self.params['pp'] @@ -390,22 +390,22 @@ def get_DFT_ref(self,ref): return DFT_references[ref][xc][pwdw][pp_ref] raise Exception("No reference found for %s with %s @ %s with %s"%(ref,xc,pwdw,pp)) - #__| + # __| def compare_pp(self,pp1,pp2,syms): """ """ - #| - compare_pp + # | - compare_pp for sym in syms: if not filecmp.cmp("%s/%s.UPF"%(pp1,sym),"%s/%s.UPF"%(pp2,sym)): return False return True - #__| + # __| def fmax(self,atoms): """ """ - #| - fmax + # | - fmax forces = atoms.get_forces() max = 0 for force in forces: @@ -413,12 +413,12 @@ def fmax(self,atoms): if tot > max: max = tot return max - #__| + # __| def compare_magmoms(self): """ """ - #| - compare_magmoms + # | - compare_magmoms def nearest_atom(atoms,position): "Returns atom nearest to position" position = np.array(position) @@ -482,22 +482,22 @@ def nearest_atom(atoms,position): # print(common) # print("Magnetic moments only present in %s"%not_indexed_by) # print(uncommon + "\n") - #__| + # __| def rms_displacement(self): """ """ - #| - rms_displacement + # | - rms_displacement displacements = np.zeros(np.min((len(self.slab_atoms),len(self.ads_atoms)))) for i in range(len(displacements)): displacements[i] = np.linalg.norm(self.slab_atoms[i].position-self.ads_atoms[i].position)**2 return np.sqrt(displacements.mean()) - #__| + # __| def compare_wf(self): """ """ - #| - compare_wf + # | - compare_wf self.wf_slab = self.read_wf(self.slab_atoms.PATH) self.wf_ads = self.read_wf(self.ads_atoms.PATH) @@ -507,12 +507,12 @@ def compare_wf(self): self.d_wf = self.wf_ads - self.wf_slab self.avg_wf = (self.wf_ads + self.wf_slab)/2 self.d_mu = 0.0055 * self.d_wf * np.linalg.norm(np.cross(self.ads_atoms.cell[0],self.ads_atoms.cell[1])) - #__| + # __| def read_wf(self,path): """ """ - #| - read_wf + # | - read_wf try: f = open(path + '/out.WF') except: @@ -520,10 +520,10 @@ def read_wf(self,path): line = f.readlines()[0] return float(line.split(',')[0][1:]) - #__| + # __| - #| - __old__ | old __repr__ + # | - __old__ | old __repr__ # def __repr__(self): # """ # String representation of free energy calculation. Example output: @@ -533,7 +533,7 @@ def read_wf(self,path): # Default vibrational corrections applied to adsorbates # Other possible references include H2... # """ - # #| - __repr__ + # # | - __repr__ # string = "" # if self.get_E: # string += "dE = %.3f eV"%self.G_std @@ -560,10 +560,10 @@ def read_wf(self,path): # string += "\nIS WF = %.2f eV, FS WF = %.2f eV, Change in WF = %.2f eV, Change in Dipole = %.2f eA, Average WF = %.2f eV"\ # %(self.wf_slab,self.wf_ads,self.d_wf,self.d_mu,self.avg_wf) # return string - # #__| - #__| + # # __| + # __| - #__| + # __| @@ -571,7 +571,7 @@ def read_wf(self,path): -#| - out_of_sight +# | - out_of_sight rydberg = 13.6057 #rydberg to eV conversion @@ -786,7 +786,7 @@ def read_wf(self,path): } } -#__| +# __| # if __name__ == "__main__": # import argparse diff --git a/atoms_objects/slab_generation.py b/atoms_objects/slab_generation.py index 2a365a7..c45112b 100644 --- a/atoms_objects/slab_generation.py +++ b/atoms_objects/slab_generation.py @@ -5,7 +5,7 @@ Author: Raul A. Flores """ -#| - Import Modules +# | - Import Modules from ase.build import surface # from ase import io @@ -14,7 +14,7 @@ from pymatgen.io.ase import AseAtomsAdaptor from pymatgen.core.surface import SlabGenerator as SlabGenerator_pymatgen -#__| +# __| def cut_slab_ase( bulk, @@ -28,7 +28,7 @@ def cut_slab_ase( bulk: facet: """ - #| - cut_slab_ase + # | - cut_slab_ase # facet = [1, 1, 0] # layers = 6 # vacuum = 8 @@ -45,7 +45,7 @@ def cut_slab_ase( # slab.write(out_file) return(slab) - #__| + # __| def cut_slab_pymatgen( bulk, @@ -59,7 +59,7 @@ def cut_slab_pymatgen( bulk: facet: """ - #| - cut_slab_pymatgen + # | - cut_slab_pymatgen # atoms = io.read("init.traj") # atoms = io.read("init.cif") @@ -92,7 +92,7 @@ def cut_slab_pymatgen( # IStructure.to(Al55, "poscar", filename="POSCAR") return(slab) - #__| + # __| def cut_slab_catkit( bulk, @@ -106,7 +106,7 @@ def cut_slab_catkit( bulk: facet: """ - #| - cut_slab_catkit + # | - cut_slab_catkit gen = SlabGenerator_catkit( bulk, miller_index=facet, @@ -128,4 +128,4 @@ def cut_slab_catkit( # view(images) return(images) - #__| + # __| diff --git a/aws/aws_class.py b/aws/aws_class.py index c46ddb9..9eacf70 100644 --- a/aws/aws_class.py +++ b/aws/aws_class.py @@ -5,7 +5,7 @@ Author: Raul A. Flores """ -#| - Import Modules +# | - Import Modules from dft_job_automat.job_setup import DFT_Jobs_Setup import os import errno @@ -17,50 +17,50 @@ import pandas as pd import boto3 -#__| +# __| def force_symlink(file1, file2): - #| - force_symlink + # | - force_symlink try: os.symlink(file1, file2) except OSError, e: if e.errno == errno.EEXIST: os.unlink(file2) os.symlink(file1, file2) - #__| + # __| class AWS_Queues(): """ """ - #| - AWS_Class ***************************************************************** + # | - AWS_Class ***************************************************************** def __init__(self): """ """ - #| - __init__ + # | - __init__ try: self.aws_dir = os.environ["TRI_PATH"] self.job_queue_dir = self.aws_dir + "/bin/job_queues" except: pass - #__| + # __| def job_info_batch(self, job_id): """ """ - #| - job_info_batch + # | - job_info_batch batch = boto3.client("batch") job_descriptions = batch.describe_jobs(jobs=[job_id]) - #| - Checking if Job is in AWS Batch + # | - Checking if Job is in AWS Batch if len(job_descriptions["jobs"]) == 0: return("job not in batch system") else: job_info = job_descriptions["jobs"][0] - #__| + # __| job_status = job_info["status"] job_path = job_info["parameters"]["model"] @@ -84,27 +84,27 @@ def job_info_batch(self, job_id): "job_name": job_name} return(job_queue_dict) - #__| + # __| def cancel_job(self, job_id, reason="N/A"): """ """ - #| - cancel_job + # | - cancel_job bash_command = "aws batch cancel-job --job-id " bash_command += job_id + " --reason " + reason print("Cancelling job | " + job_id) run_bash = subprocess.check_output(bash_command, shell=True) - #__| + # __| def cancel_jobs(self, job_id_list): """Cancels all jobs in a given job id list """ - #| - cancel_jobs + # | - cancel_jobs for job in job_id_list: self.cancel_job(job) - #__| + # __| def list_jobs(self, queue="small"): """ @@ -112,7 +112,7 @@ def list_jobs(self, queue="small"): Included jobs in SUBMITTED, PENDING, RUNNABLE, STARTING, RUNNING, and FAILED states. """ - #| - list_jobs + # | - list_jobs batch = boto3.client("batch") job_status_opt = ["SUBMITTED", "PENDING", "RUNNABLE", "STARTING", "RUNNING", "SUCCEEDED", "FAILED"] @@ -122,15 +122,15 @@ def list_jobs(self, queue="small"): all_jobs = batch.list_jobs(jobQueue=queue, jobStatus=status) # TEMP job_ids = [i["jobId"] for i in all_jobs["jobSummaryList"]] - #| - Checking that queue is being used + # | - Checking that queue is being used print(str(len(job_ids)) + " jobs in the " + status + " queue") # # if len(job_ids) < 1: # print("No jobs in the " + status + " queue") - #__| + # __| - #| - Retreiving Job ID's From AWS + # | - Retreiving Job ID's From AWS job_ids_split = [job_ids[x:x+100] for x in xrange(0, len(job_ids), 100)] for j in range(len(job_ids_split)): job_descriptions = batch.describe_jobs(jobs=job_ids_split[j]) @@ -141,23 +141,23 @@ def list_jobs(self, queue="small"): "model": i["parameters"]["model"], "job_state": status, }) - #__| + # __| return(job_id_list) - #__| + # __| def submit_job(self, path=None, queue="test", cpus="default", copy_PythonModules=True): """ Submits job to aws cluster. Copies PythonModules folder into job directory """ - #| - submit_job + # | - submit_job root_dir = os.getcwd() if path == None: path = root_dir - #| - Checking if job has already been submitted + # | - Checking if job has already been submitted os.chdir(path) if os.path.isfile(".SUBMITTED"): print("Directory already submitted, will be skipped") @@ -165,10 +165,10 @@ def submit_job(self, path=None, queue="test", cpus="default", copy_PythonModules return(None) # <-------- SKIP JOB --------------------------------- else: os.chdir(root_dir) - #__| + # __| - #| - Copy PYTHONMODULES to Job Directory + # | - Copy PYTHONMODULES to Job Directory if os.path.isdir(path + "/PythonModules") == True: print("PythonModules already exists, erasing and recopying") shutil.rmtree(path + "/PythonModules") @@ -177,10 +177,10 @@ def submit_job(self, path=None, queue="test", cpus="default", copy_PythonModules else: py_mod = os.environ["python_modules"] shutil.copytree(py_mod, path + "/PythonModules") - #__| + # __| - #| - Submit Job + # | - Submit Job os.chdir(path) if os.path.isfile(".sSUBMITTED"): @@ -211,17 +211,17 @@ def submit_job(self, path=None, queue="test", cpus="default", copy_PythonModules print("JOB SKIPPED: ") return(None) - #__| + # __| - #| - Parsing Submission for Job ID + # | - Parsing Submission for Job ID output = output.splitlines() for line in output: if "jobId" in line: lst = line.split('"') job_id_ind = (lst.index("jobId") + 2) jobId = lst[job_id_ind] - #__| + # __| file = open(".submitted", "w") file.close() @@ -231,7 +231,7 @@ def submit_job(self, path=None, queue="test", cpus="default", copy_PythonModules os.chdir(root_dir) - #| - Querying AWS For Job Info + # | - Querying AWS For Job Info job_queue_dict = self.job_info_batch(jobId) job_queue_dict["submit_time"] = sub_time @@ -245,12 +245,12 @@ def submit_job(self, path=None, queue="test", cpus="default", copy_PythonModules df = df_new df.to_csv(jobs_file_path, index=False) - #__| + # __| return job_queue_dict - #__| + # __| - #__| *************************************************************************** + # __| *************************************************************************** class AWS_Class_tmp(): @@ -258,27 +258,27 @@ class AWS_Class_tmp(): TEMP """ - #| - AWS_Class_tmp ************************************************************* + # | - AWS_Class_tmp ************************************************************* def __init__(self): """TMP_docstring. TEMP TEMP """ - #| - __init__ + # | - __init__ self.tmp = "TMP AWS Class Atribute!!!!!" try: self.TRI_PATH = os.environ["TRI_PATH"] except: pass - #__| + # __| def create_symlinks(self): """ Attempts to create symlinks in all subfolders whose names starts with _ """ - #| - create_symlinks + # | - create_symlinks # root_dir = '.' root_dir = os.getcwd() for dir_name, subdirList, fileList in os.walk(root_dir): @@ -299,23 +299,23 @@ def create_symlinks(self): # print("Found directory: %s" % dirName) # for fname in fileList: # print('\t%s' % fname) - #__| + # __| def create_symlink(self, path=None): """ """ - #| - create_symlink + # | - create_symlink # def force_symlink(file1, file2): - # #| - force_symlink + # # | - force_symlink # try: # os.symlink(file1, file2) # except OSError, e: # if e.errno == errno.EEXIST: # os.unlink(file2) # os.symlink(file1, file2) - # #__| + # # __| if path == None: path = os.getcwd() @@ -329,9 +329,9 @@ def create_symlink(self, path=None): source = source.replace("/model/", "/simulation/", 1) force_symlink(source, dest) - #__| + # __| - #__| *************************************************************************** + # __| *************************************************************************** # NOT USEFUL FOR THIS TO BE CHILD CLASS -- FIX!!!!!!!!!!!! class AWS_Class(DFT_Jobs_Setup): @@ -339,13 +339,13 @@ class AWS_Class(DFT_Jobs_Setup): TEMP """ - #| - AWS_Class ***************************************************************** + # | - AWS_Class ***************************************************************** def __init__(self, system="sherlock"): """TMP_docstring. TEMP TEMP """ - #| - __init__ + # | - __init__ DFT_Jobs_Setup.__init__(self, system=system) self.tmp = "TMP AWS Class Atribute!!!!!" @@ -353,7 +353,7 @@ def __init__(self, system="sherlock"): self.TRI_PATH = os.environ["TRI_PATH"] except: pass - #__| + # __| def create_symlinks(self): @@ -361,17 +361,17 @@ def create_symlinks(self): TEMP TEMP """ - #| - create_symlinks + # | - create_symlinks def force_symlink(file1, file2): - #| - force_symlink + # | - force_symlink try: os.symlink(file1, file2) except OSError, e: if e.errno == errno.EEXIST: os.unlink(file2) os.symlink(file1, file2) - #__| + # __| for job in self.job_var_lst: path = self.var_lst_to_path(job) @@ -385,20 +385,20 @@ def force_symlink(file1, file2): source = source.replace("/model/", "/simulation/", 1) force_symlink(source, dest) - #__| + # __| - #__| *************************************************************************** + # __| *************************************************************************** -#| - DEPRECATED METHODS +# | - DEPRECATED METHODS # def list_jobs(self, status="RUNNING", queue="small"): # """ # """ -# #| - list_jobs +# # | - list_jobs # bash_command = "aws batch list-jobs --job-status {} --job-queue {} > aws_list_jobs.json".format(status, queue) # run_bash = subprocess.check_output(bash_command, shell=True) #TEMP # data = json.load(open("aws_list_jobs.json")) @@ -409,7 +409,7 @@ def force_symlink(file1, file2): # # return(job_id_list) # -# #__| +# # __| -#__| +# __| diff --git a/bader_charge/bader.py b/bader_charge/bader.py index e24addb..15583d5 100644 --- a/bader_charge/bader.py +++ b/bader_charge/bader.py @@ -5,7 +5,7 @@ Author(s): Colin Dickins wrote most of this; Raul A. Flores """ -#| - IMPORT MODULES +# | - IMPORT MODULES import sys import os @@ -15,7 +15,7 @@ import numpy as np # from ase.io import write from ase import io -#__| +# __| def bader( @@ -42,12 +42,12 @@ def bader( run_exec: Whether to run bader executable or just create preliminary file (some clusters don't/can't have the bader fortran code) """ - #| - bader + # | - bader mess = "Executing Bader Analysis " mess += "*****************************************************" print(mess); sys.stdout.flush() - #| - Don't Run Bader Executable on AWS + # | - Don't Run Bader Executable on AWS if "COMPENV" not in os.environ: print("COMPENV env. var. doesn't exits, probably in AWS?") print("Bader executable turned off") @@ -55,17 +55,17 @@ def bader( run_exec = False else: pass - #__| + # __| if not os.path.exists("dir_bader"): os.makedirs("dir_bader") calc = atoms.calc - #| - Using Spin Polarization + # | - Using Spin Polarization if spinpol: - #| - Spin up + # | - Spin up if convert_charge_den: cd2cube(atoms, spin="up") if run_exec: @@ -77,9 +77,9 @@ def bader( dft_code=dft_code, ) - #__| + # __| - #| - Spin down + # | - Spin down if convert_charge_den: cd2cube(atoms, spin="down") if run_exec: @@ -90,13 +90,13 @@ def bader( clean_up=cleanup, dft_code=dft_code, ) - #__| + # __| print("BADER MAGMOMS: " + str(atoms.get_initial_magnetic_moments())) - #__| + # __| - #| - Not Spin Polarized + # | - Not Spin Polarized else: if convert_charge_den: cd2cube(atoms) @@ -108,7 +108,7 @@ def bader( execute_bader=run_exec_2, dft_code=dft_code, ) - #__| + # __| print("BADER CHARGES: " + str(atoms.get_initial_charges())) @@ -118,7 +118,7 @@ def bader( if outdir: os.system("rm %s/charge.log" % outdir) - #| - Writing 2 Atoms Objects with mogmoms and charges written + # | - Writing 2 Atoms Objects with mogmoms and charges written if run_exec: # Charges written to init_charges @@ -135,9 +135,9 @@ def bader( # atoms.set_calculator(calc=calc) atoms.write("dir_bader/out.traj") - #__| + # __| - #__| + # __| def cd2cube(atoms, spin=""): @@ -150,7 +150,7 @@ def cd2cube(atoms, spin=""): atoms: spin: """ - #| - cd2cube + # | - cd2cube # cd2cube(atoms.calc.extract_charge_density(spin="up")[2], atoms) if spin == "": @@ -195,7 +195,7 @@ def cd2cube(atoms, spin=""): f = open(file_name, "w") f.writelines(lines) f.close() - #__| + # __| def cleanup(suffix="", save_cube=True): """Cleanup unnecessary and/or large file after routine completes. @@ -204,7 +204,7 @@ def cleanup(suffix="", save_cube=True): suffix: save_cube: """ - #| - cleanup + # | - cleanup if not os.path.exists("dir_bader"): os.makedirs("dir_bader") @@ -220,7 +220,7 @@ def cleanup(suffix="", save_cube=True): os.system("mv AVF.dat dir_bader/AVF.dat") os.system("mv BCF.dat dir_bader/BCF.dat") - #__| + # __| def bader_exec( atoms, @@ -237,7 +237,7 @@ def bader_exec( Spin component to process | "", "up", "down" """ - #| - bader_exec + # | - bader_exec if execute_bader: bash_comm = "bader density" + spin + ".cube >> bader.out" os.system(bash_comm) @@ -249,7 +249,7 @@ def bader_exec( bash_comm = "bader CHGCAR -ref CHGCAR_sum" os.system(bash_comm) - #| - Spin Polarlized Calculation + # | - Spin Polarlized Calculation if spin == "up": f = open("ACF.dat"); lines = f.readlines(); f.close() for i, line in enumerate(lines[2:-4]): @@ -268,7 +268,7 @@ def bader_exec( line = line.split() atoms[i].magmom -= float(line[4]) - #| - Getting number of valence electrons + # | - Getting number of valence electrons calc_has_get_nvalence = getattr(atoms.calc, "get_nvalence", None) if calc_has_get_nvalence: val_i = atoms.calc.get_nvalence()[1][atoms[i].symbol] @@ -276,7 +276,7 @@ def bader_exec( val_i = valence_dict.get(atoms[i].symbol, None) assert val_i is not None, "Can't find # of valence electrons!!" - #__| + # __| atoms[i].charge -= float(line[4]) - val_i @@ -287,26 +287,26 @@ def bader_exec( atoms.info.update({"bader_magmoms": magmom_list}) atoms.info.update({"bader_charges": charge_list}) - #| - Write data to file + # | - Write data to file with open("dir_bader/bader_charges_magmoms.pickle", "w") as fle: pickle.dump( {"charge_list": charge_list, "magmom_list": magmom_list}, fle, ) - #__| + # __| if clean_up: cleanup(suffix=spin) - #__| + # __| - #| - Non-Spin Polarized Calculation + # | - Non-Spin Polarized Calculation elif spin == "": charge_list = [] f = open("ACF.dat"); lines = f.readlines(); f.close() for i, line in enumerate(lines[2:-4]): line = line.split() - #| - Getting number of valence electrons + # | - Getting number of valence electrons calc_has_get_nvalence = getattr(atoms.calc, "get_nvalence", None) if calc_has_get_nvalence: charge_i = atoms.calc.get_nvalence()[1][atoms[i].symbol] @@ -314,7 +314,7 @@ def bader_exec( charge_i = valence_dict.get(atoms[i].symbol, None) assert charge_i is not None, "Can't find # of valence electrons!!" - #__| + # __| # charge_i = atoms.calc.get_nvalence()[1][atoms[i].symbol] @@ -323,18 +323,18 @@ def bader_exec( atoms.info.update({"bader_charges": charge_list}) - #| - Write data to file + # | - Write data to file with open("dir_bader/bader_charges_magmoms.pickle", "w") as fle: pickle.dump( {"charge_list": charge_list, "magmom_list": None}, fle, ) - #__| + # __| cleanup() - #__| + # __| - #__| + # __| # From VASP PBE diff --git a/classical_methods/lennard_jones.py b/classical_methods/lennard_jones.py index 51a9fa7..de99b95 100644 --- a/classical_methods/lennard_jones.py +++ b/classical_methods/lennard_jones.py @@ -6,14 +6,14 @@ Author: Raul A. Flores """ -#| - Import Modules +# | - Import Modules import gc import math import numpy as np # import pandas as pd import collections from pymatgen.core.periodic_table import Element from asap3 import LennardJones -#__| +# __| def lennard_jones_sp( epsilon, @@ -36,7 +36,7 @@ def lennard_jones_sp( modified_lj: return: """ - #| - lennard_jones_sp + # | - lennard_jones_sp atomic_num_list = atoms.get_atomic_numbers() atomic_num_list = list(set(atomic_num_list)) atomic_num_list.sort() @@ -45,7 +45,7 @@ def lennard_jones_sp( orig_num_of_atoms = atoms.get_number_of_atoms() - #| - Filter Relevant LJ Parameters + # | - Filter Relevant LJ Parameters row_col_to_keep = [ Element.from_Z(atomic_num).name for @@ -62,7 +62,7 @@ def lennard_jones_sp( # epsilon = epsilon.values # sigma = sigma.values - #__| + # __| calc = LennardJones( list(atomic_type_num_dict), @@ -72,15 +72,15 @@ def lennard_jones_sp( modified=modified_lj, ) - #| - Repeat Unit Cell + # | - Repeat Unit Cell repeat_unit_cell = repeat_unit_cell_ASAP(atoms, sigma) atoms = atoms.repeat(repeat_unit_cell) - #__| + # __| atoms.set_calculator(calc) - #| - Energy + # | - Energy lj_energy = atoms.get_potential_energy() lj_energy_per_atom = lj_energy / atoms.get_number_of_atoms() @@ -93,14 +93,14 @@ def lennard_jones_sp( else: lj_energy = lj_total_energy - #__| + # __| - #| - Forces -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* + # | - Forces -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* # COMBAK # Find a way to decrease the number of atoms again lj_forces = atoms.get_forces() - #__| + # __| if return_quantity == "energy": out = lj_energy @@ -110,7 +110,7 @@ def lennard_jones_sp( out = (lj_energy, lj_forces) return(out) - #__| + # __| def repeat_unit_cell_ASAP(atoms, sigma): """Return repeat array such that ASAP doesn't complain about r_cut. @@ -118,7 +118,7 @@ def repeat_unit_cell_ASAP(atoms, sigma): Args: atoms """ - #| - repeat_unit_cell_ASAP + # | - repeat_unit_cell_ASAP def calc_cell_heights(unit_cell): """Calculate heights of cell. @@ -129,7 +129,7 @@ def calc_cell_heights(unit_cell): unit_cell: sigma: """ - #| - calc_cell_heights + # | - calc_cell_heights determinant = np.cross( unit_cell[0], unit_cell[1], @@ -148,7 +148,7 @@ def calc_cell_heights(unit_cell): heights.append(height_i) return(heights) - #__| + # __| heights = calc_cell_heights(atoms.cell) @@ -159,7 +159,7 @@ def calc_cell_heights(unit_cell): cut_off_length = 3 * 2 * max_sigma - #| - Repeat Unit Cell + # | - Repeat Unit Cell repeat_unit_cell = [] for i_ind, height_i in enumerate(heights): if height_i < cut_off_length: @@ -168,7 +168,7 @@ def calc_cell_heights(unit_cell): repeat_unit_cell.append(cell_repeat_fact) else: repeat_unit_cell.append(1) - #__| + # __| return(repeat_unit_cell) - #__| + # __| diff --git a/colors/colors.py b/colors/colors.py index f6c5996..3a06acf 100644 --- a/colors/colors.py +++ b/colors/colors.py @@ -5,11 +5,11 @@ Author: Raul A. Flores """ -#| - Import Modules +# | - Import Modules import seaborn as sns import colorlover as cl -#__| +# __| color_list = [ "rgb(113,166,190)", @@ -25,7 +25,7 @@ ] -#| - My Sets of Contrasting Color Schemes +# | - My Sets of Contrasting Color Schemes color_palette_1 = [ "rgb(198,158,61)", "rgb(238,88,121)", @@ -42,7 +42,7 @@ "rgb(93,80,139)", ] -#__| +# __| # palette = sns.color_palette(None, 3) # palette @@ -63,12 +63,12 @@ def generate_color_palette( bins: Number of colors to produce """ - #| - generate_color_palette + # | - generate_color_palette # tmp = sns.palplot(sns.color_palette(palette_name, bins)) colors = sns.color_palette(palette_name, bins) return(colors) - #__| + # __| @@ -76,7 +76,7 @@ def generate_color_palette( def rgb_to_hex(rgb_tuple): """ """ - #| - rgb_to_hex + # | - rgb_to_hex r = int(rgb_tuple[0]) g = int(rgb_tuple[1]) b = int(rgb_tuple[2]) @@ -91,7 +91,7 @@ def clamp(x): ) return(hex_rep) - #__| + # __| def color_scale_interp( input_num, @@ -102,7 +102,7 @@ def color_scale_interp( ): """ """ - #| - color_scale_interp + # | - color_scale_interp # cl.scales["8"]["seq"]["Purples"] black_white_cs = [ @@ -144,4 +144,4 @@ def color_scale_interp( return(color_out) - #__| + # __| diff --git a/compute_envs/nersc.py b/compute_envs/nersc.py index c34c1c6..16c7864 100644 --- a/compute_envs/nersc.py +++ b/compute_envs/nersc.py @@ -5,7 +5,7 @@ """ -#| - Import Modules +# | - Import Modules import os import sys import subprocess @@ -20,7 +20,7 @@ from dft_job_automat.compute_env import ComputerCluster from dft_job_automat.compute_env import slurm_squeue_parse -#__| +# __| @@ -30,7 +30,7 @@ class NERSC_Cluster(ComputerCluster): Two main architectures, knl and haswell """ - #| - NERSC_Cluster ******************************************************** + # | - NERSC_Cluster ******************************************************** def __init__(self, root_dir=".", ): @@ -39,7 +39,7 @@ def __init__(self, Args: root_dir: """ - #| - __init__ + # | - __init__ # print(80 * "#") # print(4 * "jfsd78yf7823w78yhs81") # print(80 * "#") @@ -58,7 +58,7 @@ def __init__(self, self.job_queue_state_key = "STAT" # COMBAK self.error_file = "job.err" self.out_file = "job.out" - #__| + # __| def submit_job_clust(self, **kwargs): """Submit job to sherlck. @@ -66,10 +66,10 @@ def submit_job_clust(self, **kwargs): Args: **kwargs: """ - #| - submit_job + # | - submit_job time.sleep(1.5) - #| - Merging Submission Parameters + # | - Merging Submission Parameters params = merge_two_dicts(self.default_sub_params, kwargs) path = params["path_i"] @@ -94,7 +94,7 @@ def submit_job_clust(self, **kwargs): params["min_time"] = 180 # --time-min=01:30:00 - #__| + # __| self.__import_arch_class__(params) @@ -102,7 +102,7 @@ def submit_job_clust(self, **kwargs): params = merge_two_dicts(params, self.arch_inst.sbatch_params) - #| - Write submission script + # | - Write submission script knl_sub_script = """#!/bin/bash # KNL submission bash script @@ -148,10 +148,10 @@ def submit_job_clust(self, **kwargs): with open(os.path.join(path, 'submit_job.pbs'), 'w') as the_file: the_file.write(sub_script) - #__| + # __| - #| - Submit Job + # | - Submit Job os.chdir(path) if params["job_name"] == "Default": params["job_name"] = os.getcwd() @@ -159,9 +159,9 @@ def submit_job_clust(self, **kwargs): print("submitting job") os.system("chmod 777 *") # bash_command = "/u/if/flores12/bin/qv model.py" - #__| **** TEMP + # __| **** TEMP - #| - Bash Submisssion Command + # | - Bash Submisssion Command # The -q flag is being used in place of the -p flag # Only the -q needs to be defined @@ -196,7 +196,7 @@ def submit_job_clust(self, **kwargs): print("Bash Submission Command:") print(bash_command) - #__| + # __| try: output = subprocess.Popen( @@ -213,7 +213,7 @@ def submit_job_clust(self, **kwargs): print("JOB SKIPPED: ") return(None) - #| - Parsing Output + # | - Parsing Output out, err = output.communicate() out_copy = copy.deepcopy(out) @@ -225,9 +225,9 @@ def submit_job_clust(self, **kwargs): except: print("Couldn't parse for jobid") job_id = None - #__| + # __| - #| - Writing Files + # | - Writing Files with open(".SUBMITTED", "w") as fle: fle.write("\n") @@ -244,16 +244,16 @@ def submit_job_clust(self, **kwargs): else: with open(".sub_out", "w") as fle: fle.write(out_copy) - #__| + # __| os.chdir(self.root_dir) - #__| + # __| def default_submission_parameters(self): """Defaul SLURM parameters for Sherlock cluster.""" - #| - default_submission_parameters + # | - default_submission_parameters def_params = { "queue": "regular", # -p flag | regular, debug @@ -288,13 +288,13 @@ def default_submission_parameters(self): } return(def_params) - #__| + # __| def __import_arch_class__(self, params): """ """ - #| - __import_arch_class__ + # | - __import_arch_class__ arch = params["architecture"] if arch == "knl": @@ -304,15 +304,15 @@ def __import_arch_class__(self, params): self.arch_inst = arch_inst - #__| + # __| - #| - out of sight + # | - out of sight def job_state_dict(self): """ """ - #| - job_state_dict + # | - job_state_dict job_state_dict = { "PD": "PENDING", "R": "RUNNING", @@ -326,13 +326,13 @@ def job_state_dict(self): } return(job_state_dict) - #__| + # __| def __queue_types__(self): """Queue types for Edison cluster """ - #| - __queue_types__ + # | - __queue_types__ queue_list = [ "regular", "debug", @@ -340,12 +340,12 @@ def __queue_types__(self): ] return(queue_list) - #__| + # __| def job_info_batch(self, job_id, path_i=None): """ """ - #| - job_info_batch + # | - job_info_batch data_dict = slurm_squeue_parse( job_id, path_i=path_i, @@ -355,7 +355,7 @@ def job_info_batch(self, job_id, path_i=None): return(data_dict) - #| - __old__ + # | - __old__ # bash_comm = "squeue -j " + str(job_id) # # try: @@ -432,9 +432,9 @@ def job_info_batch(self, job_id, path_i=None): # print(data_dict) # # return(data_dict) - #__| + # __| - #__| + # __| def completed_file(self, path_i="."): """Check whether ".FINISHED" file exists. @@ -444,13 +444,13 @@ def completed_file(self, path_i="."): Args: path_i: """ - #| - completed_file + # | - completed_file completed_fle = False if os.path.exists(path_i + "/.FINISHED"): completed_fle = True return(completed_fle) - #__| + # __| def job_state(self, path_i="."): """Return job state of path_i --> job_i. @@ -458,7 +458,7 @@ def job_state(self, path_i="."): Args: path_i """ - #| - job_state + # | - job_state job_id = self.get_jobid(path_i=path_i) job_state_out = None @@ -471,14 +471,14 @@ def job_state(self, path_i="."): job_state_out = job_info[key] job_state_out = self.job_state_keys[job_state_out] - #| - Checking for "completed" file indicating success + # | - Checking for "completed" file indicating success completed_fle = self.completed_file(path_i=path_i) if completed_fle: job_state_out = self.job_state_keys["SUCCEEDED"] - #__| + # __| return(job_state_out) - #__| + # __| def get_jobid(self, path_i="."): """Return job ID of job_i. @@ -486,7 +486,7 @@ def get_jobid(self, path_i="."): Args: path_i: """ - #| - get_jobid + # | - get_jobid fileid_path = path_i + "/.jobid" if os.path.isfile(fileid_path): with open(path_i + "/.jobid") as fle: @@ -495,11 +495,11 @@ def get_jobid(self, path_i="."): jobid = None return(jobid) - #__| + # __| - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** @@ -509,14 +509,14 @@ class KNL_Arch(): """ """ - #| - KNL_Arch ************************************************************* + # | - KNL_Arch ************************************************************* def __init__(self): """Initialize Sherlock cluster instance. Args: root_dir: """ - #| - __init__ + # | - __init__ self.vasp_module = "vasp-tpc/5.4.4-knl" # Actually 68 @@ -532,12 +532,12 @@ def __init__(self): os.putenv("TMPDIR", "$SLURM_SUBMIT_DIR") os.putenv("VASP_SCRIPT", "./run_vasp.py") - #__| + # __| def __make_run_vasp_script__(self, params): """ """ - #| - __make_run_vasp_script__ + # | - __make_run_vasp_script__ os.system("echo import os > run_vasp.py") exitcode_line = "exitcode = os.system('srun -n" + " " + \ str(int(self.cores_per_node * int(params["nodes"]))) + " " + \ @@ -556,34 +556,34 @@ def __make_run_vasp_script__(self, params): os.path.join(params["path_i"], "run_vasp.py"), ) - #__| + # __| def __self_sbatch_settings__(self): """ """ - #| - __self_sbatch_settings__ + # | - __self_sbatch_settings__ def_params = { "constraints": "knl", "tasks-per-node": 66, } return(def_params) - #__| + # __| def __module_load_vasp__(self): """ THIS DOESN'T WORK """ - #| - __module_load_vasp__ + # | - __module_load_vasp__ bash_comm = "module load " + self.vasp_module # TEMP print("fjjisdjfjisdfu8h3w8whgiafd6gwgundssoijgg") print(bash_comm) os.system(bash_comm) - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** @@ -592,38 +592,38 @@ class Haswell_Arch(): """ """ - #| - Haswell_Arch ********************************************************* + # | - Haswell_Arch ********************************************************* def __init__(self): """Initialize Sherlock cluster instance. Args: root_dir: """ - #| - __init__ + # | - __init__ self.vasp_module = "vasp-tpc/5.4.4-hsw" # Actually 68 self.cores_per_node = 32 self.sbatch_params = self.__self_sbatch_settings__() - #__| + # __| def __self_sbatch_settings__(self): """ """ - #| - __self_sbatch_settings__ + # | - __self_sbatch_settings__ def_params = { "constraints": "haswell", # "tasks-per-node": 66, } return(def_params) - #__| + # __| def __make_run_vasp_script__(self, params): """ """ - #| - __make_run_vasp_script__ + # | - __make_run_vasp_script__ os.system("echo import os > run_vasp.py") exitcode_line = "exitcode = os.system('srun -n" + " " + \ str(int(self.cores_per_node * int(params["nodes"]))) + " " + \ @@ -632,6 +632,6 @@ def __make_run_vasp_script__(self, params): os.system(line_2) # on edison - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** diff --git a/dft_job_automat/__old__.py b/dft_job_automat/__old__.py index 7c1c711..1701d9c 100644 --- a/dft_job_automat/__old__.py +++ b/dft_job_automat/__old__.py @@ -1,9 +1,9 @@ - #| - __old__ + # | - __old__ # def __parse_username__(self): # """ # """ - # #| - __parse_username__ + # # | - __parse_username__ # username = os.environ.get("USER") # # cond_1 = False @@ -20,28 +20,28 @@ # print(username) # print("*******") # return(username) - # #__| - #__| + # # __| + # __| -#| - job_analysis +# | - job_analysis -#| - DEPRECATED METHODS +# | - DEPRECATED METHODS # def max_force(self, path): # """ # """ -# #| - max_force +# # | - max_force # with open(path + "/simulation/qn.log", "r") as file: # max_f = file.readlines()[-1].split(" ")[-1] # return(float(max_f)) -# #__| +# # __| # def job_id_names(self): # """ # """ -# #| - job_id_names +# # | - job_id_names # try: # f = open("job_id_names", "r") # job_id_data = pickle.load(f) @@ -55,20 +55,20 @@ # except: # print("Couldn't parse job_id_names file") # -# #__| -#__| +# # __| +# __| -#__| +# __| -#| - job_setup +# | - job_setup -#| - __old__ +# | - __old__ -#| - TEMP +# | - TEMP # def __parse_cluster_type__(self): # """ # """ -# #| - __parse_cluster_type__ +# # | - __parse_cluster_type__ # clusters_dict = { # "aws": "AWSCluster", # "slac": "SLACCluster", @@ -86,16 +86,16 @@ # self.cluster = cluster # # from dft_job_automat.compute_env import clusters_dict[cluster_sys] # return(cluster_sys) -#__| +# __| -#__| +# __| -#__| +# __| -#| - job_manager +# | - job_manager -#| - TEMP - Old Restart Job Method +# | - TEMP - Old Restart Job Method # def restart_job(self, prev_rev_file_list=[], root_dir_file_list=[], # revision="Auto"): # """Restart job from previous run @@ -113,7 +113,7 @@ # Job revision number from which the job will be restarted from. # "Auto" | Restarts the job from the most recent job revision. # """ -# #| - restart_job +# # | - restart_job # for job in self.job_var_lst: # path = self.var_lst_to_path(job) # @@ -144,10 +144,10 @@ # # os.system("chmod 777 " + new_path + "/*") # -# #__| -#__| +# # __| +# __| -#| - __old__ +# | - __old__ # def job_revision_number(self, variable_lst): """Returns the largest revision number for the job with the given @@ -156,7 +156,7 @@ Args: variable_lst: """ - #| - job_revision_number + # | - job_revision_number # # path = "data/" + self.var_lst_to_path(variable_lst) # path = self.var_lst_to_path(variable_lst) # bash_comm = "" @@ -175,20 +175,20 @@ # os.chdir(self.root_dir) # # return(num_jobs) - #__| + # __| -#__| +# __| -#__| +# __| -#| - compute_env +# | - compute_env -#| - __old__ +# | - __old__ # def get_jobid(self, path_i="."): # """ # """ - # #| - get_jobid + # # | - get_jobid # # path_i = "." # fileid_path = path_i + "/.jobid" # # print(fileid_path) @@ -199,33 +199,33 @@ # jobid=None # # return(jobid) - # #__| + # # __| - #| - OLD + # | - OLD # def job_info_batch(self, path_i="."): - # #| - job_info_batch + # # | - job_info_batch # data_dict = self.cluster.job_info_batch(path_i = path_i) # # return(data_dict) # - # #__| + # # __| # def write_job_queue_state_file(self, path="."): # """ # """ - # #| - write_job_queue_state_file + # # | - write_job_queue_state_file # print(self) # data_dict = self.cluster.job_info_batch(path_i=path) # key = self.cluster.job_state_key # with open(path_i + "/.QUEUESTATE", "w") as fle: # fle.write(data_dict[key]) - #__| + # __| -#__| +# __| -#__| +# __| diff --git a/dft_job_automat/compute_env.py b/dft_job_automat/compute_env.py index def0211..dde5a4b 100644 --- a/dft_job_automat/compute_env.py +++ b/dft_job_automat/compute_env.py @@ -15,7 +15,7 @@ """ -#| - Import Modules +# | - Import Modules import os import sys import subprocess @@ -32,9 +32,9 @@ # My Modules from misc_modules.misc_methods import merge_two_dicts -#__| +# __| -#| - Methods +# | - Methods def slurm_squeue_parse( job_id, @@ -55,7 +55,7 @@ def slurm_squeue_parse( path_i: queue_state_key: """ - #| - slurm_squeue_parse + # | - slurm_squeue_parse bash_comm = "squeue -j " + str(job_id) try: @@ -90,9 +90,9 @@ def slurm_squeue_parse( pass return(data_dict) - #__| + # __| -#__| +# __| ############################################################################### class ComputerCluster(): @@ -102,23 +102,23 @@ class ComputerCluster(): TODO Create dummy cluster for WSL system """ - #| - ComputerCluster ****************************************************** + # | - ComputerCluster ****************************************************** def __init__(self, ): """Initialize the base ComputerCluster class.""" - #| - __init__ + # | - __init__ self.root_dir = os.getcwd() self.default_sub_params = self.default_submission_parameters() # self.username = self.__parse_username__() self.__parse_cluster_type__() - #__| + # __| def __parse_cluster_type__(self): """Parse for the current cluster system.""" - #| - __parse_cluster_type__ + # | - __parse_cluster_type__ clusters_dict = { "aws": "AWSCluster", "slac": "SLACCluster", @@ -164,11 +164,11 @@ def __parse_cluster_type__(self): jobs_dir = None self.jobs_list_dir = jobs_dir - #__| + # __| def default_submission_parameters(self): """Global submission parameters for cluster jobs.""" - #| - default_submission_parameters + # | - default_submission_parameters def_params = { "path_i": ".", "job_name": "Default", @@ -178,7 +178,7 @@ def default_submission_parameters(self): } return(def_params) - #__| + # __| def is_job_submitted(self, path_i="."): """Check if job has been submitted. @@ -189,7 +189,7 @@ def is_job_submitted(self, path_i="."): Args: path """ - #| - add_jobs_queue_data + # | - add_jobs_queue_data root_dir = os.getcwd() os.chdir(path_i) if os.path.isfile(".SUBMITTED"): @@ -201,7 +201,7 @@ def is_job_submitted(self, path_i="."): submitted = False return(submitted) - #__| + # __| def job_state(self, path_i="."): """ @@ -209,7 +209,7 @@ def job_state(self, path_i="."): Args: path_i: """ - #| - job_state + # | - job_state job_state = self.cluster.job_state(path_i=path_i) if job_state is None: @@ -227,7 +227,7 @@ def job_state(self, path_i="."): pass return(job_state) - #__| + # __| def job_info_batch(self, path_i="."): """ @@ -235,7 +235,7 @@ def job_info_batch(self, path_i="."): Args: path_i: """ - #| - job_info_batch + # | - job_info_batch data_dict = self.cluster.job_info_batch(path_i=path_i) @@ -253,7 +253,7 @@ def job_info_batch(self, path_i="."): return(data_dict) - #__| + # __| def submit_job(self, **kwargs): """Call cluster specific job submission method. @@ -264,10 +264,10 @@ def submit_job(self, **kwargs): Args: **kwargs: """ - #| - submit_job + # | - submit_job kwargs = merge_two_dicts(self.default_sub_params, kwargs) - #| - Checking if job has already been submitted + # | - Checking if job has already been submitted if "path_i" in kwargs: path_i = kwargs["path_i"] else: @@ -275,28 +275,28 @@ def submit_job(self, **kwargs): if self.is_job_submitted(path_i=path_i): return(None) - #__| + # __| - #| - Writing Job Submission Parameters + # | - Writing Job Submission Parameters sub_params_name = ".submission_params.json" with open(os.path.join(path_i, sub_params_name), "w") as fle: json.dump(kwargs, fle, indent=2, skipkeys=True) - #__| + # __| self.cluster.submit_job_clust(**kwargs) - #| - Writing Cluster System Info to File + # | - Writing Cluster System Info to File if "path_i" in kwargs: path_i = kwargs["path_i"] with open(os.path.join(path_i, ".cluster_sys"), "w") as fle: # fle.write(self.cluster_sys) fle.write(self.cluster_sys + "\n") - #__| + # __| - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** ############################################################################### @@ -306,14 +306,14 @@ class EdisonCluster(ComputerCluster): DEPRECATED """ - #| - EdisonCluster ******************************************************** + # | - EdisonCluster ******************************************************** def __init__(self, root_dir="."): """Initialize Sherlock cluster instance. Args: root_dir: """ - #| - __init__ + # | - __init__ print("THIS MODULE IS DEPRECATED") print("USE THE ONE IN COMPUTE_ENVS") print("THIS MODULE IS DEPRECATED") @@ -359,11 +359,11 @@ def __init__(self, root_dir="."): # self.aws_dir = os.environ["aws_sc"] # self.job_queue_dir = self.aws_dir + "/jobs_bin" # self.job_queue_state_key = "job_status" - #__| + # __| def default_submission_parameters(self): """Defaul SLURM parameters for Sherlock cluster.""" - #| - default_submission_parameters + # | - default_submission_parameters def_params = { "queue": "regular", # -p flag | regular, debug @@ -393,7 +393,7 @@ def default_submission_parameters(self): } return(def_params) - #__| + # __| def submit_job_clust(self, **kwargs): """Submit job to sherlck. @@ -401,10 +401,10 @@ def submit_job_clust(self, **kwargs): Args: **kwargs: """ - #| - submit_job + # | - submit_job time.sleep(1.5) - #| - Merging Submission Parameters + # | - Merging Submission Parameters params = merge_two_dicts(self.default_sub_params, kwargs) path = params["path_i"] @@ -416,9 +416,9 @@ def submit_job_clust(self, **kwargs): if params["queue"] == "debug": params["priority"] = "debug" - #__| + # __| - #| - Submit Job + # | - Submit Job os.chdir(path) if params["job_name"] == "Default": @@ -427,9 +427,9 @@ def submit_job_clust(self, **kwargs): print("submitting job") os.system("chmod 777 *") # bash_command = "/u/if/flores12/bin/qv model.py" - #__| **** TEMP + # __| **** TEMP - #| - Create vasp_run script + # | - Create vasp_run script os.system("cd $SLURM_SUBMIT_DIR") os.system("export TMPDIR=$SLURM_SUBMIT_DIR") os.system("export VASP_SCRIPT=./run_vasp.py") @@ -442,9 +442,9 @@ def submit_job_clust(self, **kwargs): line_2 = 'echo ' + '"' + exitcode_line + '" >> run_vasp.py' os.system(line_2) # on edison - #__| + # __| - #| - Bash Submisssion Command + # | - Bash Submisssion Command bash_command = "/usr/bin/sbatch " # The -q flag is being used in place of the -p flag @@ -465,7 +465,7 @@ def submit_job_clust(self, **kwargs): print("Bash Submission Command:") print(bash_command) - #__| + # __| try: output = subprocess.Popen( @@ -482,7 +482,7 @@ def submit_job_clust(self, **kwargs): print("JOB SKIPPED: ") return(None) - #| - Parsing Output + # | - Parsing Output # out, err = pickle.load(open("job_sub_output.pickle", "r")) try: @@ -517,9 +517,9 @@ def submit_job_clust(self, **kwargs): # jobid = jobid # else: # jobid = None - #__| + # __| - #| - Writing Files + # | - Writing Files with open(".SUBMITTED", "w") as fle: fle.write("\n") @@ -536,11 +536,11 @@ def submit_job_clust(self, **kwargs): else: with open(".sub_out", "w") as fle: fle.write(out_copy) - #__| + # __| os.chdir(self.root_dir) - #| - Save subprocess output for analysis + # | - Save subprocess output for analysis # import pickle # # pickle.dump( @@ -548,15 +548,15 @@ def submit_job_clust(self, **kwargs): # open("job_sub_output.pickle", "wb"), # ) # return(output) - #__| + # __| # return(out, jobid) - #__| + # __| def job_state_dict(self): """ """ - #| - job_state_dict + # | - job_state_dict job_state_dict = { "PD": "PENDING", "R": "RUNNING", @@ -570,12 +570,12 @@ def job_state_dict(self): } return(job_state_dict) - #__| + # __| def __queue_types__(self): """Queue types for Edison cluster """ - #| - __queue_types__ + # | - __queue_types__ queue_list = [ "regular", "debug", @@ -583,12 +583,12 @@ def __queue_types__(self): ] return(queue_list) - #__| + # __| def job_info_batch(self, job_id, path_i=None): """ """ - #| - job_info_batch + # | - job_info_batch data_dict = slurm_squeue_parse( job_id, path_i=path_i, @@ -598,7 +598,7 @@ def job_info_batch(self, job_id, path_i=None): return(data_dict) - #| - __old__ + # | - __old__ # bash_comm = "squeue -j " + str(job_id) # # try: @@ -675,9 +675,9 @@ def job_info_batch(self, job_id, path_i=None): # print(data_dict) # # return(data_dict) - #__| + # __| - #__| + # __| def completed_file(self, path_i="."): """Check whether ".FINISHED" file exists. @@ -687,13 +687,13 @@ def completed_file(self, path_i="."): Args: path_i: """ - #| - completed_file + # | - completed_file completed_fle = False if os.path.exists(path_i + "/.FINISHED"): completed_fle = True return(completed_fle) - #__| + # __| def job_state(self, path_i="."): """Return job state of path_i --> job_i. @@ -701,7 +701,7 @@ def job_state(self, path_i="."): Args: path_i """ - #| - job_state + # | - job_state job_id = self.get_jobid(path_i=path_i) # print("compute_env job_state job_id:") @@ -721,14 +721,14 @@ def job_state(self, path_i="."): job_state_out = job_info[key] job_state_out = self.job_state_keys[job_state_out] - #| - Checking for "completed" file indicating success + # | - Checking for "completed" file indicating success completed_fle = self.completed_file(path_i=path_i) if completed_fle: job_state_out = self.job_state_keys["SUCCEEDED"] - #__| + # __| return(job_state_out) - #__| + # __| def get_jobid(self, path_i="."): """Return job ID of job_i. @@ -736,7 +736,7 @@ def get_jobid(self, path_i="."): Args: path_i: """ - #| - get_jobid + # | - get_jobid fileid_path = path_i + "/.jobid" if os.path.isfile(fileid_path): with open(path_i + "/.jobid") as fle: @@ -745,21 +745,21 @@ def get_jobid(self, path_i="."): jobid = None return(jobid) - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** class SLACCluster(ComputerCluster): """SLAC computing cluster.""" - #| - SLACCluster ********************************************************** + # | - SLACCluster ********************************************************** def __init__(self, root_dir=".", ): """ """ - #| - __init__ + # | - __init__ self.root_dir = root_dir self.default_sub_params = self.default_submission_parameters() self.job_data_dir = "" @@ -769,12 +769,12 @@ def __init__(self, self.job_state_keys = self.job_state_dict() self.job_queue_state_key = "STAT" - #__| + # __| def job_state_dict(self): """ """ - #| - job_state_dict + # | - job_state_dict job_state_dict = { # "PENDING": "PEND", # "FINISHED": "DONE", @@ -788,12 +788,12 @@ def job_state_dict(self): } return(job_state_dict) - #__| + # __| def __queue_types__(self): """Queue types for SLAC cluster """ - #| - __queue_types__ + # | - __queue_types__ queue_list = [ "suncat-test", "suncat", @@ -805,12 +805,12 @@ def __queue_types__(self): ] return(queue_list) - #__| + # __| def default_submission_parameters(self): """ """ - #| - default_submission_parameters + # | - default_submission_parameters def_params = { "queue": "suncat", "cpus": "10", @@ -823,7 +823,7 @@ def default_submission_parameters(self): } return(def_params) - #__| + # __| def submit_job_clust(self, **kwargs): """FIX This should update data table @@ -833,20 +833,20 @@ def submit_job_clust(self, **kwargs): Submits job to aws cluster. Copies PythonModules folder into job directory """ - #| - submit_job + # | - submit_job - #| - Merging Submission Parameters + # | - Merging Submission Parameters params = merge_two_dicts(self.default_sub_params, kwargs) path = params["path_i"] - #__| + # __| - #| - Checking if job has already been submitted + # | - Checking if job has already been submitted # if self.is_job_submitted(): # return(None) - #__| + # __| - #| - Submit Job ******************************************************* + # | - Submit Job ******************************************************* os.chdir(path) @@ -879,7 +879,7 @@ def submit_job_clust(self, **kwargs): bash_command += "-J " + params["job_name"] + " " bash_command += params["job_script"] - #| - FIXME Python 2 --> 3 + # | - FIXME Python 2 --> 3 print("Python version info") print(sys.version_info) if (sys.version_info > (3, 0)): @@ -915,7 +915,7 @@ def submit_job_clust(self, **kwargs): print("JOB SKIPPED: ") return(None) - #| - __old + # | - __old # try: # output = subprocess.Popen( # bash_command, @@ -933,13 +933,13 @@ def submit_job_clust(self, **kwargs): # print("JOB SKIPPED: ") # return(None) - #__| + # __| - #__| + # __| - #__| + # __| - #| - Parsing Output + # | - Parsing Output # out = output.communicate()[0] out = output.communicate()[0].decode() ind = out.find("Job <") @@ -954,9 +954,9 @@ def submit_job_clust(self, **kwargs): jobid = int(jobid) else: jobid = None - #__| + # __| - #| - Writing Files + # | - Writing Files with open(".SUBMITTED", "w") as fle: fle.write("\n") @@ -968,17 +968,17 @@ def submit_job_clust(self, **kwargs): with open(".sub_out", "w") as fle: fle.write(out) - #__| + # __| os.chdir(self.root_dir) return(out, jobid) - #__| + # __| def get_jobid(self, path_i="."): """ """ - #| - get_jobid + # | - get_jobid # path_i = "." if os.path.isfile(path_i + "/.jobid"): @@ -991,18 +991,18 @@ def get_jobid(self, path_i="."): jobid = None return(jobid) - #__| + # __| def job_info_batch(self, path_i="."): """ """ - #| - job_info_batch + # | - job_info_batch job_id = self.get_jobid(path_i) - #| - If No Job ID File Return None + # | - If No Job ID File Return None if job_id is None: return(None) - #__| + # __| bash_comm = "/usr/local/bin/bjobs -w" + " " + job_id out = subprocess.check_output( @@ -1011,13 +1011,13 @@ def job_info_batch(self, path_i="."): stderr=subprocess.STDOUT, ).decode() - #| - Checking if Job Id Still in Batch System + # | - Checking if Job Id Still in Batch System if "is not found" in out: print("Job ID no longer in batch system, or ID is wrong") return(None) - #__| + # __| - #| - Parsing bsub Output into Dict + # | - Parsing bsub Output into Dict out = out.split() headers = out[0:8] data = out[8:] @@ -1036,18 +1036,18 @@ def job_info_batch(self, path_i="."): time = data[7:] time = "_".join(time) data_dict["SUBMIT_TIME"] = time - #__| + # __| - #| - bjob Command to Get Job Path From Job ID + # | - bjob Command to Get Job Path From Job ID bash_comm_2 = "/usr/local/bin/bjobs -o" + " 'exec_cwd' " + job_id out2 = subprocess.check_output(bash_comm_2, shell=True) out2 = out2.split() data_dict["EXEC_CWD"] = out2[1] - #__| + # __| # self.write_job_queue_state_file(path=path_i) - #| - write_job_queue_state_file + # | - write_job_queue_state_file key = self.job_queue_state_key with open(path_i + "/.QUEUESTATE", "w") as fle: @@ -1062,10 +1062,10 @@ def job_info_batch(self, path_i="."): fle.write("\n") - #__| + # __| return(data_dict) - #__| + # __| def job_state(self, path_i="."): """Query job state. @@ -1075,7 +1075,7 @@ def job_state(self, path_i="."): Args: path_i """ - #| - job_state + # | - job_state job_info = self.job_info_batch(path_i=path_i) if job_info is not None: @@ -1091,22 +1091,22 @@ def job_state(self, path_i="."): job_state_out = None return(job_state_out) - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** class SherlockCluster(ComputerCluster): """Sherlock computing cluster.""" - #| - SherlockCluster ****************************************************** + # | - SherlockCluster ****************************************************** def __init__(self, root_dir="."): """Initialize Sherlock cluster instance. Args: root_dir: """ - #| - __init__ + # | - __init__ # self.job_queue_dir = "/u/if/flores12/usr/bin" self.job_data_dir = "" @@ -1132,13 +1132,13 @@ def __init__(self, root_dir="."): # self.job_queue_dir = self.aws_dir + "/jobs_bin" # self.job_queue_state_key = "job_status" - #__| + # __| def __parse_username__(self): """ """ - #| - __parse_username__ + # | - __parse_username__ username = os.environ.get("USER") cond_1 = False @@ -1150,13 +1150,13 @@ def __parse_username__(self): cond_2 = True return(username) - #__| + # __| def __make_run_vasp_script__(self, params): """ """ - #| - __make_run_vasp_script__ + # | - __make_run_vasp_script__ os.system("echo import os > run_vasp.py") exitcode_line = "exitcode = os.system('srun -n" + " " + \ str(int(self.cores_per_node * int(params["nodes"]))) + " " + \ @@ -1174,11 +1174,11 @@ def __make_run_vasp_script__(self, params): "run_vasp.py", os.path.join(params["path_i"], "run_vasp.py"), ) - #__| + # __| def default_submission_parameters(self): """Defaul SLURM parameters for Sherlock cluster.""" - #| - default_submission_parameters + # | - default_submission_parameters def_params = { "queue": "owners,iric,normal", # -p flag @@ -1196,7 +1196,7 @@ def default_submission_parameters(self): } return(def_params) - #__| + # __| def submit_job_clust(self, **kwargs): """Submits job to sherlck. @@ -1204,19 +1204,19 @@ def submit_job_clust(self, **kwargs): Args: **kwargs: """ - #| - submit_job + # | - submit_job time.sleep(1.5) - #| - Merging Submission Parameters + # | - Merging Submission Parameters params = merge_two_dicts(self.default_sub_params, kwargs) path = params["path_i"] - #__| + # __| self.__make_run_vasp_script__(params) - #| - Submit Job + # | - Submit Job os.chdir(path) if params["job_name"] == "Default": @@ -1225,10 +1225,10 @@ def submit_job_clust(self, **kwargs): print("submitting job") os.system("chmod 777 *") # bash_command = "/u/if/flores12/bin/qv model.py" - #__| **** TEMP + # __| **** TEMP - #| - Bash Submisssion Command + # | - Bash Submisssion Command bash_command = "/usr/bin/sbatch " bash_command += "-p " + str(params["queue"]) + " " @@ -1248,7 +1248,7 @@ def submit_job_clust(self, **kwargs): print("Bash Submission Command:") print(bash_command) - #__| + # __| try: @@ -1279,7 +1279,7 @@ def submit_job_clust(self, **kwargs): print("JOB SKIPPED: ") return(None) - #| - Parsing Output + # | - Parsing Output out = output.communicate()[0] out_copy = copy.deepcopy(out) @@ -1301,9 +1301,9 @@ def submit_job_clust(self, **kwargs): jobid = jobid else: jobid = None - #__| + # __| - #| - Writing Files + # | - Writing Files with open(".SUBMITTED", "w") as fle: fle.write("\n") @@ -1316,23 +1316,23 @@ def submit_job_clust(self, **kwargs): with open(".sub_out", "w") as fle: fle.write(out_copy) - #| - Writing Job Submission Parameters + # | - Writing Job Submission Parameters with open(".submission_params_2.json", "w") as fle: json.dump(params, fle, indent=2, skipkeys=True) - #__| + # __| - #__| + # __| os.chdir(self.root_dir) return(out, jobid) - #__| + # __| def job_state_dict(self): """ """ - #| - job_state_dict + # | - job_state_dict job_state_dict = { "PD": "PENDING", "R": "RUNNING", @@ -1346,19 +1346,19 @@ def job_state_dict(self): } return(job_state_dict) - #__| + # __| def __queue_types__(self): """Queue types for SLAC cluster """ - #| - __queue_types__ + # | - __queue_types__ queue_list = [ "owners", "iric", ] return(queue_list) - #__| + # __| def get_jobid(self, path_i="."): """Return the job id. @@ -1366,7 +1366,7 @@ def get_jobid(self, path_i="."): Args: path_i: """ - #| - get_jobid + # | - get_jobid # # path_i = "." # fileid_path = path_i + "/.jobid" # # print(fileid_path) @@ -1377,12 +1377,12 @@ def get_jobid(self, path_i="."): # jobid=None # # return(jobid) - #__| + # __| def job_info_batch(self, job_id, path_i=None): """ """ - #| - job_info_batch + # | - job_info_batch data_dict = slurm_squeue_parse( job_id, path_i=path_i, @@ -1392,7 +1392,7 @@ def job_info_batch(self, job_id, path_i=None): return(data_dict) - #| - __old__ + # | - __old__ # bash_comm = "squeue -j " + str(job_id) # # try: @@ -1429,9 +1429,9 @@ def job_info_batch(self, job_id, path_i=None): # # # return(data_dict) - #__| + # __| - #__| + # __| def completed_file(self, path_i="."): """ @@ -1442,13 +1442,13 @@ def completed_file(self, path_i="."): Args: path_i: """ - #| - completed_file + # | - completed_file completed_fle = False if os.path.exists(path_i + "/.FINISHED"): completed_fle = True return(completed_fle) - #__| + # __| def job_state(self, path_i="."): """ @@ -1457,7 +1457,7 @@ def job_state(self, path_i="."): Args: path_i """ - #| - job_state + # | - job_state job_id = self.get_jobid(path_i=path_i) job_state_out = None @@ -1470,15 +1470,15 @@ def job_state(self, path_i="."): job_state_out = job_info[key] job_state_out = self.job_state_keys[job_state_out] - #| - Checking for "completed" file indicating success + # | - Checking for "completed" file indicating success completed_fle = self.completed_file(path_i=path_i) if completed_fle: job_state_out = self.job_state_keys["SUCCEEDED"] - #__| + # __| return(job_state_out) - #__| + # __| def get_jobid(self, path_i="."): """ @@ -1487,7 +1487,7 @@ def get_jobid(self, path_i="."): Args: path_i: """ - #| - get_jobid + # | - get_jobid fileid_path = path_i + "/.jobid" if os.path.isfile(fileid_path): with open(path_i + "/.jobid") as fle: @@ -1496,9 +1496,9 @@ def get_jobid(self, path_i="."): jobid = None return(jobid) - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** class AWSCluster(ComputerCluster): @@ -1508,19 +1508,19 @@ class AWSCluster(ComputerCluster): ex. $awsdir=/scratch/users/flores12/AWS/matr.io """ - #| - AWSCluster *********************************************************** + # | - AWSCluster *********************************************************** def __init__(self, root_dir=".", ): """ """ - #| - __init__ + # | - __init__ self.job_data_dir = "/simulation" self.root_dir = root_dir self.default_sub_params = self.default_submission_parameters() self.aws_dir = os.environ.get("awsdir", "") - #| - Create job_queue_dir + # | - Create job_queue_dir self.job_queue_dir = os.path.join( self.aws_dir, "jobs_bin") @@ -1528,19 +1528,19 @@ def __init__(self, directory = self.job_queue_dir if not os.path.exists(directory): os.makedirs(directory) - #__| + # __| self.job_state_keys = self.job_state_dict() self.queues = self.__queue_types__() self.job_queue_state_key = "job_status" self.error_file = "err" self.out_file = "out" - #__| + # __| def default_submission_parameters(self): """ """ - #| - default_submission_parameters + # | - default_submission_parameters def_params = { "queue": "medium", "cpus": "default", @@ -1554,7 +1554,7 @@ def default_submission_parameters(self): } return(def_params) - #__| + # __| def submit_job_clust(self, **kwargs): """Submit job to AWS cluster. @@ -1564,9 +1564,9 @@ def submit_job_clust(self, **kwargs): Args: kwargs """ - #| - submit_job_clust + # | - submit_job_clust - #| - Job Parameters + # | - Job Parameters params = merge_two_dicts(self.default_sub_params, kwargs) path = params["path_i"] @@ -1574,13 +1574,13 @@ def submit_job_clust(self, **kwargs): copy_PythonPackages = params["copy_PythonPackages"] cpus = params["cpus"] queue = params["queue"] - #__| + # __| root_dir = os.getcwd() if path is None: path = root_dir - #| - Checking if job has already been submitted + # | - Checking if job has already been submitted os.chdir(path) if os.path.isfile(".SUBMITTED"): print("Directory already submitted, will be skipped") @@ -1588,7 +1588,7 @@ def submit_job_clust(self, **kwargs): return(None) # <-------- SKIP JOB -------------------------------- else: os.chdir(root_dir) - #__| + # __| self.__copy_pyth_mods_packs_to_job_dir__( path, @@ -1596,7 +1596,7 @@ def submit_job_clust(self, **kwargs): copy_packs=copy_PythonPackages, ) - #| - Submit Job + # | - Submit Job # Args: path, root_dir, queue, cpus os.chdir(path) @@ -1613,13 +1613,13 @@ def submit_job_clust(self, **kwargs): bash_command = aws_dir + "/bin/trisub -q " + queue else: - #| - Checking that number of cpus is within allows + # | - Checking that number of cpus is within allows if queue == "medium": if cpus > 4: print("Medium queue can't have more than 4 cpus") print(" setting cpus to 4") cpus = 4 - #__| + # __| bash_command = aws_dir + "/bin/trisub -c " + str(cpus) + \ " -q " + queue @@ -1640,12 +1640,12 @@ def submit_job_clust(self, **kwargs): # os.chdir(root_dir) # print("JOB SKIPPED: ") # return(None) - #__| + # __| os.system("chmod 777 " + path + "/*") os.system("chmod 777 " + path) - #| - Parsing Submission for Job ID + # | - Parsing Submission for Job ID output = output.splitlines() print(output) # CHANGED @@ -1657,14 +1657,14 @@ def submit_job_clust(self, **kwargs): file = open(".jobid", "w") file.write(jobId + "\n") - #__| + # __| file = open(".SUBMITTED", "w") file.close() os.chdir(root_dir) - #| - Querying AWS For Job Info + # | - Querying AWS For Job Info job_queue_dict = self.job_info_batch(jobId) job_queue_dict["submit_time"] = sub_time @@ -1678,15 +1678,15 @@ def submit_job_clust(self, **kwargs): df = df_new df.to_csv(jobs_file_path, index=False) - #__| + # __| return job_queue_dict - #__| + # __| def job_state_dict(self): """ """ - #| - job_state_dict + # | - job_state_dict job_state_dict = { "PENDING": "PENDING", "SUCCEEDED": "SUCCEEDED", @@ -1702,12 +1702,12 @@ def job_state_dict(self): } return(job_state_dict) - #__| + # __| def __queue_types__(self): """Queue types for AWS cluster """ - #| - __queue_types__ + # | - __queue_types__ queue_list = [ "test", "small", @@ -1716,7 +1716,7 @@ def __queue_types__(self): ] return(queue_list) - #__| + # __| def __copy_pyth_mods_packs_to_job_dir__( self, @@ -1726,11 +1726,11 @@ def __copy_pyth_mods_packs_to_job_dir__( ): """ """ - #| - __copy_pyth_mods_packs_to_job_dir__ + # | - __copy_pyth_mods_packs_to_job_dir__ copy_PythonModules = copy_mods copy_PythonPackages = copy_packs - #| - Copy PYTHONMODULES to Job Directory + # | - Copy PYTHONMODULES to Job Directory if copy_PythonModules: if os.path.isdir(path_i + "/PythonModules") is True: print("PythonModules already exists, erasing and recopying") @@ -1742,9 +1742,9 @@ def __copy_pyth_mods_packs_to_job_dir__( # py_mod = os.environ["python_modules"] py_mod = os.environ["PYTHONMODULES"] shutil.copytree(py_mod, path_i + "/PythonModules") - #__| + # __| - #| - Copy Python Packages to Job Directory + # | - Copy Python Packages to Job Directory if copy_PythonPackages: if os.path.isdir(path_i + "/PythonPackages") is True: print("PythonPackages already exists, erasing and recopying") @@ -1754,14 +1754,14 @@ def __copy_pyth_mods_packs_to_job_dir__( else: py_pack = os.environ["PYTHONPACKAGES"] shutil.copytree(py_pack, path_i + "/PythonPackages") - #__| + # __| - #__| + # __| def get_jobid(self, path_i="."): """ """ - #| - get_jobid + # | - get_jobid # path_i = "." fileid_path = path_i + "/.jobid" if os.path.isfile(fileid_path): @@ -1771,12 +1771,12 @@ def get_jobid(self, path_i="."): jobid = None return(jobid) - #__| + # __| def job_state(self, path_i="."): """ """ - #| - job_state + # | - job_state job_id = self.get_jobid(path_i=path_i) job_state_out = None if job_id is not None: @@ -1788,17 +1788,17 @@ def job_state(self, path_i="."): job_state_out = job_info[key] job_state_out = self.job_state_keys[job_state_out] - #| - Checking for Spot Termination + # | - Checking for Spot Termination spot_term = self.spot_terminated(path_i=path_i) if spot_term: job_state_out = self.job_state_keys["FAILED"] - #__| + # __| - #| - Checking for "completed" file indicating success + # | - Checking for "completed" file indicating success completed_fle = self.completed_file(path_i=path_i) if completed_fle: job_state_out = self.job_state_keys["SUCCEEDED"] - #__| + # __| if os.path.isfile(path_i + "/.QUEUESTATE"): bash_comm = "chmod 777 " + path_i + "/.QUEUESTATE" @@ -1809,12 +1809,12 @@ def job_state(self, path_i="."): fle.write("\n") return(job_state_out) - #__| + # __| def spot_terminated(self, path_i="."): """ """ - #| - spot_terminated + # | - spot_terminated symlink_dir = path_i + "/simulation" spot_terminated = False @@ -1825,12 +1825,12 @@ def spot_terminated(self, path_i="."): print("Spot Termination") return(spot_terminated) - #__| + # __| def completed_file(self, path_i="."): """ """ - #| - completed_file + # | - completed_file symlink_dir = path_i + "/simulation" completed_fle = False @@ -1839,22 +1839,22 @@ def completed_file(self, path_i="."): completed_fle = True return(completed_fle) - #__| + # __| def job_info_batch(self, job_id): """ """ - #| - job_info_batch + # | - job_info_batch import boto3 batch = boto3.client("batch") job_descriptions = batch.describe_jobs(jobs=[job_id]) - #| - Checking if Job is in AWS Batch + # | - Checking if Job is in AWS Batch if len(job_descriptions["jobs"]) == 0: return("job not in batch system") else: job_info = job_descriptions["jobs"][0] - #__| + # __| job_status = job_info["status"] job_path = job_info["parameters"]["model"] @@ -1878,19 +1878,19 @@ def job_info_batch(self, job_id): "job_name": job_name} return(job_queue_dict) - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** class DummyCluster(ComputerCluster): """Placeholder class for when current cluster isn't supported.""" - #| - DummyCluster + # | - DummyCluster def __init__(self, root_dir="."): """ """ - #| - __init__ + # | - __init__ # self.job_queue_dir = "/u/if/flores12/usr/bin" self.job_data_dir = "" @@ -1911,7 +1911,7 @@ def __init__(self, root_dir="."): # self.job_queue_dir = self.aws_dir + "/jobs_bin" # self.job_queue_state_key = "job_status" - #__| + # __| def submit_job_clust(self, **kwargs): @@ -1920,15 +1920,15 @@ def submit_job_clust(self, **kwargs): Args: **kwargs: """ - #| - submit_job + # | - submit_job print("submit_job_clust | DummyCluster") print("Nothing happens!!") - #__| + # __| def job_state(self, path_i="."): """ """ - #| - job_state + # | - job_state return("TEMP") # job_info = self.job_info_batch(path_i=path_i) @@ -1940,7 +1940,7 @@ def job_state(self, path_i="."): # else: # job_state_out = None # return(job_state_out) - #__| + # __| - #__| + # __| diff --git a/dft_job_automat/job_analysis.py b/dft_job_automat/job_analysis.py index 0d1e211..fc2921c 100644 --- a/dft_job_automat/job_analysis.py +++ b/dft_job_automat/job_analysis.py @@ -9,7 +9,7 @@ TODO Delete the duplicate methods for job_status """ -#| - Import Modules +# | - Import Modules import os import sys @@ -28,7 +28,7 @@ # My Modules from dft_job_automat.job_setup import DFT_Jobs_Setup -#__| +# __| class DFT_Jobs_Analysis(DFT_Jobs_Setup): """Analysis methods for jobs in tree structure. @@ -39,11 +39,11 @@ class DFT_Jobs_Analysis(DFT_Jobs_Setup): Parent class to DFT_Jobs_Setup """ - #| - DFT_Jobs_Analysis **************************************************** + # | - DFT_Jobs_Analysis **************************************************** - #| - Class Variables + # | - Class Variables finished_fle = ".FINISHED.new" - #__| + # __| def __init__(self, tree_level=None, @@ -83,9 +83,9 @@ def __init__(self, Additional methods to run on each job dir and return value to populate data column with. """ - #| - __init__ + # | - __init__ - #| - Instantiate DFT_Jobs_Setup + # | - Instantiate DFT_Jobs_Setup DFT_Jobs_Setup.__init__(self, tree_level=tree_level, level_entries=level_entries, @@ -100,14 +100,14 @@ def __init__(self, parse_all_revisions=parse_all_revisions, ) - #__| + # __| - #| - Class Attributes + # | - Class Attributes self.dataframe_dir = dataframe_dir self.parallel_exec = parallel_exec - #__| + # __| - #| - General Methods + # | - General Methods if update_job_state is True: print("update_job_state == True") self.add_data_column( @@ -120,9 +120,9 @@ def __init__(self, column_name="N/A", parallel_exec=self.parallel_exec, ) - #__| + # __| - #| - Job Type Specific Methods + # | - Job Type Specific Methods # method = DFT_Methods().atom_type_num_dict # self.add_data_column(method, column_name="TEMP", allow_failure=False) @@ -134,7 +134,7 @@ def __init__(self, else: - #| - job_type_class instance attached methods + # | - job_type_class instance attached methods if job_type_class is not None: job_type_inst = job_type_class @@ -152,9 +152,9 @@ def __init__(self, ) write_data_frame = True - #__| + # __| - #| - methods_to_run + # | - methods_to_run if methods_to_run is not None: for method in methods_to_run: @@ -171,23 +171,23 @@ def __init__(self, ) write_data_frame = True - #__| + # __| if write_data_frame: self.__write_dataframe__() self.add_all_columns_from_file() - #__| + # __| - #__| + # __| - #| - Job Log ************************************************************** + # | - Job Log ************************************************************** def add_jobs_queue_data(self): """Add jobs queue data to jobs.csv file.""" - #| - add_jobs_queue_data + # | - add_jobs_queue_data # COMBAK Not finished - #__| + # __| def job_queue_info(self, path_i): """ @@ -198,7 +198,7 @@ def job_queue_info(self, path_i): Args: path_i: """ - #| - job_queue_info + # | - job_queue_info jobs_file_path = self.job_queue_dir + "/jobs.csv" df = pd.read_csv(jobs_file_path) @@ -211,13 +211,13 @@ def job_queue_info(self, path_i): job_info = df.iloc[index].to_dict() return(job_info) - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** def __load_dataframe__(self): """Attempt to load dataframe.""" - #| - __load_dataframe__ + # | - __load_dataframe__ if self.dataframe_dir is not None: fle_name = self.dataframe_dir + "/job_dataframe.pickle" else: @@ -231,7 +231,7 @@ def __load_dataframe__(self): df = pickle.load(fle) self.data_frame = df - #__| + # __| def __write_dataframe__(self): """ @@ -239,7 +239,7 @@ def __write_dataframe__(self): Writes dataframe in csv and pickle format. CSV is easily human readable """ - #| - __write_dataframe__ + # | - __write_dataframe__ df = self.data_frame # df.to_csv(self.root_dir + "/jobs_bin/job_dataframe.csv", index=False) @@ -253,7 +253,7 @@ def __write_dataframe__(self): with open(df_pickle_fle, "wb") as fle: pickle.dump(df, fle) - #__| + # __| def add_all_columns_from_file(self): """ @@ -264,7 +264,7 @@ def add_all_columns_from_file(self): Columns are separated by the "|" character File name must end with ".col" extension. """ - #| - add_all_columns_from_file + # | - add_all_columns_from_file # print("lksajkfls0sd7fsdfsd98") # print(self.root_dir + "/jobs_bin/data_columns") # print(self.working_dir) @@ -281,7 +281,7 @@ def add_all_columns_from_file(self): for col_file in col_data_file_list: self.__add_data_column_from_file__(col_file) - #__| + # __| def __add_data_column_from_file__(self, file_name, @@ -292,13 +292,13 @@ def __add_data_column_from_file__(self, Args: file_name: """ - #| - __add_data_column_from_file__ + # | - __add_data_column_from_file__ - #| - Extracting Column Name From File Name + # | - Extracting Column Name From File Name col_name = file_name.split(".")[0] - #__| + # __| - #| - Reading Column Data File - NEW + # | - Reading Column Data File - NEW # column_file = self.root_dir + "/jobs_bin/data_columns/" + file_name column_file = os.path.join( self.working_dir, @@ -319,9 +319,9 @@ def __add_data_column_from_file__(self, line_new["path"] = line[2].strip() content_new.append(line_new) - #__| + # __| - #| - Matching Dataframe with New Data Column + # | - Matching Dataframe with New Data Column df = self.data_frame df["full_path"] = df["path"].astype(str) + "_" + \ @@ -345,9 +345,9 @@ def __add_data_column_from_file__(self, df.drop("full_path", axis=1) df[col_name] = column_data_list - #__| + # __| - #__| + # __| @@ -394,7 +394,7 @@ def add_data_column(self, allow_failure: If True, a failed method call will result in NaN """ - #| - __add_data_coumn__ + # | - __add_data_coumn__ startTime = datetime.now() print("Adding " + str(column_name)) @@ -402,7 +402,7 @@ def add_data_column(self, def process_entry(entry): """ """ - #| - process_entry + # | - process_entry path = entry.full_path path = path + self.cluster.cluster.job_data_dir @@ -418,11 +418,11 @@ def process_entry(entry): # new_data_col.append(out) return(out) - #__| + # __| if parallel_exec: - #| - Parallized Execution ***************************************** + # | - Parallized Execution ***************************************** from joblib import Parallel, delayed import multiprocessing @@ -433,16 +433,16 @@ def process_entry(entry): new_data_col = Parallel(n_jobs=int(num_cores / 2))( delayed(process_entry)(i) for i in self.data_frame["Job"] ) - #__| ************************************************************** + # __| ************************************************************** else: - #| - Serial Execution ********************************************* + # | - Serial Execution ********************************************* new_data_col = [] for entry in self.data_frame["Job"]: out = process_entry(entry) new_data_col.append(out) - #__| ************************************************************** + # __| ************************************************************** data_type_list = [type(x) for x in new_data_col] @@ -469,7 +469,7 @@ def process_entry(entry): print("Run time ", str(datetime.now() - startTime)) print("__________________________________"); print("") - #__| + # __| @@ -498,7 +498,7 @@ def job_state_file(self, path_i="."): Args: path: """ - #| - job_state_file + # | - job_state_file file_path = path_i + "/.QUEUESTATE" if os.path.isfile(file_path): with open(file_path, "r") as fle: @@ -507,10 +507,10 @@ def job_state_file(self, path_i="."): job_state = None return(job_state) - #__| + # __| - #| - Data Frame Methods + # | - Data Frame Methods # COMBAK Move this to dataframe methods def create_data_sets(self, data_frame, free_variable): @@ -527,7 +527,7 @@ def create_data_sets(self, data_frame, free_variable): k-points and pw-cutoff where each set contains the full range of latt-const. """ - #| - create_data_sets + # | - create_data_sets # df = copy.deepcopy(self.data_frame) df = data_frame @@ -541,7 +541,7 @@ def create_data_sets(self, data_frame, free_variable): for index in indices: df_tmp = df_unique_params.ix[[index]] - #| - Data Labels + # | - Data Labels data_label_full = "" for column in df_tmp: col_i = df_tmp[column] @@ -554,7 +554,7 @@ def create_data_sets(self, data_frame, free_variable): data_label_full += data_label_i + " | " data_label_full = data_label_full[:-3] - #__| + # __| i1 = df.set_index(var_lst).index i2 = df_tmp.set_index(var_lst).index @@ -564,7 +564,7 @@ def create_data_sets(self, data_frame, free_variable): data_lst.append({"label": data_label_full, "data": df_i}) return(data_lst) - #__| + # __| def filter_early_revisions(self, dataframe): """Remove all entries (rows) which aren't the highest revision number. @@ -572,18 +572,18 @@ def filter_early_revisions(self, dataframe): Args: dataframe: """ - #| - filter_early_revisions + # | - filter_early_revisions max_rev = dataframe["revision_number"] == dataframe["max_revision"] data_series_maxrev = dataframe[max_rev] return(data_series_maxrev) - #__| + # __| - #__| + # __| - #| - Query Job Status ***************************************************** + # | - Query Job Status ***************************************************** - #| - __old__ + # | - __old__ def job_state(self, path_i): """ Return job state. @@ -593,12 +593,12 @@ def job_state(self, path_i): Args: path_i """ - #| - job_state + # | - job_state job_state = self.cluster.cluster.job_state(path_i=path_i) return(job_state) - #| - OLD + # | - OLD # if not os.path.isdir(path + "/simulation"): # return("no_sim_folder") # @@ -610,9 +610,9 @@ def job_state(self, path_i): # return("running") # else: # return("error") - #__| + # __| - #__| + # __| def job_state_2(self, path): """ @@ -625,18 +625,18 @@ def job_state_2(self, path): Args: path_i """ - #| - job_state_2 + # | - job_state_2 # - # #| - Formatting path Depending on Whether It is Full or Relative + # # | - Formatting path Depending on Whether It is Full or Relative # if self.root_dir in path: # ind = path.find(self.root_dir_short) # # path = path[ind:] # full_path = path[ind:] # else: # full_path = self.root_dir_short + "/" + path - # #__| + # # __| # - # #| - Finding job in jobs.csv file + # # | - Finding job in jobs.csv file # jobs_file_path = self.job_queue_dir + "/jobs.csv" # df = pd.read_csv(jobs_file_path) # @@ -645,25 +645,25 @@ def job_state_2(self, path): # index = df[df["job_path"] == full_path].index.tolist()[0] # except: # job_in_csv = False - # #__| + # # __| # # try: # - # #| - Attempting to read job_id from file + # # | - Attempting to read job_id from file # with open(path + "/job_id") as fle: # job_id = fle.read().rstrip() - # #__| + # # __| # # except: # - # #| - Attempting to read job_id from jobs.csv by matching paths + # # | - Attempting to read job_id from jobs.csv by matching paths # if job_in_csv: # job_id = df.iloc[index]["job_id"] - # #__| + # # __| # # job_queue_dict = AWS_Queues().job_info_batch(job_id) # - # #| - Handling Case Where Job ID Is Not In Batch System + # # | - Handling Case Where Job ID Is Not In Batch System # if job_queue_dict == "job not in batch system": # # if job_in_csv: @@ -675,25 +675,25 @@ def job_state_2(self, path): # job_status = fle.readline().rstrip() # # job_queue_dict = {"job_status": job_status} - # #__| + # # __| # # job_status = job_queue_dict["job_status"] # - # #| - Writing Job Status to File + # # | - Writing Job Status to File # with open(path + "/.STATUS", "w+") as fle: # fle.write(job_status + "\n") - # #__| + # # __| # - # #| - Writing Job Status to Master Jobs Queue File + # # | - Writing Job Status to Master Jobs Queue File # if job_in_csv: # df.at[index, "job_status"] = job_status # df.to_csv(jobs_file_path, index=False) - # #__| + # # __| # # return(job_status) - #__| + # __| - #__| + # __| def job_state_3(self, path_i): """ @@ -702,7 +702,7 @@ def job_state_3(self, path_i): Args: path_i """ - #| - job_state_3 + # | - job_state_3 out_dict = { "job_ready": self._job_ready(path_i), "job_pending": self._job_pending(path_i), @@ -713,9 +713,9 @@ def job_state_3(self, path_i): } return(out_dict) - #__| + # __| - #| - OLD Methods That Use job_i + # | - OLD Methods That Use job_i def job_ready(self, job_i, require_READY_tag=True): """ @@ -726,7 +726,7 @@ def job_ready(self, job_i, require_READY_tag=True): require_READY_tag: Require a ".READY" file start job """ - #| - job_ready + # | - job_ready path_i = self.var_lst_to_path( job_i, job_rev="Auto", @@ -743,11 +743,11 @@ def job_ready(self, job_i, require_READY_tag=True): if not os.path.isfile(path_i + "/.SUBMITTED"): crit_1 = True - #| - Having trouble with AWS .READY files not being copied over + # | - Having trouble with AWS .READY files not being copied over # if self.cluster.cluster_sys == "aws": # crit_ # - #__| + # __| crit_list = [crit_0, crit_1] @@ -755,7 +755,7 @@ def job_ready(self, job_i, require_READY_tag=True): return(True) else: return(False) - #__| + # __| def job_pending(self, job_i): """ @@ -764,7 +764,7 @@ def job_pending(self, job_i): Args: job_i: """ - #| - job_pending + # | - job_pending path_i = self.var_lst_to_path(job_i, job_rev="Auto", relative_path=False, @@ -800,7 +800,7 @@ def job_pending(self, job_i): # return(True) # else: # return(False) - #__| + # __| def job_running(self, job_i): """ @@ -809,7 +809,7 @@ def job_running(self, job_i): Args: job_i: """ - #| - job_running + # | - job_running path_i = self.var_lst_to_path(job_i, job_rev="Auto", relative_path=False, @@ -839,7 +839,7 @@ def job_running(self, job_i): return(True) else: return(False) - #__| + # __| def job_succeeded(self, job_i): """ @@ -848,7 +848,7 @@ def job_succeeded(self, job_i): Args: job_i: """ - #| - job_succeeded + # | - job_succeeded path_i = self.var_lst_to_path(job_i, job_rev="Auto", relative_path=False, @@ -872,13 +872,13 @@ def job_succeeded(self, job_i): if "job_completed" in lines: crit_2_1 = True - #| - DELETE THIS + # | - DELETE THIS # TEMP COMBAK FIXME Delete this after migration to new FINISHED file # format is done!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! fle_name = path_i + "/" + DFT_Jobs_Analysis.finished_fle if os.path.isfile(fle_name): crit_2_1 = True - #__| + # __| crit_2_2 = False @@ -896,7 +896,7 @@ def job_succeeded(self, job_i): return(True) else: return(False) - #__| + # __| def job_failed(self, job_i): """ @@ -905,7 +905,7 @@ def job_failed(self, job_i): Args: job_i: """ - #| - job_failed + # | - job_failed path_i = self.var_lst_to_path(job_i, job_rev="Auto", relative_path=False, @@ -925,12 +925,12 @@ def job_failed(self, job_i): if not os.path.isfile(path_i + "/." + DFT_Jobs_Analysis.finished_fle): crit_2 = True - #| - Parsing Error File for "Error" (Sherlock only for now) + # | - Parsing Error File for "Error" (Sherlock only for now) if self.cluster.cluster_sys == "sherlock": error = self.parse_job_error_file(path_i) if error: crit_0 = True - #__| + # __| crit_list = [crit_0, crit_1, crit_2] @@ -938,7 +938,7 @@ def job_failed(self, job_i): return(True) else: return(False) - #__| + # __| def job_submitted(self, path_i): """ @@ -947,7 +947,7 @@ def job_submitted(self, path_i): Args: path_i """ - #| - job_submitted + # | - job_submitted try: if os.path.isfile(path_i + "/.SUBMITTED"): return(True) @@ -955,11 +955,11 @@ def job_submitted(self, path_i): return(False) except: return(False) - #__| + # __| - #__| + # __| - #| - NEW Methods That Use path_i Instead of job_i (Create col in df!!) + # | - NEW Methods That Use path_i Instead of job_i (Create col in df!!) def _job_ready(self, path_i, require_READY_tag=True): """ Return whether job_i is in READY state. @@ -969,7 +969,7 @@ def _job_ready(self, path_i, require_READY_tag=True): require_READY_tag: Require a ".READY" file start job """ - #| - job_ready + # | - job_ready # path_i = self.var_lst_to_path(job_i, # ob_rev="Auto", # relative_path=False, @@ -985,10 +985,10 @@ def _job_ready(self, path_i, require_READY_tag=True): if not os.path.isfile(path_i + "/.SUBMITTED"): crit_1 = True - #| - Having trouble with AWS .READY files not being copied over + # | - Having trouble with AWS .READY files not being copied over # if self.cluster.cluster_sys == "aws": # crit_ - #__| + # __| crit_list = [crit_0, crit_1] @@ -996,7 +996,7 @@ def _job_ready(self, path_i, require_READY_tag=True): return(True) else: return(False) - #__| + # __| def _job_pending(self, path_i): """ @@ -1005,7 +1005,7 @@ def _job_pending(self, path_i): Args: path_i: """ - #| - job_pending + # | - job_pending # path_i = self.var_lst_to_path(job_i, job_rev="Auto", # relative_path=False) @@ -1032,7 +1032,7 @@ def _job_pending(self, path_i): return(True) else: return(False) - #__| + # __| def _job_running(self, path_i): """ @@ -1041,7 +1041,7 @@ def _job_running(self, path_i): Args: path_i: """ - #| - job_running + # | - job_running crit_0 = True if os.path.isfile(path_i + "/.READY"): crit_0 = True @@ -1065,7 +1065,7 @@ def _job_running(self, path_i): return(True) else: return(False) - #__| + # __| def _job_succeeded(self, path_i): """ @@ -1074,7 +1074,7 @@ def _job_succeeded(self, path_i): Args: path_i: """ - #| - job_succeeded + # | - job_succeeded crit_0 = True if os.path.isfile(path_i + "/.READY"): crit_0 = True @@ -1104,7 +1104,7 @@ def _job_succeeded(self, path_i): return(True) else: return(False) - #__| + # __| def _job_failed(self, path_i): """ @@ -1113,7 +1113,7 @@ def _job_failed(self, path_i): Args: path_i: """ - #| - job_failed + # | - job_failed crit_0 = False job_state = self.cluster.job_state(path_i=path_i) if job_state == "FAILED": @@ -1128,7 +1128,7 @@ def _job_failed(self, path_i): crit_2 = True - #| - Parsing Error File for "Error" (Sherlock only for now) + # | - Parsing Error File for "Error" (Sherlock only for now) if self.cluster.cluster_sys == "sherlock": error = self.parse_job_error_file(path_i) @@ -1138,7 +1138,7 @@ def _job_failed(self, path_i): # if error and self.cluster.cluster_sys == "sherlock": # # print(error) # crit_0 = True - #__| + # __| crit_list = [crit_0, crit_1, crit_2] @@ -1146,7 +1146,7 @@ def _job_failed(self, path_i): return(True) else: return(False) - #__| + # __| def _job_submitted(self, path_i): """ @@ -1155,7 +1155,7 @@ def _job_submitted(self, path_i): Args: path_i: """ - #| - job_submitted + # | - job_submitted try: if os.path.isfile(path_i + "/.SUBMITTED"): return(True) @@ -1163,9 +1163,9 @@ def _job_submitted(self, path_i): return(False) except: return(False) - #__| + # __| - #__| + # __| def parse_job_error_file(self, path_i): """ @@ -1176,7 +1176,7 @@ def parse_job_error_file(self, path_i): Args: path_i """ - #| - parse_job_error_file + # | - parse_job_error_file err_file = self.cluster.cluster.error_file err_file = os.path.join(path_i, err_file) @@ -1209,11 +1209,11 @@ def parse_job_error_file(self, path_i): error = False return(error) - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** -#__| ************************************************************************** +# __| ************************************************************************** def parse_job_dirs(dirs_to_parse): @@ -1222,25 +1222,25 @@ def parse_job_dirs(dirs_to_parse): Args: dirs_to_parse """ - #| - parse_job_dirs + # | - parse_job_dirs - #| - Import Modules + # | - Import Modules # import os # import sys # # import pickle as pickle - #__| + # __| - #| - Script Input + # | - Script Input # dirs_to_parse = [ # "01_surface_calcs", # "02_surface_coverage", # "03_OER_Calc", # "07_diff_coverages_term", # ] - #__| + # __| - #| - MASTER Loop + # | - MASTER Loop master_path_list = [] for dir_j in dirs_to_parse: # path_j = os.path.join(os.getcwd(), dir_j) @@ -1252,7 +1252,7 @@ def parse_job_dirs(dirs_to_parse): dir_root = os.path.join(*name_i.split("/")[0:-1]) dir_leaf = name_i.split("/")[-1] - #| - Test that terminal dir name has _1 format + # | - Test that terminal dir name has _1 format condition_0 = False if dir_leaf[0] == "_": condition_0 = True @@ -1265,7 +1265,7 @@ def parse_job_dirs(dirs_to_parse): condition_2 = False if numeric_part.isdigit(): condition_2 = True - #__| + # __| condition_3 = False if "__old__" not in name_i: @@ -1284,13 +1284,13 @@ def parse_job_dirs(dirs_to_parse): ]): master_path_list.append("/" + dir_root) - #__| + # __| master_path_list_unique = list(set(master_path_list)) # for i in master_path_list_unique: print(i) - #| - NEW | Check that paths contain job_params.json file + # | - NEW | Check that paths contain job_params.json file for path_i in master_path_list_unique: if "job_params.json" not in os.listdir(path_i): print("job_params not in: ", path_i) @@ -1300,7 +1300,7 @@ def parse_job_dirs(dirs_to_parse): files_i = os.listdir(path_i) for file_j in files_i: - #| - TEMP + # | - TEMP condition_list = [] # Make sure that 'run' is in the dir name (ex. 3-run) @@ -1323,26 +1323,26 @@ def parse_job_dirs(dirs_to_parse): else: condition_list.append(False) - #__| + # __| if all(condition_list): print("Found a #-run folder: ", path_i) - #__| + # __| return(master_path_list_unique) - #| - Saving List to Pickle + # | - Saving List to Pickle # with open("181016_jobs_dir_list.pickle", "w") as fle: # pickle.dump(master_path_list_unique ,fle) - #__| + # __| - #__| + # __| def compare_parsed_and_user_job_dirs(parsed_dirs, user_dirs): """ """ - #| - compare_parsed_and_user_job_dirs + # | - compare_parsed_and_user_job_dirs print(" ") print(" ") @@ -1357,17 +1357,17 @@ def compare_parsed_and_user_job_dirs(parsed_dirs, user_dirs): print("Dirs that are in the user list but not in the parsed list") print(set(user_dirs).difference(set(parsed_dirs))) - #__| + # __| -#| - __old__ +# | - __old__ - #| - __old__ + # | - __old__ - #| - Picking Revision Number(s) To Query + # | - Picking Revision Number(s) To Query # largest_rev = self.job_revision_number(entry) # # if revision == "auto": @@ -1381,7 +1381,7 @@ def compare_parsed_and_user_job_dirs(parsed_dirs, user_dirs): # rev = range(self.job_revision_number(entry) + 1) # else: # rev = [revision] - #__| + # __| # def add_data_column(self, # function, @@ -1414,7 +1414,7 @@ def compare_parsed_and_user_job_dirs(parsed_dirs, user_dirs): # allow_failure: # If True, a failed method call will result in NaN # """ - # #| - __add_data_coumn__ + # # | - __add_data_coumn__ # new_data_col = [] # job_rev_lst = [] # @@ -1428,7 +1428,7 @@ def compare_parsed_and_user_job_dirs(parsed_dirs, user_dirs): # job_rev="False", # ) # - # #| - Picking Revision Number(s) To Query + # # | - Picking Revision Number(s) To Query # largest_rev = self.job_revision_number(entry) # # if revision == "auto": @@ -1442,11 +1442,11 @@ def compare_parsed_and_user_job_dirs(parsed_dirs, user_dirs): # rev = range(self.job_revision_number(entry) + 1) # else: # rev = [revision] - # #__| + # # __| # # for rev_num in rev: # - # #| - Run Function + # # | - Run Function # path += "_" + str(rev_num) # # path = path + self.cluster.cluster.job_data_dir @@ -1461,7 +1461,7 @@ def compare_parsed_and_user_job_dirs(parsed_dirs, user_dirs): # # new_data_col.append(out) # job_rev_lst.append(rev_num) - # #__| + # # __| # # data_type_list = [type(x) for x in new_data_col] # dict_in_list = any(item == dict for item in data_type_list) @@ -1484,10 +1484,10 @@ def compare_parsed_and_user_job_dirs(parsed_dirs, user_dirs): # # else: # self.data_frame[column_name] = new_data_col - # #__| + # # __| # - #__| + # __| @@ -1500,7 +1500,7 @@ def view_atoms(self, ind): ind: Index of dataframe corresponding to entry of interest. """ - #| - view_atoms + # | - view_atoms df = self.data_frame path_i = df.iloc[ind]["path"] @@ -1514,7 +1514,7 @@ def view_atoms(self, ind): view(atoms) except: print("Couldn't read atoms object") - #__| + # __| # DEPR @@ -1525,13 +1525,13 @@ def job_revisions(self, path): Args: path: """ - #| - job_revisions + # | - job_revisions path = "/".join(path.split("/")[0:-1]) + "/" # Attempting to remove duplicate job folders (usually have spaces) dir_list = [x for x in os.walk(path).next()[1] if " " not in x] return(len(dir_list)) - #__| + # __| -#__| +# __| diff --git a/dft_job_automat/job_dependencies.py b/dft_job_automat/job_dependencies.py index b2d279f..d6deaf2 100644 --- a/dft_job_automat/job_dependencies.py +++ b/dft_job_automat/job_dependencies.py @@ -3,7 +3,7 @@ """Methods and code to handle jobs that depend on one another.""" -#| - Import Modules +# | - Import Modules import sys import os import shutil @@ -14,18 +14,18 @@ from dft_job_automat.job_analysis import DFT_Jobs_Analysis from dft_job_automat.job_manager import DFT_Jobs_Manager -#__| +# __| -#| - FUNCTIONS +# | - FUNCTIONS def full_path_i(root_dir, step_dir_names, step, path): """Formats relative path to full path for a given calculation step """ - #| - full_path_i + # | - full_path_i path_out = root_dir + "/" + step_dir_names[step - 1] + "/" + path return(path_out) - #__| + # __| def copyfiles_onestep_up( job_var_lst, @@ -36,11 +36,11 @@ def copyfiles_onestep_up( ): """ """ - #| - copyfiles_onestep_up + # | - copyfiles_onestep_up def copy_if_not_in_dest(source, dest_file): """ """ - #| - copy_if_not_in_dest + # | - copy_if_not_in_dest if not os.path.isfile(dest_file): shutil.copy(source, dest_file) @@ -50,7 +50,7 @@ def copy_if_not_in_dest(source, dest_file): else: pass - #__| + # __| curr_step = JobsInstances_lst[step - 1] @@ -66,7 +66,7 @@ def copy_if_not_in_dest(source, dest_file): ) next_var_lst = copy.deepcopy(next_step.job_var_lst) - #| - Finding jobs in step+1 whose properties match step's + # | - Finding jobs in step+1 whose properties match step's for curr_property in job_var_lst: prop_name = curr_property["property"] # force-cutoff prop_value = curr_property["value"] # 0.001 @@ -90,7 +90,7 @@ def copy_if_not_in_dest(source, dest_file): next_var_lst.remove(job) else: pass - #__| + # __| for next_job in next_var_lst: path_i = next_step.var_lst_to_path( @@ -101,7 +101,7 @@ def copy_if_not_in_dest(source, dest_file): for file_i in files_lst: - #| - Copy Files to Directory in New Step + # | - Copy Files to Directory in New Step if type(file_i) == str: curr_dir = dir_curr + \ curr_step.cluster.cluster.job_data_dir + "/" + file_i @@ -111,7 +111,7 @@ def copy_if_not_in_dest(source, dest_file): curr_dir = dir_curr + \ curr_step.cluster.cluster.job_data_dir + "/" + file_i[0] copy_if_not_in_dest(curr_dir, path_i + "/" + file_i[1]) - #__| + # __| if root_dir_files is not None: @@ -121,7 +121,7 @@ def copy_if_not_in_dest(source, dest_file): dest_dir = path_i for file_i in root_dir_files: - #| - Copy Files from Root Dir to New Job Folder + # | - Copy Files from Root Dir to New Job Folder if type(file_i) == str: copy_if_not_in_dest( source_dir + "/" + file_i, @@ -133,43 +133,43 @@ def copy_if_not_in_dest(source, dest_file): source_dir + "/" + file_i[0], dest_dir + "/" + file_i[1], ) - #__| + # __| open(dir_curr + "/.FILES_COPIED", "w") # file = open(dir_curr + "/.FILES_COPIED", "w") - #__| + # __| def create_atoms_list(atoms_name, file_ext, root_dir): """ """ - #| - create_atoms_list + # | - create_atoms_list atoms_dict = {} for atom in atoms_name: atoms_i = io.read(root_dir + "/dir_atoms/" + atom + file_ext) atoms_dict[atom] = atoms_i return(atoms_dict) - #__| + # __| def create_level_entries_dict(tree_level_labels, tree_level_values): """ """ - #| - create_level_entries_dict + # | - create_level_entries_dict level_entries_dict = {} for index, variable in enumerate(tree_level_labels): level_entries_dict[variable] = tree_level_values[index] return(level_entries_dict) - #__| + # __| -#| - __OLD__ +# | - __OLD__ def job_runnable(df, root_dir_beg, path_i): """ """ - #| - job_runnable + # | - job_runnable df["full_path"] = root_dir_beg + "/" + df["root_dir"] + "/" + df["path"] + \ "_" + df["job_revision_number"].astype(str) @@ -183,12 +183,12 @@ def job_runnable(df, root_dir_beg, path_i): return(True) else: return(False) - #__| + # __| def job_failed(df, root_dir_beg, path_i): """ """ - #| - job_failed + # | - job_failed df["full_path"] = root_dir_beg + "/" + df["root_dir"] + "/" + df["path"] + \ "_" + df["job_revision_number"].astype(str) @@ -215,11 +215,11 @@ def job_failed(df, root_dir_beg, path_i): else: return(False) - #__| + # __| -#__| +# __| -#__| +# __| class DFT_Jobs_Workflow: """Summary line. @@ -261,7 +261,7 @@ def __init__(self, root_dir: run_jobs: """ - #| - __init__ + # | - __init__ self.mod_dir = "dir_models" self.atoms_dir = "dir_atoms" @@ -310,19 +310,19 @@ def __init__(self, self.jobs_man_list = self.__create_jobs_man__() self.__job_maint__() - #__| + # __| def list_of_None_if_None(self, input): """Return list of 'None' of length == # of steps, if input is None """ - #| - list_of_None_if_None + # | - list_of_None_if_None if input is None: none_list = [None for i in range(self.num_steps)] return(none_list) else: return(input) - #__| + # __| def __set_cwd__(self, root_dir): """Set the working directory. @@ -330,19 +330,19 @@ def __set_cwd__(self, root_dir): Args: root_dir: """ - #| - __set_cwd__ + # | - __set_cwd__ if root_dir == ".": root_dir_out = os.getcwd() else: root_dir_out = root_dir return(root_dir_out) - #__| + # __| def __set_step_dir_names__(self): """ """ - #| - __set_step_dir_names__ + # | - __set_step_dir_names__ number_of_steps = self.num_steps step_dir_names = [] for step_i in range(number_of_steps): @@ -350,7 +350,7 @@ def __set_step_dir_names__(self): step_dir_names.append(step_dir_name_i) return(step_dir_names) - #__| + # __| def __set_model_names__(self, model_names): """Return list of model script names. @@ -358,7 +358,7 @@ def __set_model_names__(self, model_names): Args: model_names: """ - #| - __set_model_names__ + # | - __set_model_names__ # model_names = self.model_names if model_names is None: model_names_list = [] @@ -369,11 +369,11 @@ def __set_model_names__(self, model_names): model_names_list = model_names return(model_names_list) - #__| + # __| def __create_jobs_an__(self): """Create Jobs_Analysis instances for each step of workflow.""" - #| - __create_jobs_an__ + # | - __create_jobs_an__ # print("PREPARING EXTENDED FOLDER SYSTEM") #PERM_PRINT step_dir_names = self.step_dir_names master_root_dir = self.root_dir @@ -408,11 +408,11 @@ def __create_jobs_an__(self): Jobs_Inst_list.append(JobsAn) return(Jobs_Inst_list) - #__| + # __| def __create_jobs_man__(self): """Create Jobs_Manager instance(s).""" - #| - __create_jobs_man__ + # | - __create_jobs_man__ step_dir_names = self.step_dir_names master_root_dir = self.root_dir @@ -451,11 +451,11 @@ def __create_jobs_man__(self): Jobs_Inst_list.append(Jobs) return(Jobs_Inst_list) - #__| + # __| def __create_parent_dirs__(self): """Create parent folders.""" - #| - __create_parent_dirs__ + # | - __create_parent_dirs__ step_dir_names = self.step_dir_names master_root_dir = self.root_dir @@ -465,12 +465,12 @@ def __create_parent_dirs__(self): step_folder = master_root_dir + "/" + step_dir_names[step] if not os.path.isdir(step_folder): os.makedirs(step_folder) - #__| + # __| def __prep_dir_sys__(self): """ """ - #| - __prep_dir_sys + # | - __prep_dir_sys print("PREPARING EXTENDED FOLDER SYSTEM") # PERM_PRINT step_dir_names = self.step_dir_names master_root_dir = self.root_dir @@ -489,9 +489,9 @@ def __prep_dir_sys__(self): # if not os.path.isfile(files_placed_file): if True: - #| - Create Step Folder Structure + # | - Create Step Folder Structure JobsAn.create_dir_struct(create_first_rev_folder="True") - #__| + # __| for Job_i in JobsAn.Job_list: path_i = Job_i.full_path @@ -507,7 +507,7 @@ def __prep_dir_sys__(self): # relative_path=False, # ) # - # #| - Job_i Parameters + # # | - Job_i Parameters # job_i_params = {} # for variable in JobsAn.tree_level_labels: # job_i_var_j = JobsAn.extract_prop_from_var_lst( @@ -515,7 +515,7 @@ def __prep_dir_sys__(self): # variable, # ) # job_i_params[variable] = job_i_var_j - # #__| + # # __| # # self.setup_function(step, path_i, job_i_params, wf_vars) @@ -524,7 +524,7 @@ def __prep_dir_sys__(self): # file = open(master_root_dir + "/.FOLDERS_CREATED", "w") open(master_root_dir + "/.FOLDERS_CREATED", "w") - #__| + # __| def __job_maint__(self): """Manage jobs after being submitted. @@ -532,7 +532,7 @@ def __job_maint__(self): Tries to figure out what state the job is in and acts according to user defined methods """ - #| - __job_maint__ + # | - __job_maint__ step_dir_names = self.step_dir_names # master_root_dir = self.root_dir @@ -543,7 +543,7 @@ def __job_maint__(self): # df = Jobs.data_frame tally = {"successes": 0, "failures": 0, "running": 0, "pending": 0} - #| - PRINT + # | - PRINT print("") # PERM_PRINT print("###########################################################") @@ -554,9 +554,9 @@ def __job_maint__(self): print(str_i) print("###########################################################") print("Total Jobs: " + str(Jobs.num_jobs)) # PERM_PRINT - #__| + # __| - #| - LOOP OVER JOBS + # | - LOOP OVER JOBS if self.run_jobs: tally = { @@ -591,7 +591,7 @@ def __job_maint__(self): tally, ) - #| - __old__ + # | - __old__ # for job_i in Jobs.job_var_lst: # path_i = Jobs.var_lst_to_path( # job_i, @@ -599,7 +599,7 @@ def __job_maint__(self): # relative_path=False, # ) # - # #| - Job_i Parameters + # # | - Job_i Parameters # job_i_params = {} # for variable in Jobs.tree_level_labels: # job_i_param_j = Jobs.extract_prop_from_var_lst( @@ -608,7 +608,7 @@ def __job_maint__(self): # ) # # job_i_params[variable] = job_i_param_j - # #__| + # # __| # # tally = self.maint_function( # step, @@ -617,17 +617,17 @@ def __job_maint__(self): # wf_vars, # tally, # ) - #__| + # __| # TODO Check that tally is being incremented by 1 only print(tally) print("") - #__| + # __| - #__| + # __| -#| - Reinitiating the Jobs Instances (Is this needed) +# | - Reinitiating the Jobs Instances (Is this needed) # print("") #PERM_PRINT # print("Reinitiating the Jobs Instances") #PERM_PRINT # Jobs_Inst_list = [] @@ -644,4 +644,4 @@ def __job_maint__(self): # ) # # Jobs_Inst_list.append(Jobs) -#__| +# __| diff --git a/dft_job_automat/job_manager.py b/dft_job_automat/job_manager.py index 9cfea50..a7305b6 100644 --- a/dft_job_automat/job_manager.py +++ b/dft_job_automat/job_manager.py @@ -6,7 +6,7 @@ Author: Raul A. Flores """ -#| - Import Modules +# | - Import Modules import os import sys @@ -19,7 +19,7 @@ # My Modules from dft_job_automat.job_analysis import DFT_Jobs_Analysis # from aws.aws_class import AWS_Queues -#__| +# __| class DFT_Jobs_Manager(DFT_Jobs_Analysis): """ @@ -28,7 +28,7 @@ class DFT_Jobs_Manager(DFT_Jobs_Analysis): Manages job submission, resubmission, revision managmnet, job status, etc. """ - #| - DFT_Jobs_Manager ***************************************************** + # | - DFT_Jobs_Manager ***************************************************** def __init__(self, tree_level=None, level_entries=None, @@ -58,9 +58,9 @@ def __init__(self, TEMP TEMP """ - #| - __init__ + # | - __init__ - #| - __old__ + # | - __old__ # tree_level=None, # level_entries=None, # skip_dirs_lst=None, @@ -75,7 +75,7 @@ def __init__(self, # methods_to_run=methods_to_run, # folders_exist=folders_exist, # parse_all_revisions=parse_all_revisions, - #__| + # __| DFT_Jobs_Analysis.__init__(self, tree_level=tree_level, @@ -95,7 +95,7 @@ def __init__(self, parse_all_revisions=parse_all_revisions, ) - #| - __old__ + # | - __old__ # # system=system, # tree_level=tree_level, # level_entries=level_entries, @@ -105,9 +105,9 @@ def __init__(self, # dataframe_dir=None, # job_type_class=None, # folders_exist=None, - #__| + # __| - #__| + # __| def restart_job(self, job_i, @@ -129,7 +129,7 @@ def restart_job(self, file_list: job_i """ - #| - restart_job + # | - restart_job # FIXME I've defined this in many places. def copy_if_not_in_dest(source, dest_file): """ @@ -138,7 +138,7 @@ def copy_if_not_in_dest(source, dest_file): Args: dest_file: """ - #| - copy_if_not_in_dest + # | - copy_if_not_in_dest if not os.path.isfile(dest_file): shutil.copy(source, dest_file) @@ -147,7 +147,7 @@ def copy_if_not_in_dest(source, dest_file): print("File " + str(source_fle) + " copied") else: pass - #__| + # __| self.create_job_dir(job_i, revision="Auto") self.copy_files_from_last_revision( @@ -168,7 +168,7 @@ def copy_if_not_in_dest(source, dest_file): ) for file_i in file_list: - #| - Copy Files from Root Dir to New Job Folder + # | - Copy Files from Root Dir to New Job Folder if type(file_i) == str: copy_if_not_in_dest( source_dir + "/" + file_i, @@ -180,7 +180,7 @@ def copy_if_not_in_dest(source, dest_file): source_dir + "/" + file_i[0], dest_dir + "/" + file_i[1], ) - #__| + # __| path_i = self.var_lst_to_path( job_i, @@ -200,7 +200,7 @@ def copy_if_not_in_dest(source, dest_file): self.submit_job(**params_dict) else: pass - #__| + # __| def copy_files_from_last_revision(self, files_list, @@ -223,7 +223,7 @@ def copy_files_from_last_revision(self, If system is AWS, decidedes whether the previous jobs' files are obtained from the /simulation folder or the origial job dir. """ - #| - copy_files_from_last_revision + # | - copy_files_from_last_revision # COMBAK def copy_if_not_in_dest(source, dest_file): @@ -233,7 +233,7 @@ def copy_if_not_in_dest(source, dest_file): Args: dest_file: """ - #| - copy_if_not_in_dest + # | - copy_if_not_in_dest if not os.path.isfile(dest_file): shutil.copy(source, dest_file) @@ -242,7 +242,7 @@ def copy_if_not_in_dest(source, dest_file): print("File " + str(source_fle) + " copied") else: pass - #__| + # __| job_path = self.var_lst_to_path( job_i, @@ -268,7 +268,7 @@ def copy_if_not_in_dest(source, dest_file): for file_i in files_list: - #| - Copy Files to Directory in New Step + # | - Copy Files to Directory in New Step if from_simulation_folder is True: data_d = self.cluster.cluster.job_data_dir @@ -286,9 +286,9 @@ def copy_if_not_in_dest(source, dest_file): source_dir + data_d + "/" + file_i[0], dest_dir + "/" + file_i[1], ) - #__| + # __| - #__| + # __| def submit_job(self, **kwargs): """Submit job to appropriate cluster. @@ -296,11 +296,11 @@ def submit_job(self, **kwargs): Args: **kwargs: """ - #| - submit_job + # | - submit_job # path_i = kwargs["path_i"] self.cluster.submit_job(**kwargs) - #__| + # __| def remove_rev_folder(self, revision_number): """Remove revision job folder in all jobs directories. @@ -308,14 +308,14 @@ def remove_rev_folder(self, revision_number): Args: revision_number: """ - #| - remove_rev_folder + # | - remove_rev_folder print("Removing job revision folder " + str(revision_number)) for job in self.job_var_lst: path = self.var_lst_to_path(job) shutil.rmtree(path + "_" + str(revision_number)) - #__| + # __| def restart_job_2(self, prev_rev_file_list=[], root_dir_file_list=[]): """ @@ -325,10 +325,10 @@ def restart_job_2(self, prev_rev_file_list=[], root_dir_file_list=[]): prev_rev_file_list: root_dir_file_list: """ - #| - restart_job_2 + # | - restart_job_2 for index, row in self.data_frame.iterrows(): if row["job_state"] == "error": - #| - Body + # | - Body job = row["variable_list"] path = row["path"] rev_n = self.job_revision_number(job) @@ -369,10 +369,10 @@ def restart_job_2(self, prev_rev_file_list=[], root_dir_file_list=[]): os.system(bash_command) os.chdir(self.root_dir) - #__| + # __| else: continue - #__| + # __| def copy_files_jd(self, file_list, variable_lst, revision="Auto"): """Copy files to job directory. @@ -382,14 +382,14 @@ def copy_files_jd(self, file_list, variable_lst, revision="Auto"): variable_lst: revision: """ - #| - copy_files_jd + # | - copy_files_jd path = self.var_lst_to_path(variable_lst) path += "_" + str(self.job_revision_number(variable_lst)) for file in file_list: shutil.copyfile(self.root_dir + "/" + file, path + "/" + file) - #__| + # __| def create_job_dir(self, variable_lst, revision="Auto"): """ @@ -399,7 +399,7 @@ def create_job_dir(self, variable_lst, revision="Auto"): variable_lst: revision: """ - #| - create_job_dir + # | - create_job_dir path = self.var_lst_to_path( variable_lst, job_rev="False", @@ -421,7 +421,7 @@ def create_job_dir(self, variable_lst, revision="Auto"): if not os.path.exists(path): os.makedirs(path) - #__| + # __| # path variable not needed/used DELETE #NOTE def submit_jobs(self, path=None, queue="medium", copy_PythonModules=True): @@ -433,16 +433,16 @@ def submit_jobs(self, path=None, queue="medium", copy_PythonModules=True): 1. Copies the current model.py into the "model_version" folder 2. Goes through """ - #| - submit_jobs + # | - submit_jobs - #| - OLD + # | - OLD # def submit_folder_job(folder_dir, bash_command): # """ # """ - # #| - submit_folder_job + # # | - submit_folder_job # # - # #| - Submtting Job + # # | - Submtting Job # try: # os.chdir(folder_dir) # @@ -460,10 +460,10 @@ def submit_jobs(self, path=None, queue="medium", copy_PythonModules=True): # os.chdir(self.root_dir) # print("JOB SKIPPED: " + folder_dir) # return(None) - # #__| + # # __| # # - # #| - Parsing Submission for Job ID + # # | - Parsing Submission for Job ID # output = output.splitlines() # for line in output: # if "jobId" in line: @@ -476,7 +476,7 @@ def submit_jobs(self, path=None, queue="medium", copy_PythonModules=True): # lst = line.split('"') # job_name_ind = (lst.index("jobName") + 2) # jobName = lst[job_name_ind] - # #__| + # # __| # # # file = open(".submitted", "w") @@ -484,7 +484,7 @@ def submit_jobs(self, path=None, queue="medium", copy_PythonModules=True): # # os.chdir(self.root_dir) # - # #| - Querying AWS For Job Info + # # | - Querying AWS For Job Info # job_queue_dict = AWS_Queues(jobId).job_info_batch() # job_queue_dict["submit_time"] = sub_time # @@ -498,15 +498,15 @@ def submit_jobs(self, path=None, queue="medium", copy_PythonModules=True): # df = df_new # # df.to_csv(jobs_file_path, index=False) - # #__| + # # __| # # return job_queue_dict # - # #__| - #__| + # # __| + # __| - #| - Create Models Directoy + # | - Create Models Directoy models_dir = "model_versions" if not os.path.exists(models_dir): os.makedirs(models_dir) @@ -526,7 +526,7 @@ def num_of_files(dir): elif not filecmp.cmp("model.py", rev_file_path_prev): shutil.copyfile("model.py", rev_file_path) - #__| + # __| for job in self.job_var_lst: rev_num = self.job_revision_number(job) @@ -536,7 +536,7 @@ def num_of_files(dir): # AWS_Queues().submit_job(path=path_i, queue=queue) - #__| + # __| def cancel_jobs(self, state="RUNNABLE"): """ @@ -545,14 +545,14 @@ def cancel_jobs(self, state="RUNNABLE"): Args: state: """ - #| - cancel_jobs + # | - cancel_jobs jobs_file_path = self.job_queue_dir + "/jobs.csv" df = pd.read_csv(jobs_file_path) df_proj = df[df["job_path"].str.contains(self.root_dir_short)] return(df_proj) - #__| + # __| def update_jobs_queue_file(self): """ @@ -560,9 +560,9 @@ def update_jobs_queue_file(self): TMP """ - #| - update_jobs_queue_file + # | - update_jobs_queue_file tmp = 42 print(tmp) - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** diff --git a/dft_job_automat/job_setup.py b/dft_job_automat/job_setup.py index 1933989..307041b 100644 --- a/dft_job_automat/job_setup.py +++ b/dft_job_automat/job_setup.py @@ -6,7 +6,7 @@ Author: Raul A. Flores """ -#| - Import Modules +# | - Import Modules import os import shutil import copy @@ -22,7 +22,7 @@ # My Modules from dft_job_automat.compute_env import ComputerCluster -#__| +# __| class Job: @@ -31,7 +31,7 @@ class Job: Still a work in progress """ - #| - Job ****************************************************************** + # | - Job ****************************************************************** def __init__(self, path_i=None, job_params_dict=None, @@ -49,14 +49,14 @@ def __init__(self, max_revision: Max revisions for unique job, defined by the set of job params """ - #| - __init__ + # | - __init__ - #| - Setting class attributes + # | - Setting class attributes self.full_path = path_i self.job_params_dict = job_params_dict self.max_revision = max_revision self.root_dir = root_dir - #__| + # __| # if job_params_dict is None: @@ -70,12 +70,12 @@ def __init__(self, # self.__write_job_parameters__() self.revision_number = self.__revision_number__() - #__| + # __| def write_job_parameters(self): """ """ - #| - __write_job_parameters__ + # | - __write_job_parameters__ leaf_dir = self.full_path.split("/")[-1] if "_" in leaf_dir: @@ -86,17 +86,17 @@ def write_job_parameters(self): path_i = self.full_path[:ind_i - 1] - #| - NEW | Trying to remove keys which aren't JSON serializable + # | - NEW | Trying to remove keys which aren't JSON serializable def is_jsonable(x): """ """ - #| - is_jsonable + # | - is_jsonable try: json.dumps(x) return True except: return False - #__| + # __| job_params_dict_cpy = copy.deepcopy(self.job_params_dict) @@ -116,7 +116,7 @@ def is_jsonable(x): job_params_dict_cpy.pop(k, None) print(job_params_dict_cpy) - #__| + # __| file_path_i = os.path.join(path_i, "job_params.json") @@ -127,7 +127,7 @@ def is_jsonable(x): outfile, indent=2, ) - #__| + # __| def __set_job_parameters__(self, job_params_dict): """ @@ -135,7 +135,7 @@ def __set_job_parameters__(self, job_params_dict): Args: job_params_dict: """ - #| - __set_job_parameters__ + # | - __set_job_parameters__ job_params_from_file = self.__read_job_params_file__() if job_params_dict is not None: @@ -143,7 +143,7 @@ def __set_job_parameters__(self, job_params_dict): job_params_from_file.update(job_params_dict) return(job_params_from_file) - #__| + # __| def __read_job_params_file__(self): """Read "job_parameters.json" file from job direcory. @@ -153,7 +153,7 @@ def __read_job_params_file__(self): Args: """ - #| - __read_job_params_file__ + # | - __read_job_params_file__ job_params = {} # file_path = self.full_path + "/" + "job_parameters.json" @@ -198,19 +198,19 @@ def __read_job_params_file__(self): print(self.full_path) return(job_params) - #__| + # __| def __revision_number__(self): """ """ - #| - __revision_number__ + # | - __revision_number__ # print(self.full_path) revision_i = int(self.full_path.split("/")[-1].split("_")[-1]) return(revision_i) - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** class DFT_Jobs_Setup: @@ -219,7 +219,7 @@ class DFT_Jobs_Setup: Must be initialized with tree_level and level_entries inputs (Not really) """ - #| - DFT_Jobs_Setup ******************************************************* + # | - DFT_Jobs_Setup ******************************************************* def __init__(self, tree_level=None, @@ -245,9 +245,9 @@ def __init__(self, working_dir: folders_exist: """ - #| - __init__ + # | - __init__ - #| - Initializing Some Class Attributes + # | - Initializing Some Class Attributes self.order_dict = None self.job_var_lst = None self.Job_list = [] @@ -261,7 +261,7 @@ def __init__(self, self.indiv_job_dict_lst = indiv_job_dict_lst self.parse_all_revisions = parse_all_revisions - #__| + # __| self.root_dir = self.__set_root_dir__(root_dir) @@ -283,7 +283,7 @@ def __init__(self, # # self.data_frame = self.__generate_data_table__() self.check_inputs() - #__| + # __| def __job_i_param_dict_to_job_var_lst__(self, params_dict): """Constructs a job_variable list from a dictionary of parameters. @@ -291,7 +291,7 @@ def __job_i_param_dict_to_job_var_lst__(self, params_dict): Args: params_dict: """ - #| - __job_i_param_dict_to_job_var_lst__ + # | - __job_i_param_dict_to_job_var_lst__ assert self.tree_level_labels is not None job_var_lst_i = [] @@ -306,17 +306,17 @@ def __job_i_param_dict_to_job_var_lst__(self, params_dict): break return(job_var_lst_i) - #__| + # __| def write_job_params_json_file(self): """ """ - #| - write_job_params_json_file + # | - write_job_params_json_file for Job in self.Job_list: Job.write_job_parameters() - #__| + # __| def create_Jobs_from_dicts_and_paths(self, jobs_list, @@ -328,7 +328,7 @@ def create_Jobs_from_dicts_and_paths(self, List of dictionaries with 'properties' and 'path' keys """ - #| - create_Jobs_from_dicts_and_paths + # | - create_Jobs_from_dicts_and_paths for job_i in jobs_list: path_i = job_i["path"] @@ -350,13 +350,13 @@ def create_Jobs_from_dicts_and_paths(self, ) self.Job_list.append(Job_i) - #__| + # __| def __Job_list__(self): """Create Job list from various input sources.""" - #| - __Job_list__ + # | - __Job_list__ - #| - Adding Jobs From Individual Directory List + # | - Adding Jobs From Individual Directory List if self.indiv_dir_lst is not None: for job_i_dir in self.indiv_dir_lst: @@ -383,9 +383,9 @@ def __Job_list__(self): print("Didn't find any job dirs here:") print(job_i_dir) pass - #__| + # __| - #| - Adding Jobs From Enumerated Job Properties Tree + # | - Adding Jobs From Enumerated Job Properties Tree if self.job_var_lst is not None: for job_i in self.job_var_lst: job_var_dict = self.__job_i_vars_to_dict__(job_i) @@ -397,7 +397,7 @@ def __Job_list__(self): relative_path=False, ) - #| - __old__ + # | - __old__ # else: # print("else *s8fs*sdf") # path_i = os.path.join( @@ -414,7 +414,7 @@ def __Job_list__(self): # # "_1", # ) - #__| + # __| rev_dirs, max_rev = self.__revision_list_and_max__( # path_i @@ -433,9 +433,9 @@ def __Job_list__(self): ) self.Job_list.append(Job_i) - #__| + # __| - #| - TEMP | I don't remember why this is here + # | - TEMP | I don't remember why this is here indiv_job = self.indiv_job_lst is not None level_labels = self.tree_level_labels is not None if indiv_job and level_labels: @@ -459,16 +459,16 @@ def __Job_list__(self): ) self.Job_list.append(Job_i) - #__| + # __| if self.indiv_job_dict_lst is not None: self.create_Jobs_from_dicts_and_paths( self.indiv_job_dict_lst, ) - #__| + # __| - #| - Misc Methods + # | - Misc Methods def __job_i_vars_to_dict__(self, job_i_vars): """ @@ -476,7 +476,7 @@ def __job_i_vars_to_dict__(self, job_i_vars): Args: job_i_vars: """ - #| - __job_i_vars_to_dict__ + # | - __job_i_vars_to_dict__ job_vars_dict = {} for prop in job_i_vars: prop_key = prop["property"] @@ -485,11 +485,11 @@ def __job_i_vars_to_dict__(self, job_i_vars): job_vars_dict[prop_key] = prop_value return(job_vars_dict) - #__| + # __| def __create_jobs_bin__(self): """Create /jobs_bin folder if it doesn't exist.""" - #| - __create_jobs_bin__ + # | - __create_jobs_bin__ folder_dir = os.path.join(self.root_dir, self.working_dir, "jobs_bin") # folder_dir = self.root_dir + "/jobs_bin" @@ -497,7 +497,7 @@ def __create_jobs_bin__(self): # print("KDJFDI__") # print(folder_dir) os.makedirs(folder_dir) - #__| + # __| def __folders_exist__(self, folders_exist): """Check whether directory structure exists. @@ -505,14 +505,14 @@ def __folders_exist__(self, folders_exist): The alternative is to be creating an instance from a location where the original job files don't exist but the job dataframe does """ - #| - __folders_exist__ + # | - __folders_exist__ # User override if folders_exist is not None: return(folders_exist) folders_exist = False - #| - Folders Exist Criteria + # | - Folders Exist Criteria crit_0 = False if os.path.isfile(self.root_dir + "/jobs_bin/.folders_exist"): crit_0 = True @@ -520,9 +520,9 @@ def __folders_exist__(self, folders_exist): crit_1 = False if os.path.isdir(self.root_dir + "/data"): crit_1 = True - #__| + # __| - #| - Deciding whether folders exist or not + # | - Deciding whether folders exist or not if crit_0 is True: pass if crit_1 is True: @@ -531,38 +531,38 @@ def __folders_exist__(self, folders_exist): folders_exist = False else: folders_exist = False - #__| + # __| return(folders_exist) - #__| + # __| def __set_root_dir__(self, root_dir_in): """Returns root directory.""" - #| - __set_root_dir__ + # | - __set_root_dir__ if root_dir_in == ".": root_dir = os.getcwd() else: root_dir = root_dir_in return(root_dir) - #__| + # __| def __set_working_dir__(self, working_dir_in): """ """ - #| - __set_working_dir__ + # | - __set_working_dir__ if working_dir_in == ".": working_dir = "" else: working_dir = working_dir_in return(working_dir) - #__| + # __| def __check_input__(self): """Check that tree_level and level_entries are of matching length.""" - #| - __check_input__ + # | - __check_input__ tmp = set(self.tree_level_labels) input_diff = tmp.symmetric_difference(self.level_entries.keys()) if not input_diff == set(): @@ -575,7 +575,7 @@ def __check_input__(self): message += "The following properties need to be defined" + "\n" message += str(undefined_labels) raise ValueError(message) - #__| + # __| def __number_of_jobs__(self): @@ -588,7 +588,7 @@ def __number_of_jobs__(self): Depends on number of unique variable list and number of revisions for each job. """ - #| - __number_of_jobs__ + # | - __number_of_jobs__ num_jobs = 0 # Regular jobs @@ -601,7 +601,7 @@ def __number_of_jobs__(self): return(num_jobs) - #__| + # __| def new_var_lst_to_path(self, @@ -611,7 +611,7 @@ def new_var_lst_to_path(self, ): """ """ - #| - new_var_lst_to_path + # | - new_var_lst_to_path if isinstance(variable_lst, str): variable_lst = ast.literal_eval(variable_lst) else: @@ -636,7 +636,7 @@ def new_var_lst_to_path(self, index = "" beggining = index - #| - REPLACING PERIODS WITH "p" and NEGATIVE SIGNS WITH "n" + # | - REPLACING PERIODS WITH "p" and NEGATIVE SIGNS WITH "n" # if type(level["value"]) == type(1.23): if isinstance(level["value"], float): @@ -653,7 +653,7 @@ def new_var_lst_to_path(self, else: prop_value = str(level["value"]) - #__| + # __| dir_name += beggining + prop_value + "/" @@ -678,7 +678,7 @@ def new_var_lst_to_path(self, ) return(dir_name) - #__| + # __| def var_lst_to_path(self, variable_lst, @@ -694,7 +694,7 @@ def var_lst_to_path(self, False: Auto: """ - #| - var_lst_to_path + # | - var_lst_to_path if isinstance(variable_lst, str): variable_lst = ast.literal_eval(variable_lst) else: @@ -709,7 +709,7 @@ def var_lst_to_path(self, if index < 10: index = "0" + str(index) else: index = str(index) - #| - REPLACING PERIODS WITH "p" and NEGATIVE SIGNS WITH "n" + # | - REPLACING PERIODS WITH "p" and NEGATIVE SIGNS WITH "n" # if type(level["value"]) == type(1.23): if isinstance(level["value"], float): prop_value = str(level["value"]).replace(".", "p") @@ -719,7 +719,7 @@ def var_lst_to_path(self, else: prop_value = str(level["value"]) - #__| + # __| dir_name += index + self.sep + prop_value + "/" @@ -747,7 +747,7 @@ def var_lst_to_path(self, ) return(dir_name) - #__| + # __| def extract_prop_from_var_lst(self, variable_lst, property): """Extract the property from the variable list. @@ -756,18 +756,18 @@ def extract_prop_from_var_lst(self, variable_lst, property): variable_lst: property: """ - #| - extract_prop_from_var_lst + # | - extract_prop_from_var_lst # result = {} for i in variable_lst: if i["property"] == property: return i["value"] - #__| + # __| - #__| + # __| - #| - Job Variable Tree Methods + # | - Job Variable Tree Methods def load_dir_struct(self): """Attempt to load dir structure from file in root dir if none given. @@ -778,17 +778,17 @@ def load_dir_struct(self): order_dict is constructed """ - #| - load_dir_struct + # | - load_dir_struct if self.tree_level_labels is None and self.level_entries is None: self.__load_dir_structure_file__() # self.__check_input__() # TEMP had to comment out because of switching # to new format of input files - #| - If tree_level_labels and level_entries couldn't be parsed + # | - If tree_level_labels and level_entries couldn't be parsed if self.tree_level_labels is None and self.level_entries is None: return(None) - #__| + # __| # FIXME # if not type(self.level_entries) == list: @@ -796,7 +796,7 @@ def load_dir_struct(self): # level_entries_list = self.__level_entries_list__() if type(self.level_entries) == dict: - #| - OLD way + # | - OLD way self.order_dict = self.__order_dict__( self.tree_level_labels, self.level_entries) @@ -805,10 +805,10 @@ def load_dir_struct(self): # self.level_entries_list, level_entries_list, self.order_dict) - #__| + # __| elif type(self.level_entries) == list: - #| - New Way of Inputing Structure Files + # | - New Way of Inputing Structure Files tmp = self.__create_level_entries_dict__( self.tree_level_labels, self.level_entries, @@ -829,13 +829,13 @@ def load_dir_struct(self): # level_entries_list, self.order_dict, ) - #__| + # __| - #__| + # __| def __load_dir_structure_file__(self): """Attempt o load dir_structure.json from file.""" - #| - __load_dir_structure_file__ + # | - __load_dir_structure_file__ try: try: fle_name = self.root_dir + "/jobs_bin/dir_structure.json" @@ -859,7 +859,7 @@ def __load_dir_structure_file__(self): try: - #| - __old__ + # | - __old__ tmp = 42 # print("old - Reading dir_structure.json file \ # from root_dir") @@ -876,7 +876,7 @@ def __load_dir_structure_file__(self): # # self.tree_level_labels = tree_level # self.level_entries = level_entries - #__| + # __| except: print("Couldn't read /dir_structure.json") @@ -887,7 +887,7 @@ def __load_dir_structure_file__(self): mess = "Error opening 'dir_structure.json' file" raise IOError(mess) - #__| + # __| def __create_level_entries_dict__(self, tree_level_labels, @@ -900,20 +900,20 @@ def __create_level_entries_dict__(self, tree_level_labels: tree_level_values: """ - #| - create_level_entries_dict + # | - create_level_entries_dict level_entries_dict = {} for index, variable in enumerate(tree_level_labels): level_entries_dict[variable] = tree_level_values[index] return(level_entries_dict) - #__| + # __| def __level_entries_list__(self): """Construct level entries list. Construct level_entries_list from level_entries_dict and level_labels """ - #| - __level_entries_list__ + # | - __level_entries_list__ level_entries_dict = self.level_entries level_labels = self.tree_level_labels @@ -925,7 +925,7 @@ def __level_entries_list__(self): level_entries_list.append(params_list) return(level_entries_list) - #__| + # __| def __order_dict__(self, tree_level_labels, level_entries): """Order of properties to correspond to order of tree. @@ -939,7 +939,7 @@ def __order_dict__(self, tree_level_labels, level_entries): tree_level_labels: level_entries: """ - #| - __order_dict__ + # | - __order_dict__ order_dict = {} # <-------------------------------------- level_cnt = 0 @@ -950,7 +950,7 @@ def __order_dict__(self, tree_level_labels, level_entries): return order_dict - #__| + # __| def __job_variable_list__(self, level_entries, order_dict): """Return the job variable list. @@ -959,7 +959,7 @@ def __job_variable_list__(self, level_entries, order_dict): level_entries: order_dict: """ - #| - __job_variable_list__ + # | - __job_variable_list__ all_comb = itertools.product(*level_entries) job_dir_lst = [] @@ -980,12 +980,12 @@ def __job_variable_list__(self, level_entries, order_dict): job_dir_lst.remove(skip) return(job_dir_lst) - #__| + # __| - #__| + # __| - #| - Create Directory Tree + # | - Create Directory Tree # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -1002,10 +1002,10 @@ def create_dir_struct(self, create_first_rev_folder="True"): Args: create_first_rev_folder: """ - #| - create_dir_struct + # | - create_dir_struct for Job_i in self.Job_list: - #| - FOR LOOP BODY + # | - FOR LOOP BODY # if create_first_rev_folder == "True": # path = os.path.join(Job_i.full_path, "_1") # elif create_first_rev_folder == "False": @@ -1020,9 +1020,9 @@ def create_dir_struct(self, create_first_rev_folder="True"): elif not os.path.exists(path): os.makedirs(path) - #__| + # __| - #| - folders_exist attribute should be True from now on + # | - folders_exist attribute should be True from now on # file_name = self.root_dir + "/jobs_bin/.folders_exist" file_name = os.path.join( self.root_dir, @@ -1034,9 +1034,9 @@ def create_dir_struct(self, create_first_rev_folder="True"): fle.write("\n") self.folders_exist = self.__folders_exist__(True) - #__| + # __| - #__| + # __| def old_create_dir_struct(self, create_first_rev_folder="True"): """Create directory structure according to job variable list & dict. @@ -1047,7 +1047,7 @@ def old_create_dir_struct(self, create_first_rev_folder="True"): Args: create_first_rev_folder: """ - #| - create_dir_struct + # | - create_dir_struct for job in self.job_var_lst: if create_first_rev_folder == "True": path = self.var_lst_to_path(job) + "_1" @@ -1063,7 +1063,7 @@ def old_create_dir_struct(self, create_first_rev_folder="True"): elif not os.path.exists(path): os.makedirs(path) - #| - Creating Variable Text Files Through Directoy Structure + # | - Creating Variable Text Files Through Directoy Structure for job in self.job_var_lst: path = self.var_lst_to_path(job) path = self.root_dir + "/" + path @@ -1095,19 +1095,19 @@ def old_create_dir_struct(self, create_first_rev_folder="True"): # f = open(root + "/properties.txt", "w") # f.write(key + "\n") # f.close() - #__| + # __| # self.__create_dir_structure_file__() - #| - folders_exist attribute should be True from now on + # | - folders_exist attribute should be True from now on file_name = self.root_dir + "/jobs_bin/.folders_exist" with open(file_name, "w") as fle: fle.write("\n") self.folders_exist = self.__folders_exist__(True) - #__| + # __| - #__| + # __| @@ -1133,7 +1133,7 @@ def old_create_dir_struct(self, create_first_rev_folder="True"): def check_inputs(self): """ """ - #| - check_inputs + # | - check_inputs if self.tree_level_labels is not None: assert isinstance(self.tree_level_labels[0], np.ndarray) is False, \ "Please don't use numpy array types, can't be json serialized" @@ -1141,7 +1141,7 @@ def check_inputs(self): if self.level_entries_list is not None: assert isinstance(self.level_entries_list[0], np.ndarray) is False, \ "Please don't use numpy array types, can't be json serialized" - #__| + # __| def __create_dir_structure_file__(self): """ @@ -1150,7 +1150,7 @@ def __create_dir_structure_file__(self): Creates dir structure file from which the parameter list & dict can be loaded from. """ - #| - __create_dir_structure_file__ + # | - __create_dir_structure_file__ dir_structure_data = {} dir_structure_data["tree_level_labels"] = self.tree_level_labels @@ -1166,7 +1166,7 @@ def __create_dir_structure_file__(self): with open(fle_name, "w") as fle: json.dump(dir_structure_data, fle, indent=2) - #__| + # __| def __replace_p_for_per__(self, text): """Replace p in variable with "." character. @@ -1176,7 +1176,7 @@ def __replace_p_for_per__(self, text): Variables with "." character had them previously replaced with a "p" character to avoid periods in a folder name. """ - #| - __replace_p_for_per__ + # | - __replace_p_for_per__ lst = [pos for pos, char in enumerate(text) if char == "p"] # Replaces character at lett with a period if both the previous @@ -1196,7 +1196,7 @@ def __replace_p_for_per__(self, text): text = text[:lett] + "." + text[lett + 1:] return(text) - #__| + # __| def __replace_negative_for_n__(self, text): """Replace variable quantities that are negative with an "n". @@ -1204,7 +1204,7 @@ def __replace_negative_for_n__(self, text): Args: text: """ - #| - __replace_negative_for_n__ + # | - __replace_negative_for_n__ lst = [pos for pos, char in enumerate(text) if char == "n"] for lett in lst: @@ -1212,16 +1212,16 @@ def __replace_negative_for_n__(self, text): text = text[:lett] + "-" + text[lett + 1:] return(text) - #__| + # __| - #__| + # __| - #| - Job Attributes + # | - Job Attributes def __load_jobs_attributes__(self): """Load jobs attributes data from file.""" - #| - __load_jobs_attributes__ + # | - __load_jobs_attributes__ job_att_file = self.root_dir + "/jobs_bin/job_attributes.csv" if os.path.exists(job_att_file): @@ -1232,7 +1232,7 @@ def __load_jobs_attributes__(self): jobs_att = {} return(jobs_att) - #__| + # __| def append_jobs_attributes(self, attribute): """ @@ -1241,26 +1241,26 @@ def append_jobs_attributes(self, attribute): Append dictionary key value pair to the jobs_attributes dict. To be pickled and saved """ - #| - append_jobs_attributes + # | - append_jobs_attributes att_new = attribute self.jobs_att.update(att_new) job_att_file = self.root_dir + "/jobs_bin/job_attributes.csv" pickle.dump(self.jobs_att, open(job_att_file, "wb")) - #__| + # __| - #__| + # __| def __gen_datatable__(self): """Initialze data table from the properties of the jobs directory. New methods iterates through Job instances """ - #| - __generate_data_table + # | - __generate_data_table rows_list = [] for Job_i in self.Job_list: - #| - FOR LOOP BODY + # | - FOR LOOP BODY entry_param_dict = {} for prop, value in Job_i.job_params.items(): entry_param_dict[prop] = value @@ -1271,12 +1271,12 @@ def __gen_datatable__(self): entry_param_dict["revision_number"] = Job_i.revision_number rows_list.append(entry_param_dict) - #__| + # __| data_frame = pd.DataFrame(rows_list) return(data_frame) - #__| + # __| def __revision_list_and_max__(self, path_i): """Return list of revisions for given job path and highest revision. @@ -1291,7 +1291,7 @@ def __revision_list_and_max__(self, path_i): Args: path_i: """ - #| - __revision_list_and_max__ + # | - __revision_list_and_max__ if self.folders_exist: # dirs = os.listdir(os.path.join(self.working_dir, path_i)) @@ -1319,7 +1319,7 @@ def __revision_list_and_max__(self, path_i): ) return(dummy_return) - #__| + # __| def copy_files_jd(self, file_list, variable_lst, revision="Auto"): """ @@ -1330,32 +1330,32 @@ def copy_files_jd(self, file_list, variable_lst, revision="Auto"): variable_lst: revision: """ - #| - copy_files_jd + # | - copy_files_jd path = self.var_lst_to_path(variable_lst) path += "_" + str(self.job_revision_number(variable_lst)) for file in file_list: shutil.copyfile(self.root_dir + "/" + file, path + "/" + file) - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** - #| - __old__ + # | - __old__ # DEPR def __generate_data_table__(self): """Initialze data table from the properties of the jobs directory. Appends unique row for every job revision """ - #| - __generate_data_table__ + # | - __generate_data_table__ rows_list = [] for job in self.job_var_lst: revisions = self.job_revision_number(job) for revision in range(revisions + 1)[1:]: - #| - FOR LOOP BODY + # | - FOR LOOP BODY entry_param_dict = {} for prop in job: entry_param_dict[prop["property"]] = prop["value"] @@ -1367,12 +1367,12 @@ def __generate_data_table__(self): entry_param_dict["revision_number"] = revision rows_list.append(entry_param_dict) - #__| + # __| data_frame = pd.DataFrame(rows_list) return(data_frame) - #__| + # __| # DEPR def job_revision_number_old(self, variable_lst): @@ -1385,7 +1385,7 @@ def job_revision_number_old(self, variable_lst): Args: variable_lst: """ - #| - job_revision_number + # | - job_revision_number if self.folders_exist: path = self.var_lst_to_path(variable_lst) orig_dir = os.getcwd() @@ -1403,7 +1403,7 @@ def job_revision_number_old(self, variable_lst): else: return(1) - #| - __old__ + # | - __old__ # path = self.var_lst_to_path(variable_lst) # # path = "/".join(path.split("/")[0:-1]) + "/" @@ -1412,9 +1412,9 @@ def job_revision_number_old(self, variable_lst): # # return(len(dir_list)) # - #__| + # __| - #__| + # __| - #__| + # __| diff --git a/dft_job_automat/job_types_classes/data_frame_methods.py b/dft_job_automat/job_types_classes/data_frame_methods.py index baadeb7..1833426 100644 --- a/dft_job_automat/job_types_classes/data_frame_methods.py +++ b/dft_job_automat/job_types_classes/data_frame_methods.py @@ -7,15 +7,15 @@ Development Notes: """ -#| - IMPORT MODULES +# | - IMPORT MODULES import os import shutil -#__| +# __| class DataFrame_Methods(): """Summary line.""" - #| - DataFrame_Methods **************************************************** + # | - DataFrame_Methods **************************************************** def __init__(self, dataframe): """ Initialize with dataframe. @@ -25,9 +25,9 @@ def __init__(self, dataframe): Pandas dataframe object created by jobs_setup, jobs_analysis classes """ - #| - __init__ + # | - __init__ self.df = dataframe - #__| + # __| def create_atoms_objects(self, outdir="atoms_objects", @@ -44,7 +44,7 @@ def create_atoms_objects(self, atoms_row: image: """ - #| - create_atoms_objects + # | - create_atoms_objects df = self.df if not os.path.exists(outdir): @@ -75,6 +75,6 @@ def create_atoms_objects(self, else: pass - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** diff --git a/dft_job_automat/job_types_classes/dft_methods.py b/dft_job_automat/job_types_classes/dft_methods.py index 58879fd..99a6295 100644 --- a/dft_job_automat/job_types_classes/dft_methods.py +++ b/dft_job_automat/job_types_classes/dft_methods.py @@ -7,7 +7,7 @@ Make methods work for VASP and QE by using teh DFT_code attribute """ -#| - Import Modules +# | - Import Modules import os import pickle as pickle import json @@ -20,12 +20,12 @@ from ase_modules.ase_methods import create_species_element_dict from quantum_espresso.qe_methods import magmom_charge_data -#__| +# __| class DFT_Methods(): """Methods and analysis to perform within DFT jobs folders.""" - #| - DFT_Methods ********************************************************** + # | - DFT_Methods ********************************************************** def __init__(self, methods_to_run=[], DFT_code="QE", # VASP @@ -35,10 +35,10 @@ def __init__(self, Args: methods_to_run: """ - #| - __init__ + # | - __init__ self.methods_to_run = methods_to_run self.DFT_code = DFT_code - #__| + # __| def pdos_data(self, path_i): """Read pdos.pickle file and return data. @@ -46,7 +46,7 @@ def pdos_data(self, path_i): Args: path_i: """ - #| - pdos_data + # | - pdos_data fle_name = "dir_pdos/dos.pickle" if os.path.exists(path_i + "/" + fle_name): # with open(path_i + "/" + fle_name, "r") as fle: @@ -55,7 +55,7 @@ def pdos_data(self, path_i): data = pickle.load(fle, encoding="latin1") return(data) - #__| + # __| def bands_data(self, path_i): """Read band_disp.pickle file and return data. @@ -63,7 +63,7 @@ def bands_data(self, path_i): Args: path_i: """ - #| - bands_data + # | - bands_data fle_name = "dir_bands/band_disp.pickle" # print(path_i + "/" + fle_name) if os.path.exists(path_i + "/" + fle_name): @@ -71,7 +71,7 @@ def bands_data(self, path_i): data = pickle.load(fle, encoding="latin1") return(data) - #__| + # __| def magmom_charge_history(self, path_i, log="calcdir/log"): """Return atomic charges and magmoms thourgh SCF convergence history. @@ -79,7 +79,7 @@ def magmom_charge_history(self, path_i, log="calcdir/log"): Args: path_i """ - #| - magmom_charge_history + # | - magmom_charge_history df = magmom_charge_data(path_i=path_i, log=log) imp_col = ["atom_num", "iteration", "element"] @@ -91,7 +91,7 @@ def magmom_charge_history(self, path_i, log="calcdir/log"): out_dict["charge_history"] = charge_history_df return(out_dict) - #__| + # __| def gibbs_energy(self, path_i): """Read gibbs free energy from file. @@ -101,7 +101,7 @@ def gibbs_energy(self, path_i): Args: path_i """ - #| - gibbs_energy + # | - gibbs_energy gibbs_e = None fle_name = "g_energy.out" @@ -110,7 +110,7 @@ def gibbs_energy(self, path_i): gibbs_e = float(fle.read().strip()) return(gibbs_e) - #__| + # __| def gibbs_correction(self, path_i): """Return gibbs free energy correction. @@ -118,7 +118,7 @@ def gibbs_correction(self, path_i): Args: path_i """ - #| - gibbs_correction + # | - gibbs_correction gibbs_corr = 0. fle_name = "dir_vib/gibbs_corr.out" @@ -127,7 +127,7 @@ def gibbs_correction(self, path_i): gibbs_corr = float(fle.read().strip()) return(gibbs_corr) - #__| + # __| def elec_energy(self, path_i, atoms_file="out_opt.traj"): """Read electronic energy from ASE atoms object. @@ -136,7 +136,7 @@ def elec_energy(self, path_i, atoms_file="out_opt.traj"): path_i: atoms_file: """ - #| - elec_energy + # | - elec_energy energy = None try: @@ -150,7 +150,7 @@ def elec_energy(self, path_i, atoms_file="out_opt.traj"): pass - #| - Non-favored methods + # | - Non-favored methods try: atoms = self.atoms_object(path_i)[-1] energy = atoms.get_potential_energy() @@ -162,10 +162,10 @@ def elec_energy(self, path_i, atoms_file="out_opt.traj"): energy = atoms.get_potential_energy() except: pass - #__| + # __| return(energy) - #__| + # __| def atoms_object(self, path_i): """Attempt to read and return atoms object. @@ -173,7 +173,7 @@ def atoms_object(self, path_i): Args: path_i: """ - #| - atoms_object + # | - atoms_object # atoms_file_names = ["out_opt.traj", "out.traj"] # 'out.traj' should be read first @@ -186,7 +186,7 @@ def atoms_object(self, path_i): for file_name in atoms_file_names: try: - #| - try to read atoms + # | - try to read atoms if self.DFT_code == "VASP": cwd = os.getcwd() @@ -207,13 +207,13 @@ def atoms_object(self, path_i): ) break - #__| + # __| except: traj = None return(traj) - #__| + # __| def outcar(self, path_i): @@ -222,7 +222,7 @@ def outcar(self, path_i): Args: path_i: """ - #| - atoms_object + # | - atoms_object line_list = [] with open(os.path.join(path_i, "OUTCAR")) as fle: for line in fle: @@ -231,11 +231,11 @@ def outcar(self, path_i): return(line_list) - #| - __old__ + # | - __old__ # for file_name in atoms_file_names: # try: # - # #| - try to read atoms + # # | - try to read atoms # if self.DFT_code == "VASP": # # cwd = os.getcwd() @@ -255,21 +255,21 @@ def outcar(self, path_i): # ) # # break - # #__| + # # __| # # except: # traj = None # # return(traj) - #__| + # __| - #__| + # __| def incar(self, path_i): """ """ - #| - incar + # | - incar line_list = [] with open(os.path.join(path_i, "INCAR")) as fle: for line in fle: @@ -277,7 +277,7 @@ def incar(self, path_i): line_list.append(line) return(line_list) - #__| + # __| def init_atoms(self, path_i): """Attempt to read and return initial atoms object. @@ -285,7 +285,7 @@ def init_atoms(self, path_i): Args: path_i: """ - #| - init_atoms + # | - init_atoms traj = None atoms_file_names = [ @@ -304,7 +304,7 @@ def init_atoms(self, path_i): pass return(traj) - #__| + # __| def parse_error_file(self, path_i): """Parse QE ase-espresso error file for keywords indicating failure. @@ -315,10 +315,10 @@ def parse_error_file(self, path_i): Args: path_i: """ - #| - parse_error_file + # | - parse_error_file tmp = 42 print(tmp) - #__| + # __| def atom_type_num_dict(self, path_i): """Return dictionary containing atomic count for each element. @@ -326,7 +326,7 @@ def atom_type_num_dict(self, path_i): Args: path_i: """ - #| - atom_type_num_dict + # | - atom_type_num_dict atoms = None atoms_file_names = ["out_opt.traj", "out.traj"] for file_name in atoms_file_names: @@ -353,7 +353,7 @@ def atom_type_num_dict(self, path_i): # out_dict = number_of_atoms(atoms) return([out_dict]) - #__| + # __| # def dft_params(path_i): @@ -369,7 +369,7 @@ def dft_params(self, path_i): Args: path_i """ - #| - dft_params + # | - dft_params file_path_1 = os.path.join( path_i, path_i, @@ -388,6 +388,6 @@ def dft_params(self, path_i): dft_params_dict = json.load(open(file_path_2, "r")) return(dft_params_dict) - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** diff --git a/dft_job_automat/job_types_classes/raman_methods.py b/dft_job_automat/job_types_classes/raman_methods.py index 1178369..a3a7648 100644 --- a/dft_job_automat/job_types_classes/raman_methods.py +++ b/dft_job_automat/job_types_classes/raman_methods.py @@ -1,6 +1,6 @@ """Class defining methods to extract/manipulate data in vasp raman job folders.""" -#| - Import Modules +# | - Import Modules # from dft_job_automat.job_setup import DFT_Jobs_Setup # from aws.aws_class import AWS_Queues import pandas as pd @@ -12,7 +12,7 @@ # import pickle # import boto3 # import subprocess -#__| +# __| class Raman_Vasp(): """Summary line. @@ -25,22 +25,22 @@ def __init__(self, methods_to_run=[]): Args: """ - #| - __init__ + # | - __init__ self.tmp = 42 self.methods_to_run = methods_to_run - #__| + # __| def tmp_meth(self, path_i): """ """ - #| - tmp_meth + # | - tmp_meth return("tmp - tmp_meth") - #__| + # __| def read_modes_file(self, path_i): """ """ - #| - read_modes_file + # | - read_modes_file # raman_dat_file = path_i + "/simulation/vasp_raman.dat" # print("3:" + path_i) @@ -95,4 +95,4 @@ def read_modes_file(self, path_i): return(data_list) - #__| + # __| diff --git a/dft_post_analysis/bands.py b/dft_post_analysis/bands.py index 61e1096..2867cc0 100644 --- a/dft_post_analysis/bands.py +++ b/dft_post_analysis/bands.py @@ -5,7 +5,7 @@ Author: Johanness Voss mostly """ -#| - IMPORT MODULES +# | - IMPORT MODULES import copy from itertools import compress @@ -13,9 +13,9 @@ import plotly.graph_objs as go from misc_modules.numpy_methods import make_filter_list -#__| +# __| -#| - Methods +# | - Methods def plot_band_series( x_data, y_data, @@ -27,7 +27,7 @@ def plot_band_series( x_data: y_data: """ - #| - plot_dos_series + # | - plot_dos_series # trace = go.Scatter( trace = go.Scattergl( x=x_data, @@ -43,9 +43,9 @@ def plot_band_series( ) return(trace) - #__| + # __| -#__| +# __| def filter_bands_data(bands_data, percent_keep=0.6): """Filter bands data series to lower memory cost. @@ -54,7 +54,7 @@ def filter_bands_data(bands_data, percent_keep=0.6): bands_data: percent_keep: """ - #| - filter_bands_data + # | - filter_bands_data len_data = len(bands_data[2]) filter_list = make_filter_list(len_data, percent_keep) @@ -100,7 +100,7 @@ def filter_bands_data(bands_data, percent_keep=0.6): new_bands_data += (np.array(new_data),) return(new_bands_data) - #__| + # __| def plot_bands( bands_data, @@ -112,42 +112,42 @@ def plot_bands( bands_data: plot_title: """ - #| - plot_bands + # | - plot_bands - #| - SCRIPT PARAMETERS + # | - SCRIPT PARAMETERS # COMBAK emin = -20 emax = 20 plot_title = "Band diagram" - #__| + # __| s, k, x, X, e = bands_data # symbols = [t.replace('Gamma', '$\Gamma$') for t in s] symbols = [t.replace("Gamma", "G") for t in s] - #| - Checking if Atomic Projections Present + # | - Checking if Atomic Projections Present # If atomic projections in "e" variable if isinstance(e, tuple) and len(e) == 2: # For the time being I'll just redefine e e = e[0] - #__| + # __| - #| - Checking for Spin Polarization + # | - Checking for Spin Polarization # If spinpol = True, then e will have added dimension if e.shape[0] == 2: spinpol = True else: spinpol = False - #__| + # __| data_list = [] - #| - Plotting Bands + # | - Plotting Bands if spinpol is False: - #| - Spinpol: False + # | - Spinpol: False for n in range(len(e[0])): if n == 0: showleg = True @@ -155,10 +155,10 @@ def plot_bands( showleg = False data_list.append(plot_band_series(x, e[:, n], showlegend=showleg)) - #__| + # __| elif spinpol is True: - #| - Spinpol: True + # | - Spinpol: True for n in range(len(e[0][0])): if n == 0: showleg = True @@ -172,11 +172,11 @@ def plot_bands( data_list.append( plot_band_series(x, e[1][:, n], showlegend=showleg), ) - #__| + # __| - #__| + # __| - #| - Plot y=0 (Fermi Level) + # | - Plot y=0 (Fermi Level) fermi_level = go.Scattergl( x=[X[0], X[-1]], y=[0, 0], @@ -191,9 +191,9 @@ def plot_bands( ) data_list.append(fermi_level) - #__| + # __| - #| - Plot Vertical Lines at Special K-Points + # | - Plot Vertical Lines at Special K-Points for p in X: trace = go.Scattergl( x=[p, p], @@ -207,18 +207,18 @@ def plot_bands( ) ) data_list.append(trace) - #__| + # __| - #| - Plotly + # | - Plotly - #| - Plot Settings + # | - Plot Settings plot_title_size = 18 tick_lab_size = 16 axes_lab_size = 18 legend_size = 18 - #__| + # __| - #| - Plot Layout + # | - Plot Layout layout = { "title": plot_title, "showlegend": False, @@ -228,7 +228,7 @@ def plot_bands( "color": "black", }, - #| - Axes -------------------------------------------------------------- + # | - Axes -------------------------------------------------------------- "yaxis": { "title": "Energy [eV]", "range": [emin, emax], @@ -254,24 +254,24 @@ def plot_bands( }, - #__| ------------------------------------------------------------------- + # __| ------------------------------------------------------------------- - #| - Legend ------------------------------------------------------------ + # | - Legend ------------------------------------------------------------ "legend": { "traceorder": "normal", "font": dict(size=legend_size) }, - #__| ------------------------------------------------------------------- + # __| ------------------------------------------------------------------- - #| - Plot Size + # | - Plot Size "width": 200 * 4., "height": 200 * 3., - #__| + # __| } - #__| + # __| - #__| + # __| return(data_list, layout) - #__| + # __| diff --git a/dft_post_analysis/charge_density.py b/dft_post_analysis/charge_density.py index 30b9e7b..af83e38 100644 --- a/dft_post_analysis/charge_density.py +++ b/dft_post_analysis/charge_density.py @@ -5,7 +5,7 @@ Author: Raul A. Flores """ -#| - Import Modules +# | - Import Modules # import os # import sys import random @@ -19,12 +19,12 @@ # import plotly as py import plotly.graph_objs as go -#__| +# __| class ChargeDensity(object): """docstring for ChargeDensity.""" - #| - ChargeDensity ******************************************************** + # | - ChargeDensity ******************************************************** def __init__(self, cube_filename, master_data_filter=0.98, @@ -43,15 +43,15 @@ def __init__(self, Lower bound of normalized density value to be discarded working_dir: """ - #| - __init__ + # | - __init__ - #| - Define User Attributes + # | - Define User Attributes self.cube_filename = cube_filename self.master_data_filter = master_data_filter self.lower_bound_density_filter = lower_bound_density_filter self.wrap_atoms = wrap_atoms self.working_dir = working_dir - #__| + # __| ( self.atoms, @@ -70,11 +70,11 @@ def __init__(self, self.__norm_electron_density__() self.__filter_low_density__() # self.__keep_only_edges__() - #__| + # __| def __load_cube_file__(self): """Load charge density cube file.""" - #| - __load_cube_file__ + # | - __load_cube_file__ filename = self.cube_filename with open(filename, "r") as fle: @@ -88,11 +88,11 @@ def __load_cube_file__(self): atoms.wrap(pbc=True) return(atoms, cd_data, origin) - #__| + # __| def __process_data__(self): """Create dataframe from charge density data grid.""" - #| - __process_data__ + # | - __process_data__ cd_data = self.cd_data atoms = self.atoms @@ -142,21 +142,21 @@ def multiply_by_unit_cell(row, atoms=None): ) = zip(*df.apply(multiply_by_unit_cell, axis=1, atoms=atoms)) return(df) - #__| + # __| def __number_of_data_points__(self): """Return the number of individual density data points.""" - #| - __number_of_data_points__ + # | - __number_of_data_points__ master_data_df = self.master_data_df num_data_points = len(master_data_df) return(num_data_points) - #__| + # __| def __filter_data__(self): """Filter data randomly to decrease the data set size.""" - #| - __filter_data__ + # | - __filter_data__ master_data_df = self.master_data_df num_data_points = self.num_data_points master_data_filter = self.master_data_filter @@ -172,31 +172,31 @@ def __filter_data__(self): df_filtered = master_data_df[bool_list] self.master_data_df = df_filtered - #__| + # __| def __norm_electron_density__(self): """Normalize electron density from 0 to 1.""" - #| - __norm_electron_density__ + # | - __norm_electron_density__ df = self.master_data_df max_density = df.density.max() df["norm_dens"] = df["density"] / max_density - #__| + # __| def __filter_low_density__(self): """Filter low density entries from the data.""" - #| - __filter_low_density__ + # | - __filter_low_density__ df = self.master_data_df lower_bound_density_filter = self.lower_bound_density_filter df = df[df["norm_dens"] > lower_bound_density_filter] self.master_data_df = df - #__| + # __| def __keep_only_edges__(self): """Only keep the outer surface points for clarity.""" - #| - __keep_only_edges__ + # | - __keep_only_edges__ df = self.master_data_df df_a = df[df["x_ind"] == df.x_ind.max()] @@ -215,30 +215,30 @@ def __keep_only_edges__(self): ) self.master_data_df = df_surf - #__| + # __| def __save_dataframe__(self): """Save dataframe to pickle file. COMBAK impliment this to save time """ - #| - __save_dataframe__ + # | - __save_dataframe__ df = self.master_data_df working_dir = self.working_dir with open(working_dir + "/dataframe.pickle", "w") as fle: pickle.dump(df, fle) - #__| + # __| def __load_dataframe__(self): """Load dataframe from pickle file. COMBAK and finish this """ - #| - __load_dataframe__ + # | - __load_dataframe__ tmp = 42 - #__| + # __| def create_charge_density_plotting_trace(self, opacity=0.4, @@ -255,7 +255,7 @@ def create_charge_density_plotting_trace(self, "variable", to scale individual marker size with charge density """ - #| - create_charge_density_plotting_trace + # | - create_charge_density_plotting_trace df = self.master_data_df if size == "variable": @@ -290,7 +290,7 @@ def create_charge_density_plotting_trace(self, ) return(trace1) - #__| + # __| def create_unit_cell_plotting_trace(self, color="red", @@ -301,7 +301,7 @@ def create_unit_cell_plotting_trace(self, Args: color """ - #| - create_unit_cell_plotting_trace + # | - create_unit_cell_plotting_trace atoms = self.atoms def unit_cell_leg_trace( @@ -318,7 +318,7 @@ def unit_cell_leg_trace( color: width: """ - #| - unit_cell_leg_trace + # | - unit_cell_leg_trace line_i = np.array([ point_0, point_1, @@ -334,11 +334,11 @@ def unit_cell_leg_trace( ) return(trace_i) - #__| + # __| line_trace_list = [ - #| - Origin to 3 adjacent corners + # | - Origin to 3 adjacent corners unit_cell_leg_trace( np.array([0., 0., 0.]), atoms.cell[0], @@ -353,9 +353,9 @@ def unit_cell_leg_trace( np.array([0., 0., 0.]), atoms.cell[2], ), - #__| + # __| - #| - Farthest corner and 3 adjacent cornerse + # | - Farthest corner and 3 adjacent cornerse unit_cell_leg_trace( atoms.cell[0] + atoms.cell[1] + atoms.cell[2], atoms.cell[0] + atoms.cell[2] @@ -370,9 +370,9 @@ def unit_cell_leg_trace( atoms.cell[0] + atoms.cell[1] + atoms.cell[2], atoms.cell[0] + atoms.cell[1] ), - #__| + # __| - #| - TEMP + # | - TEMP unit_cell_leg_trace( atoms.cell[1] + atoms.cell[2], atoms.cell[1], @@ -384,9 +384,9 @@ def unit_cell_leg_trace( atoms.cell[0], color=color, ), - #__| + # __| - #| - TEMP + # | - TEMP unit_cell_leg_trace( atoms.cell[2], atoms.cell[0] + atoms.cell[2], @@ -399,9 +399,9 @@ def unit_cell_leg_trace( atoms.cell[1] + atoms.cell[2], color=color, ), - #__| + # __| - #| - TEMP + # | - TEMP unit_cell_leg_trace( atoms.cell[0] + atoms.cell[1], atoms.cell[0], @@ -413,12 +413,12 @@ def unit_cell_leg_trace( atoms.cell[1], color=color, ), - #__| + # __| ] return(line_trace_list) - #__| + # __| def create_atoms_plotting_trace(self, size=12, @@ -428,7 +428,7 @@ def create_atoms_plotting_trace(self, Args: size: """ - #| - create_atoms_plotting_trace + # | - create_atoms_plotting_trace atoms = self.atoms element_color_dict = {} @@ -454,6 +454,6 @@ def create_atoms_plotting_trace(self, ) return(trace_i) - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** diff --git a/dft_post_analysis/dos.py b/dft_post_analysis/dos.py index e56f9ca..7e57129 100644 --- a/dft_post_analysis/dos.py +++ b/dft_post_analysis/dos.py @@ -8,7 +8,7 @@ TODO Include the atoms object so that I can reference atom types """ -#| - Import Modules +# | - Import Modules import copy from itertools import compress @@ -17,9 +17,9 @@ import plotly.graph_objs as go from misc_modules.numpy_methods import make_filter_list -#__| +# __| -#| - Methods +# | - Methods def plot_dos_series( x_data, y_data, @@ -31,7 +31,7 @@ def plot_dos_series( Args: """ - #| - plot_dos_series + # | - plot_dos_series trace = go.Scatter( x=x_data, y=y_data, @@ -43,9 +43,9 @@ def plot_dos_series( ) return(trace) - #__| + # __| -#__| +# __| def filter_pdos_data(pdos_data, percent_keep=0.4): """Filter dos and pdos data series to lower memory cost. @@ -55,7 +55,7 @@ def filter_pdos_data(pdos_data, percent_keep=0.4): percent_keep: Fraction of data to keep, the rest is discarded """ - #| - filter_pdos_data + # | - filter_pdos_data len_data = len(pdos_data[0]) filter_list = make_filter_list(len_data, percent_keep) @@ -99,7 +99,7 @@ def filter_pdos_data(pdos_data, percent_keep=0.4): return(new_pdos_data) - #__| + # __| def plot_pdos_dos( pdos_data, @@ -116,19 +116,19 @@ def plot_pdos_dos( filter_dict: atoms: """ - #| - plot_pdos_dos + # | - plot_pdos_dos energies, dos, pdos = pdos_data - #| - Determing Whether Calclation Is Spin Polarized + # | - Determing Whether Calclation Is Spin Polarized if len(dos) != 2: spinpol = False elif len(dos) == 2: spinpol = True - #__| + # __| - #| - Data Processing + # | - Data Processing - #| - Total Density of State + # | - Total Density of State dos_data = [] if spinpol: dos_tot_u = dos[0] @@ -169,18 +169,18 @@ def plot_pdos_dos( dos_data.append(trace) - #__| + # __| - #| - Atomic Projected Density of State + # | - Atomic Projected Density of State pdos_master_data = [] if spinpol: - #| - Spinpol: True + # | - Spinpol: True for pdos_i, atom_i in zip(pdos, atoms): elem_i = atom_i.symbol ind_i = atom_i.index - #| - Data Format Type Dict + # | - Data Format Type Dict type_dict = {} type_dict["p"] = [ @@ -224,7 +224,7 @@ def plot_pdos_dos( "dxy up", "dxy down", ] - #__| + # __| # for band_j, dos_j in pdos_i.iteritems(): for band_j, dos_j in pdos_i.items(): @@ -240,12 +240,12 @@ def plot_pdos_dos( } pdos_master_data.append(row_i) - #__| + # __| else: - #| - Spinpol: False + # | - Spinpol: False - #| - Data Format Type Dict + # | - Data Format Type Dict type_dict = {} type_dict["p"] = [ "sum", @@ -267,7 +267,7 @@ def plot_pdos_dos( "dx2-y2", "dxy", ] - #__| + # __| pdos_master_data = [] for pdos_i, atom_i in zip(pdos, atoms): @@ -286,14 +286,14 @@ def plot_pdos_dos( } pdos_master_data.append(row_i) - #__| + # __| - #__| + # __| df = pd.DataFrame(pdos_master_data) - #__| + # __| - #| - Data Analysis + # | - Data Analysis df["name"] = df["element"] + df["atom_ind"].astype(str) + " | " + \ df["band"] + df["type"] @@ -307,9 +307,9 @@ def plot_pdos_dos( for key, value in filter_dict.items(): df = df[df[key].isin(value)] - #__| + # __| - #| - Plotly Scatter Plot Creation + # | - Plotly Scatter Plot Creation data = [] for index, row in df.iterrows(): if group is not None: @@ -327,20 +327,20 @@ def plot_pdos_dos( data.append(data_i) pdos_data_out = data - #__| + # __| - #| - Plotting + # | - Plotting - #| - Plot Settings + # | - Plot Settings plot_title_size = 20 tick_lab_size = 16 axes_lab_size = 18 legend_size = 18 - #__| + # __| - #| - Plot Layout + # | - Plot Layout layout = { "title": plot_title, "font": { @@ -349,7 +349,7 @@ def plot_pdos_dos( "color": "black", }, - #| - Axes ------------------------------------------------------------- + # | - Axes ------------------------------------------------------------- "yaxis": { "title": "E - Efermi [eV]", "zeroline": True, @@ -374,25 +374,25 @@ def plot_pdos_dos( "showticklabels": False, "range": [0, max_dens], }, - #__| ------------------------------------------------------------------ + # __| ------------------------------------------------------------------ - #| - Legend ----------------------------------------------------------- + # | - Legend ----------------------------------------------------------- "legend": { "traceorder": "normal", "font": dict(size=legend_size) }, - #__| ------------------------------------------------------------------ + # __| ------------------------------------------------------------------ - #| - Plot Size + # | - Plot Size # "width": 200 * 4., # "height": 200 * 3., - #__| + # __| } - #__| + # __| - #__| + # __| return(dos_data, pdos_data_out, layout) - #__| + # __| diff --git a/dft_post_analysis/dos_bands_combined.py b/dft_post_analysis/dos_bands_combined.py index 9c2587c..9d195e3 100644 --- a/dft_post_analysis/dos_bands_combined.py +++ b/dft_post_analysis/dos_bands_combined.py @@ -5,9 +5,9 @@ Author: Raul A. Flores """ -#| - IMPORT MODULES +# | - IMPORT MODULES from plotly import tools -#__| +# __| # pdos_data_out, @@ -39,7 +39,7 @@ def plot_pdos_bands( pdos_layout: bands_layout: """ - #| - plot_pdos_bands + # | - plot_pdos_bands # TEMP_PRINT print("KSDKFJDSJIFJSIDJFISD") @@ -80,4 +80,4 @@ def plot_pdos_bands( fig["layout"]["yaxis"]["range"] = e_range return(fig) - #__| + # __| diff --git a/dft_post_analysis/rapiDOS/rapiDOS.py b/dft_post_analysis/rapiDOS/rapiDOS.py index 9117ff7..4adb355 100644 --- a/dft_post_analysis/rapiDOS/rapiDOS.py +++ b/dft_post_analysis/rapiDOS/rapiDOS.py @@ -5,7 +5,7 @@ jagt@stanford.edu & bajdich@slac.stanford.edu """ -#| - Import Modules +# | - Import Modules import numpy as np from ase import io import re @@ -14,7 +14,7 @@ # __| -#| - Script Inputs +# | - Script Inputs # data_folder = "/home/raulf2012/__temp__/irox_bulk_systems/IrO2/_1" # # out_folder = "out_data" @@ -26,10 +26,10 @@ # URL: http://theory.cm.utexas.edu/vtsttools/scripts.html ############################################################################## -#| - Methods +# | - Methods # ### READ DOSCAR ### def read_dosfile(data_folder): - #| - read_dosfile + # | - read_dosfile f = open(os.path.join(data_folder, "DOSCAR"), 'r') lines = f.readlines() f.close() @@ -41,11 +41,11 @@ def read_dosfile(data_folder): print(natoms, nedos, efermi) return lines, index, natoms, nedos, efermi - #__| + # __| # ### READ POSCAR or CONTCAR and save pos def read_posfile(data_folder): - #| - read_posfile + # | - read_posfile from ase.io import read try: @@ -55,11 +55,11 @@ def read_posfile(data_folder): atoms = [] return atoms - #__| + # __| # ### WRITE DOS0 CONTAINING TOTAL DOS ### def write_dos0(lines, index, nedos, efermi, out_folder): - #| - write_dos0 + # | - write_dos0 fdos = open(os.path.join(out_folder, "DOS0"), 'w') line = lines[index + 1].strip().split() ncols = int(len(line)) @@ -76,11 +76,11 @@ def write_dos0(lines, index, nedos, efermi, out_folder): fdos.write('%15.8f ' % (dos)) fdos.write('\n') return index - #__| + # __| # ### LOOP OVER SETS OF DOS, NATOMS ### def write_nospin(lines, index, nedos, natoms, ncols, efermi, data_folder, out_folder): - #| - write_nospin + # | - write_nospin atoms = read_posfile(data_folder) if len(atoms) < natoms: pos = np.zeros((natoms, 3)) @@ -109,10 +109,10 @@ def write_nospin(lines, index, nedos, natoms, ncols, efermi, data_folder, out_fo fdos.write('%15.8f ' % (dos)) fdos.write('\n') fdos.close() - #__| + # __| def write_spin(lines, index, nedos, natoms, ncols, efermi, data_folder, out_folder): - #| - write_spin + # | - write_spin #pos=[] atoms = read_posfile(data_folder) if len(atoms) < natoms: @@ -144,11 +144,11 @@ def write_spin(lines, index, nedos, natoms, ncols, efermi, data_folder, out_fold fdos.write('%15.8f %15.8f ' % (dos_up, dos_down)) fdos.write('\n') fdos.close() - #__| + # __| def get_bandgap(total_dos): - #| - get_bandgap + # | - get_bandgap arg_fermi = np.where(total_dos[:, 0] > 0)[0][0] arg_fermi_upper = arg_fermi arg_fermi_lower = arg_fermi @@ -174,7 +174,7 @@ def get_bandgap(total_dos): print("Approx. band gap: ", np.abs(band_gap), "eVv") return [e_lower, e_upper, band_gap] - #__| + # __| # __| @@ -186,7 +186,7 @@ def rapiDOS( ): """ """ - #| - __main__ ************************************************************* + # | - __main__ ************************************************************* if data_folder is None: data_folder = "." @@ -373,4 +373,4 @@ def rapiDOS( #use the second to run it without a browser #os.system('jupyter nbconvert --to notebook --execute rapiDOS_analysis.ipynb') - #__| + # __| diff --git a/dft_post_analysis/wf.py b/dft_post_analysis/wf.py index d229eb4..ecfa3b5 100644 --- a/dft_post_analysis/wf.py +++ b/dft_post_analysis/wf.py @@ -6,12 +6,12 @@ Author(s): I got the script from Colin but I think Karen wrote these methods """ -#| - Import Modules +# | - Import Modules # from ase.units import Bohr # from ase.io import read import numpy as np import os -#__| +# __| def find_max_empty_space(atoms, edir=3): """Return scaled midpoint coordinate of largest empty space. @@ -20,7 +20,7 @@ def find_max_empty_space(atoms, edir=3): continuous segment of free, unoccupied space and returns its midpoint in scaled coordinates (0 to 1) in the edir direction (default z). """ - #| - find_max_empty_space + # | - find_max_empty_space # 0-indexed direction position_array = atoms.get_scaled_positions()[..., edir - 1] position_array.sort() @@ -38,7 +38,7 @@ def find_max_empty_space(atoms, edir=3): out = (position_array[max_diff_index] + position_array[max_diff_index + 1]) / 2. return(out) - #__| + # __| def calc_wf(atoms, outdir): """Calculate work function of slab. @@ -47,7 +47,7 @@ def calc_wf(atoms, outdir): atoms: outdir: """ - #| - calc_wf + # | - calc_wf hartree = 27.21138505 rydberg = 0.5 * hartree bohr = 0.52917721092 @@ -112,4 +112,4 @@ def calc_wf(atoms, outdir): ] return(wf) - #__| + # __| diff --git a/energetics/dft_energy.py b/energetics/dft_energy.py index 2b01002..4d6376e 100644 --- a/energetics/dft_energy.py +++ b/energetics/dft_energy.py @@ -8,17 +8,17 @@ NOTE Take into account BEEF ensemble of energies """ -#| - Import Modules +# | - Import Modules import os import sys -#__| +# __| class Energy(object): """ """ - #| - Energy *************************************************************** + # | - Energy *************************************************************** def __init__(self, gibbs_e=None, internal_e=None, @@ -46,7 +46,7 @@ def __init__(self, rot_e: vib_e: """ - #| - __init__ + # | - __init__ self.gibbs_e = gibbs_e self.internal_e = internal_e self.enthalpy_e = enthalpy_e @@ -71,33 +71,33 @@ def __init__(self, if self.gibbs_e is None: self.gibbs_e = self.calc_gibbs_free_energy() - #__| + # __| def __str__(self): """ """ - #| - __str__ + # | - __str__ mess = "Gibbs Free Energy: " + str(self.gibbs_e) + "\n" mess += "Electronic Energy: " + str(self.electronic_e) + "\n" return(mess) - #__| + # __| def __repr__(self): """Representation of instance when printed to stdout.""" - #| - __repr__ + # | - __repr__ mess = "Electronic Energy: " + str(self.electronic_e) + "\n" mess += "Enthalpy Energy: " + str(self.enthalpy_e) + "\n" mess += "Gibbs Free Energy: " + str(self.gibbs_e) + "\n" return(mess) - #__| + # __| def __sub__(self, other): """ """ - #| - __sub__ + # | - __sub__ def subtract_mine(a, b): """Return a - b. @@ -108,14 +108,14 @@ def subtract_mine(a, b): a: b: """ - #| - subtract_mine + # | - subtract_mine if a is None or b is None: return(None) else: out = a - b return(out) - #__| + # __| if isinstance(other, Energy): @@ -166,19 +166,19 @@ def subtract_mine(a, b): out_Energy = Energy(**E_dict) return(out_Energy) - #__| + # __| def __add__(self, other): """ """ - #| - __add__ + # | - __add__ return (self.gibbs_e + other.gibbs_e) - #__| + # __| def __truediv__(self, other): """ """ - #| - __truediv__ + # | - __truediv__ if isinstance(other, Energy): electronic_e_new = self.electronic_e / other.electronic_e @@ -204,15 +204,15 @@ def __truediv__(self, other): out_Energy = Energy(**E_dict) return(out_Energy) - #__| + # __| def __floordiv__(self, other): """ """ - #| - __floordiv__ + # | - __floordiv__ print("__floordiv__") return (self.gibbs_e / other.gibbs_e) - #__| + # __| @@ -225,7 +225,7 @@ def add_entries(entries_list): Args: entries_list: """ - #| - add_entries + # | - add_entries sum_tot = 0. for entry in entries_list: if entry is None: @@ -235,7 +235,7 @@ def add_entries(entries_list): sum_tot += summand return(sum_tot) - #__| + # __| def calc_internal_energy(self): """Calculate internal energy. @@ -243,7 +243,7 @@ def calc_internal_energy(self): Args: """ - #| - internal_energy + # | - internal_energy energy_list = [ self.electronic_e, self.zero_point_e, @@ -256,12 +256,12 @@ def calc_internal_energy(self): internal_energy = self.add_entries(energy_list) return(internal_energy) - #__| + # __| def calc_enthalpy_energy(self): """ """ - #| - calc_enthalpy_energy + # | - calc_enthalpy_energy energy_list = [ self.internal_e, self.PV_term, @@ -270,12 +270,12 @@ def calc_enthalpy_energy(self): enthalpy_e = self.add_entries(energy_list) return(enthalpy_e) - #__| + # __| def calc_gibbs_free_energy(self): """ """ - #| - calc_gibbs_free_energy + # | - calc_gibbs_free_energy if self.entropy_term is not None: entropy_term = -self.entropy_term else: @@ -289,9 +289,9 @@ def calc_gibbs_free_energy(self): gibbs_e = self.add_entries(energy_list) return(gibbs_e) - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** class Element_Refs(): @@ -310,7 +310,7 @@ class Element_Refs(): """ - #| - Element_Refs ********************************************************* + # | - Element_Refs ********************************************************* def __init__(self, H2O_dict={ @@ -387,7 +387,7 @@ def __init__(self, Conventionally for ORR/OER I use a H2, H2O reference state, such that H2O has 0 energy """ - #| - __init__ + # | - __init__ self.En_H2O = Energy(**H2O_dict) self.En_H2 = Energy(**H2_dict) self.En_O2 = Energy(**O2_dict) @@ -400,12 +400,12 @@ def __init__(self, self.H2O_form_gibbs = Energy(**H2O_form_e_dict) self.E_O_ref, self.E_H_ref = self.calc_ref_energies() - #__| + # __| def calc_ref_energies(self): """ """ - #| - calc_ref_energies + # | - calc_ref_energies if self.hydrogen_ref == "H2": # hyd_ref = self.En_H2 / 2. @@ -424,6 +424,6 @@ def calc_ref_energies(self): # print(tmp) return(oxy_ref, hyd_ref) - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** diff --git a/energetics/formation_energy.py b/energetics/formation_energy.py index 9556aa8..5ceae1c 100644 --- a/energetics/formation_energy.py +++ b/energetics/formation_energy.py @@ -5,11 +5,11 @@ Author: Raul A. Flores """ -#| - Import Modules +# | - Import Modules import numpy as np from ase_modules.ase_methods import create_species_element_dict -#__| +# __| def calc_formation_energy( atoms, @@ -36,7 +36,7 @@ def calc_formation_energy( reference_dict: normalize_per_atom: """ - #| - calc_formation_energy + # | - calc_formation_energy atoms.info["element_dict"] = create_species_element_dict(atoms) num_atoms = atoms.get_number_of_atoms() @@ -45,7 +45,7 @@ def calc_formation_energy( # ordered_elems.sort() - #| - Finding List of Unique Elements Defined by reference_states list + # | - Finding List of Unique Elements Defined by reference_states list ref_state_elem_list = [] for i in reference_states: ref_i_elems = list(i["element_dict"]) @@ -53,10 +53,10 @@ def calc_formation_energy( ordered_elems = list(set(ref_state_elem_list)) ordered_elems.sort() - #__| + # __| - #| - Constructing the A matrix + # | - Constructing the A matrix a_matrix = [] for ref_state_i in reference_states: ref_i_comp_vect = [] @@ -69,9 +69,9 @@ def calc_formation_energy( a_matrix.append(np.array(ref_i_comp_vect)) a_matrix = np.array(a_matrix).transpose() - #__| + # __| - #| - Constructing the b vector + # | - Constructing the b vector # phi = 1. b_vect = [] for elem_i in ordered_elems: @@ -82,16 +82,16 @@ def calc_formation_energy( b_vect.append(0.) b_vect = np.array(b_vect) - #__| + # __| - #| - Solve linear system of equations + # | - Solve linear system of equations x = np.linalg.solve( a_matrix, b_vect, ) - #__| + # __| - #| - Calculate Formation Energy + # | - Calculate Formation Energy ref_e_sum = 0. for coeff_i, ref_i in zip(x, reference_states): ref_i_contribution = coeff_i * ref_i["elec_e"] @@ -101,8 +101,8 @@ def calc_formation_energy( if normalize_per_atom: form_e = form_e / num_atoms - #__| + # __| return(form_e) - #__| + # __| diff --git a/misc_modules/image_processing.py b/misc_modules/image_processing.py index 42380a9..82a70fa 100644 --- a/misc_modules/image_processing.py +++ b/misc_modules/image_processing.py @@ -2,9 +2,9 @@ """ -#| - IMPORT MODULES +# | - IMPORT MODULES import os -#__| +# __| def convert_pdf_to_svg(figure_path, out_dir, converter="inkscape"): @@ -13,7 +13,7 @@ def convert_pdf_to_svg(figure_path, out_dir, converter="inkscape"): Args: converter: 'inkscape' or 'cairo' """ - #| - convert_pdf_to_svg + # | - convert_pdf_to_svg extension = figure_path[-3:] # assert extension == "pdf", "Must give a pdf" @@ -50,4 +50,4 @@ def convert_pdf_to_svg(figure_path, out_dir, converter="inkscape"): print("Can only handle svg or pdfs now") return(output_path) - #__| + # __| diff --git a/misc_modules/misc_methods.py b/misc_modules/misc_methods.py index 493b8fb..2d32c27 100644 --- a/misc_modules/misc_methods.py +++ b/misc_modules/misc_methods.py @@ -4,11 +4,11 @@ TEMP """ -#| - IMPORT MODULES +# | - IMPORT MODULES import os # import sys -#__| +# __| def even_spaced_range(start_finish, spacing): @@ -25,7 +25,7 @@ def even_spaced_range(start_finish, spacing): ex. [1, 87] spacing: """ - #| - even_spaced_range + # | - even_spaced_range out_list = [] entry_i = start_finish[0] while entry_i < start_finish[1]: @@ -34,17 +34,17 @@ def even_spaced_range(start_finish, spacing): out_list.append(start_finish[1]) return(out_list) - #__| + # __| def merge_two_dicts(x, y): """ """ - #| - merge_two_dicts + # | - merge_two_dicts z = x.copy() # start with x's keys and values z.update(y) # modifies z with y's keys and values & returns None return z - #__| + # __| import collections @@ -62,17 +62,17 @@ def dict_merge(dct, merge_dct): Obtained from: https://gist.github.com/angstwad/bf22d1822c38a92ec0a9 """ - #| - dict_merge + # | - dict_merge # for k, v in merge_dct.iteritems(): for k, v in merge_dct.items(): if (k in dct and isinstance(dct[k], dict) and isinstance(merge_dct[k], collections.Mapping)): dict_merge(dct[k], merge_dct[k]) else: dct[k] = merge_dct[k] - #__| + # __| -#| - File and Directory Management +# | - File and Directory Management def remove_file_from_all_folders( file_name_list, @@ -87,7 +87,7 @@ def remove_file_from_all_folders( remove_files: Whether to perform file deletion or not. root_dir: Starting directory, assumed to be current directory """ - #| - remove_file_from_all_folders + # | - remove_file_from_all_folders total_disk_usage = 0. for dirName, subdirList, fileList in os.walk(root_dir): for file_name in file_name_list: @@ -130,13 +130,13 @@ def remove_file_from_all_folders( unit = "MB" print("Total Disk Usage: ", str(total_disk_usage_print), "", unit) - #__| + # __| -#__| +# __| -#| - Generating unique IDs +# | - Generating unique IDs import random from random import choice @@ -145,7 +145,7 @@ def GetFriendlyID(append_random_num=False): Create an ID string we can recognise. (Think Italian or Japanese or Native American.) """ - #| - GetFriendlyID + # | - GetFriendlyID v = 'aeiou' c = 'bdfghklmnprstvw' @@ -157,7 +157,7 @@ def GetFriendlyID(append_random_num=False): str(random.randint(0, 9)) return(id_i) - #__| + # __| def GetUniqueFriendlyID(used_ids): """Return an ID that is not in our list of already used IDs. @@ -167,7 +167,7 @@ def GetUniqueFriendlyID(used_ids): id = GetUniqueFriendlyID(used_ids) used_ids.add(id) """ - #| - GetUniqueFriendlyID + # | - GetUniqueFriendlyID # trying infinitely is a bad idea LIMIT = 1000 @@ -179,6 +179,6 @@ def GetUniqueFriendlyID(used_ids): count += 1 id = '' return id - #__| + # __| -#__| +# __| diff --git a/misc_modules/numpy_methods.py b/misc_modules/numpy_methods.py index a8af1c3..02b228b 100644 --- a/misc_modules/numpy_methods.py +++ b/misc_modules/numpy_methods.py @@ -1,15 +1,15 @@ -#| - IMPORT MODULES +# | - IMPORT MODULES import numpy as np -#__| +# __| def unit_vector(vector): """ Returns the unit vector of the vector. https://stackoverflow.com/questions/2827393/angles-between-two-n-dimensional-vectors-in-python """ - #| - unit_vector + # | - unit_vector return vector / np.linalg.norm(vector) - #__| + # __| def angle_between(v1, v2): """ Returns the angle in radians between vectors 'v1' and 'v2':: @@ -22,26 +22,26 @@ def angle_between(v1, v2): 3.141592653589793 https://stackoverflow.com/questions/2827393/angles-between-two-n-dimensional-vectors-in-python """ - #| - angle_between + # | - angle_between v1_u = unit_vector(v1) v2_u = unit_vector(v2) return np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0)) - #__| + # __| def smooth_data_series(y_data, box_pts): """Smooth data series by convolution """ - #| - smooth_data_series + # | - smooth_data_series box = np.ones(box_pts) / box_pts y_smooth = np.convolve(y_data, box, mode="same") return(y_smooth) - #__| + # __| def make_filter_list(len_data, percent_keep): """ """ - #| - filter_list + # | - filter_list # len_data = len(pdos_data[0]) @@ -52,4 +52,4 @@ def make_filter_list(len_data, percent_keep): ) return(filter_list) - #__| + # __| diff --git a/misc_modules/pandas_methods.py b/misc_modules/pandas_methods.py index ddd06a3..b140e93 100644 --- a/misc_modules/pandas_methods.py +++ b/misc_modules/pandas_methods.py @@ -14,9 +14,9 @@ def reorder_df_columns(col_order_list, df): """ """ - #| - show + # | - show - #| - __old__ + # | - __old__ # col_order_list = [ # # Main system variables # "bulk_system", @@ -70,7 +70,7 @@ def reorder_df_columns(col_order_list, df): # "Job", # "layers", # ] - #__| + # __| col_order_list.reverse() @@ -86,14 +86,14 @@ def reorder_df_columns(col_order_list, df): df = df[df_col_list] return(df) - #__| + # __| def drop_columns(df=None, columns=None, keep_or_drop="keep"): """ """ - #| - drop_columns + # | - drop_columns if keep_or_drop == "keep": cols_to_keep = columns @@ -113,4 +113,4 @@ def drop_columns(df=None, columns=None, keep_or_drop="keep"): raise ValueError('BAD BAD BAD') return(df_out) - #__| + # __| diff --git a/misc_modules/plotly_methods.py b/misc_modules/plotly_methods.py index 4526793..bbe435f 100644 --- a/misc_modules/plotly_methods.py +++ b/misc_modules/plotly_methods.py @@ -5,13 +5,13 @@ Author: Raul A. Flores """ -#| - Import Modules +# | - Import Modules import json # import numpy as np from plotly.utils import PlotlyJSONEncoder import plotly.graph_objs as go -#__| +# __| def plotlyfig2json(fig, fpath=None): """Serialize a plotly figure object to JSON so it can be persisted to disk. @@ -27,7 +27,7 @@ def plotlyfig2json(fig, fpath=None): Modified from https://github.com/nteract/nteract/issues/1229 """ - #| - plotlyfig2json + # | - plotlyfig2json redata = json.loads(json.dumps(fig.data, cls=PlotlyJSONEncoder)) relayout = json.loads(json.dumps(fig.layout, cls=PlotlyJSONEncoder)) @@ -38,7 +38,7 @@ def plotlyfig2json(fig, fpath=None): f.write(fig_json) else: return fig_json - #__| + # __| def plotlyfromjson(fpath): """Render a plotly figure from a json file. @@ -49,7 +49,7 @@ def plotlyfromjson(fpath): Args: fpath: """ - #| - plotlyfromjson + # | - plotlyfromjson with open(fpath, 'r') as f: v = json.loads(f.read()) @@ -57,4 +57,4 @@ def plotlyfromjson(fpath): return(fig) # iplot(fig, show_link=False) - #__| + # __| diff --git a/oxr_reaction/__old__/old.py b/oxr_reaction/__old__/old.py index 8e85c38..b7fceaf 100644 --- a/oxr_reaction/__old__/old.py +++ b/oxr_reaction/__old__/old.py @@ -1,5 +1,5 @@ -#| - __old__ +# | - __old__ # def __create_trace_i__(self, # x_energy, @@ -12,7 +12,7 @@ # ): # """ # """ - # #| - __create_trace_i__ + # # | - __create_trace_i__ # # if show_data_labels is True: # mode_i = "markers+text" @@ -69,7 +69,7 @@ # ) # # return(trace_i) - # #__| + # # __| # # def __create_volcano_plot__(self, @@ -82,12 +82,12 @@ # smart_format_dict: # Optional dictionary that will format data points # """ - # #| - create_volcano_relations_plot + # # | - create_volcano_relations_plot # # - # #| - __temp__ | out of the way + # # | - __temp__ | out of the way # # - # # #| - Default Smart Format Dict + # # # | - Default Smart Format Dict # # smart_format_dict = self.smart_format_dict # # # # if smart_format_dict is None: @@ -103,15 +103,15 @@ # # [{"facet": "211"}, {"color1": "green"}], # # [{"facet": "100"}, {"color1": "black"}], # # ] - # # #__| + # # # __| # # - # # #| - Processing Data Points + # # # | - Processing Data Points # # x_data_list = [] # # y_data_list = [] # # # # for series_i in self.ORR_Free_E_Plot.series_list: # # - # # #| - x-axis energy + # # # | - x-axis energy # # x_spec = self.x_ax_species # # if x_spec == "o-oh": # # e_o = series_i.energy_states_dict["o"] @@ -119,9 +119,9 @@ # # x_ax_energy = e_o - e_oh # # else: # # x_ax_energy = series_i.energy_states_dict[x_spec] - # # #__| + # # # __| # # - # # #| - y-axis limiting potential + # # # | - y-axis limiting potential # # if self.ORR_Free_E_Plot.rxn_type == "ORR": # # lim_pot_i = 1.23 - series_i.overpotential # # @@ -129,9 +129,9 @@ # # lim_pot_i = 1.23 + series_i.overpotential_OER # # else: # # print("LSDJFlksdj") - # # #__| + # # # __| # # - # # #| - Process series_i + # # # | - Process series_i # # x_data_list.append(x_ax_energy) # # y_data_list.append(lim_pot_i) # # @@ -172,11 +172,11 @@ # # ) # # # # self.data_points.append(trace_i) - # # #__| + # # # __| # # - # # #__| + # # # __| # # - # # #| - Finding plot axis limits + # # # | - Finding plot axis limits # # if self.plot_range is None: # # y_axis_range = [min(y_data_list) - 0.2, max(y_data_list) + 0.2] # # if self.ORR_Free_E_Plot.rxn_type == "OER": @@ -190,11 +190,11 @@ # # } # # # # self.plot_range = plot_range - # # #__| + # # # __| # # - # #__| + # # __| # - # #__| + # # __| # # # # def create_volcano_lines(self, @@ -223,11 +223,11 @@ # plot, if 'bottom' then the data points will by on top of the # volcano # """ - # #| - create_volcano_lines + # # | - create_volcano_lines # out_data = [] # x_range = self.plot_range["x"] # - # #| - Volcano Legs + # # | - Volcano Legs # volc_legs = [ # 'o2_to_ooh', # 'ooh_to_o', @@ -242,7 +242,7 @@ # 'oh_to_h2o': [], # } # - # #| - Create Volcano Legs (LOOP) + # # | - Create Volcano Legs (LOOP) # x_axis = np.linspace(x_range[0], x_range[1], num=500) # for leg_i in volc_legs: # for x_energy_i in x_axis: @@ -266,11 +266,11 @@ # rxn_direction="forward", # ), # ) - # #__| + # # __| # # if plot_all_legs: # - # #| - plot_all_legs + # # | - plot_all_legs # # hoverinfo_type = "none" # hoverinfo_type = "name" # @@ -333,11 +333,11 @@ # out_data.insert(0, trace_ooh_to_o) # out_data.insert(0, trace_o_to_oh) # out_data.insert(0, trace_oh_to_h2o) - # #__| + # # __| # - # #__| + # # __| # - # #| - Minimum Energy Legs + # # | - Minimum Energy Legs # energy_lists= [] # for leg_i in legs_to_plot: # energy_lists.append(energy_dict[leg_i]) @@ -371,15 +371,15 @@ # # elif trace_priority == "bottom": # out_data.insert(0, trace_volcano) - # #__| + # # __| # # return(out_data) - # #__| + # # __| -#__| +# __| -#| - __old__ +# | - __old__ # # # ███████ ██████ ██████ ██ ██████ ████████ @@ -395,7 +395,7 @@ # IDEA: Add vertical lines to connect *O, *OH, and *OOH data points # """ # -# #| - Scaling_Relations_Plot *********************************************** +# # | - Scaling_Relations_Plot *********************************************** # # def __init__(self, # ORR_Free_E_Plot, @@ -420,7 +420,7 @@ # mode: # "all", "ooh_vs_oh", "o_vs_oh" # """ -# #| - __init__ +# # | - __init__ # self.ORR_Free_E_Plot = ORR_Free_E_Plot # # assert (x_ax_species == "oh"), "Only *OH as the x-axis is allowed now" @@ -466,7 +466,7 @@ # # self.annotations_list = [] # -# #__| +# # __| # # def create_scaling_relations_plot(self, # smart_format_dict=None, @@ -477,9 +477,9 @@ # y_ax_spec: # x_ax_spec: # """ -# #| - create_scaling_relations_plot +# # | - create_scaling_relations_plot # -# #| - Default Smart Format Dict +# # | - Default Smart Format Dict # if smart_format_dict is None: # print("No smart format given!") # smart_format_dict = [ @@ -493,9 +493,9 @@ # [{"facet": "211"}, {self.marker_border_color_key: "green"}], # [{"facet": "100"}, {self.marker_border_color_key: "black"}], # ] -# #__| +# # __| # -# #| - Processing Data Points +# # | - Processing Data Points # for series_i in self.ORR_Free_E_Plot.series_list: # # e_oh = series_i.energy_states_dict["oh"] @@ -525,7 +525,7 @@ # smart_format_i = series_i.format_dict # # -# #| - ooh_vs_oh +# # | - ooh_vs_oh # trace_i = self.__create_trace_i__( # e_oh, # e_ooh, @@ -535,9 +535,9 @@ # ) # # self.data_ooh_oh.append(trace_i) # self.data_points["ooh_vs_oh"].append(trace_i) -# #__| +# # __| # -# #| - o_vs_oh +# # | - o_vs_oh # trace_i = self.__create_trace_i__( # e_oh, # e_o, @@ -547,9 +547,9 @@ # ) # # self.data_o_oh.append(trace_i) # self.data_points["o_vs_oh"].append(trace_i) -# #__| +# # __| # -# #| - oh_vs_oh +# # | - oh_vs_oh # trace_i = self.__create_trace_i__( # e_oh, # e_oh, @@ -559,11 +559,11 @@ # ) # # self.data_oh_oh.append(trace_i) # self.data_points["oh_vs_oh"].append(trace_i) -# #__| +# # __| # -# #__| +# # __| # -# #__| +# # __| # # # Deprecated, delete this later # def __create_smart_format_dict__(self, property_dict, smart_format_dict): @@ -573,7 +573,7 @@ # property_dict: # smart_format_dict: # """ -# #| - __create_smart_format_dict__ +# # | - __create_smart_format_dict__ # format_dict = {} # for key_i, value_i in property_dict.items(): # for format_i in smart_format_dict: @@ -582,12 +582,12 @@ # format_dict.update(format_i[1]) # # return(format_dict) -# #__| +# # __| # # def __create_series_name__(self, series_i): # """ # """ -# #| - create_series_name +# # | - create_series_name # name_i = "" # for key, value in series_i.properties.items(): # if key == "coverage": @@ -596,7 +596,7 @@ # name_i += str(key) + ": " + str(value) + " | " # # return(name_i) -# #__| +# # __| # # def __create_trace_i__(self, # x_energy, @@ -607,7 +607,7 @@ # ): # """ # """ -# #| - create_trace_i +# # | - create_trace_i # # NOTE Looks like I need to put these in a list here # x_energy = [x_energy] # y_energy = [y_energy] @@ -635,7 +635,7 @@ # ) # # return(trace_i) -# #__| +# # __| # # # NOTE | This shouldn't be an internal method # def __create_layout__(self, @@ -651,7 +651,7 @@ # title: # showlegend: # """ -# #| - create_layout +# # | - create_layout # # # if x_ax_spec == "" # if self.x_ax_species == "oh": @@ -668,7 +668,7 @@ # # # legend_size = 18 # -# #| - Common Axis Dict +# # | - Common Axis Dict # common_axis_dict = { # # # "range": y_axis_range, @@ -689,9 +689,9 @@ # "ticklen": 2, # "tickwidth": 1, # } -# #__| +# # __| # -# #| - __old__ +# # | - __old__ # # x_range_ooh_vs_oh=[0., 3.5], # # y_range_ooh_vs_oh=[0., 5.], # # x_range_o_vs_oh=[0., 3.5], @@ -714,7 +714,7 @@ # # y_range = self.y_range_oh_vs_oh # # else: # # print("Woops - create_layout") -# #__| +# # __| # # x_range = self.x_range # y_range = self.y_range @@ -772,7 +772,7 @@ # # layout_i = {**layout_i, **layout_dict} # # return(layout_i) -# #__| +# # __| # # def __series_excluded__(self, # properties_i, @@ -788,7 +788,7 @@ # properties_i: # exclude_dict: # """ -# #| - series_excluded +# # | - series_excluded # exclude_dict_keys = list(exclude_dict.keys()) # properties_i_keys = list(properties_i.keys()) # @@ -816,7 +816,7 @@ # # return(all_props_match) # -# #__| +# # __| # # def fit_scaling_lines(self, # dependent_species, # 'ooh', 'o', 'oh' @@ -828,14 +828,14 @@ # dependent_species: # y-axis species 'ooh' or 'o' # """ -# #| - fit_scaling_lines +# # | - fit_scaling_lines # -# #| - LOOP +# # | - LOOP # oh_list = [] # dependent_e_list = [] # for series_i in self.ORR_Free_E_Plot.series_list: # -# #| - Excluding series from fitting +# # | - Excluding series from fitting # if exclude_dict is not None: # properties_i = series_i.properties # exclude_series = self.__series_excluded__( @@ -844,13 +844,13 @@ # ) # if exclude_series: # continue -# #__| +# # __| # # energy_i = series_i.energy_states_dict[dependent_species] # dependent_e_list.append(energy_i) # oh_list.append(series_i.energy_states_dict["oh"]) # -# #__| +# # __| # # X = np.array([[i] for i in oh_list]) # y = np.array(dependent_e_list) @@ -873,7 +873,7 @@ # } # # print("_------__)_Z(*XF(8))") # -# #| - Equation Annotations +# # | - Equation Annotations # if dependent_species == "ooh": # eqn_str_i = ("" + # "GOOH=" + @@ -923,15 +923,15 @@ # ) # # self.annotations_list.append(annotation_i) -# #__| +# # __| # # # return(out) -# #__| +# # __| # # def add_ideal_lines(self): # """Add ideal scaling liknes to plot.""" -# #| - add_ideal_lines +# # | - add_ideal_lines # self.add_line({"slope": 1, "intercept": 3.2}, # name="*OOH vs *OH Scaling", # color="black", @@ -952,7 +952,7 @@ # width=1, # dash="dash", # ) -# #__| +# # __| # # def add_line(self, # slope_intercept_dict, @@ -970,7 +970,7 @@ # width: # dash: # """ -# #| - add_line +# # | - add_line # # # print(slope_intercept_dict) # @@ -980,11 +980,11 @@ # def scaling_meth(E_OH): # """ # """ -# #| - scaling_meth +# # | - scaling_meth # out = slope * E_OH + intercept # # return(out) -# #__| +# # __| # # LH_bound = self.x_range[0] # RH_bound = self.x_range[1] @@ -1026,22 +1026,22 @@ # # "" # # ) # -# #__| +# # __| # # # # # -# #| - __old__ +# # | - __old__ # # def __ideal_ooh_oh_scaling__(self, E_OH): # # """Return the *OOH adsorption energy given DG_*OH by scaling. # # # # Args: # # E_OH:DG_*OH energy of adsorption # # """ -# # #| - __ideal_ooh_oh_scaling__ +# # # | - __ideal_ooh_oh_scaling__ # # return(E_OH + 3.2) -# # #__| +# # # __| # # # # def __ideal_h_oh_scaling__(self, E_OH): # # """Return the *OOH adsorption energy given DG_*OH by scaling. @@ -1049,9 +1049,9 @@ # # Args: # # E_OH: DG_*OH energy of adsorption. # # """ -# # #| - __ideal_h_oh_scaling__ +# # # | - __ideal_h_oh_scaling__ # # return(2 * E_OH) -# # #__| +# # # __| # # # # def __ideal_oh_oh_scaling__(self, E_OH): # # """Return the *OH adsorption energy given DG_*OH by scaling. @@ -1061,13 +1061,13 @@ # # Args: # # E_OH: DG_*OH energy of adsorption. # # """ -# # #| - __ideal_oh_oh_scaling__ +# # # | - __ideal_oh_oh_scaling__ # # return(E_OH) -# # #__| +# # # __| # # -# #__| +# # __| # -# #__| ********************************************************************** +# # __| ********************************************************************** @@ -1085,7 +1085,7 @@ # TEMP # """ # -# #| - Volcano_Plot ********************************************************* +# # | - Volcano_Plot ********************************************************* # # def __init__(self, # ORR_Free_E_Plot, @@ -1111,7 +1111,7 @@ # } # # """ -# #| - __init__ +# # | - __init__ # self.ORR_Free_E_Plot = ORR_Free_E_Plot # self.x_ax_species = x_ax_species # self.plot_range = plot_range @@ -1124,7 +1124,7 @@ # self.marker_border_color_key = marker_border_color_key # self.marker_shape_key = marker_shape_key # -# #__| +# # __| # # # NOTE | Rename this create_volcano_plot # def create_volcano_relations_plot(self, @@ -1137,9 +1137,9 @@ # smart_format_dict: # Optional dictionary that will format data points # """ -# #| - create_volcano_relations_plot +# # | - create_volcano_relations_plot # -# #| - Default Smart Format Dict +# # | - Default Smart Format Dict # smart_format_dict = self.smart_format_dict # # if smart_format_dict is None: @@ -1155,15 +1155,15 @@ # [{"facet": "211"}, {"color1": "green"}], # [{"facet": "100"}, {"color1": "black"}], # ] -# #__| +# # __| # -# #| - Processing Data Points +# # | - Processing Data Points # x_data_list = [] # y_data_list = [] # # for series_i in self.ORR_Free_E_Plot.series_list: # -# #| - x-axis energy +# # | - x-axis energy # x_spec = self.x_ax_species # if x_spec == "o-oh": # e_o = series_i.energy_states_dict["o"] @@ -1171,9 +1171,9 @@ # x_ax_energy = e_o - e_oh # else: # x_ax_energy = series_i.energy_states_dict[x_spec] -# #__| +# # __| # -# #| - y-axis limiting potential +# # | - y-axis limiting potential # if self.ORR_Free_E_Plot.rxn_type == "ORR": # lim_pot_i = 1.23 - series_i.overpotential # @@ -1181,9 +1181,9 @@ # lim_pot_i = 1.23 + series_i.overpotential_OER # else: # print("LSDJFlksdj") -# #__| +# # __| # -# #| - Process series_i +# # | - Process series_i # x_data_list.append(x_ax_energy) # y_data_list.append(lim_pot_i) # @@ -1214,11 +1214,11 @@ # ) # # self.data_points.append(trace_i) -# #__| +# # __| # -# #__| +# # __| # -# #| - Finding plot axis limits +# # | - Finding plot axis limits # if self.plot_range is None: # y_axis_range = [min(y_data_list) - 0.2, max(y_data_list) + 0.2] # if self.ORR_Free_E_Plot.rxn_type == "OER": @@ -1232,9 +1232,9 @@ # } # # self.plot_range = plot_range -# #__| +# # __| # -# #__| +# # __| # # def create_volcano_lines(self, # gas_molec_dict=None, @@ -1262,11 +1262,11 @@ # plot, if 'bottom' then the data points will by on top of the # volcano # """ -# #| - create_volcano_lines +# # | - create_volcano_lines # out_data = [] # x_range = self.plot_range["x"] # -# #| - Volcano Legs +# # | - Volcano Legs # volc_legs = [ # 'o2_to_ooh', # 'ooh_to_o', @@ -1281,7 +1281,7 @@ # 'oh_to_h2o': [], # } # -# #| - Create Volcano Legs (LOOP) +# # | - Create Volcano Legs (LOOP) # x_axis = np.linspace(x_range[0], x_range[1], num=500) # for leg_i in volc_legs: # for x_energy_i in x_axis: @@ -1305,11 +1305,11 @@ # rxn_direction="forward", # ), # ) -# #__| +# # __| # # if plot_all_legs: # -# #| - plot_all_legs +# # | - plot_all_legs # # hoverinfo_type = "none" # hoverinfo_type = "name" # @@ -1372,11 +1372,11 @@ # out_data.insert(0, trace_ooh_to_o) # out_data.insert(0, trace_o_to_oh) # out_data.insert(0, trace_oh_to_h2o) -# #__| +# # __| # -# #__| +# # __| # -# #| - Minimum Energy Legs +# # | - Minimum Energy Legs # energy_lists= [] # for leg_i in legs_to_plot: # energy_lists.append(energy_dict[leg_i]) @@ -1410,10 +1410,10 @@ # # elif trace_priority == "bottom": # out_data.insert(0, trace_volcano) -# #__| +# # __| # # return(out_data) -# #__| +# # __| # # def __create_trace_i__(self, # x_energy, @@ -1426,7 +1426,7 @@ # ): # """ # """ -# #| - __create_trace_i__ +# # | - __create_trace_i__ # # if show_data_labels is True: # mode_i = "markers+text" @@ -1483,7 +1483,7 @@ # ) # # return(trace_i) -# #__| +# # __| # # def get_plotly_layout(self, # showlegend=False, @@ -1493,9 +1493,9 @@ # ): # """ # """ -# #| - get_plotly_layout +# # | - get_plotly_layout # -# #| - Properties +# # | - Properties # # plot_title="FED" # plot_title = None # # plot_title_size = 18 @@ -1505,7 +1505,7 @@ # legend_size = 18 # # font_family="Computer Modern" # "Courier New, monospace" # font_family = "Arial" # "Courier New, monospace" -# #__| +# # __| # # # self.x_ax_spec # @@ -1532,9 +1532,9 @@ # "color": "black", # }, # -# #| - Axes ----------------------------------------------------- +# # | - Axes ----------------------------------------------------- # -# #| - yaxis +# # | - yaxis # "yaxis": { # "title": "Limiting Potential (V)", # # "title": "$\\Delta G (ev)$", @@ -1559,9 +1559,9 @@ # "ticklen": 2, # "tickwidth": 1, # }, -# #__| +# # __| # -# #| - xaxis +# # | - xaxis # "xaxis": { # # "title": "$\\Delta G_{OH} (ev)$", # "title": xaxis_title, @@ -1584,9 +1584,9 @@ # size=tick_lab_size, # ), # }, -# #__| +# # __| # -# #__| +# # __| # # "margin": go.layout.Margin( # b=50., @@ -1598,7 +1598,7 @@ # # "paper_bgcolor": 'rgba(0,0,0,0)', # "plot_bgcolor": 'rgba(0,0,0,0)', # -# #| - Legend --------------------------------------------------- +# # | - Legend --------------------------------------------------- # "legend": { # "traceorder": "normal", # "font": dict(size=legend_size), @@ -1611,11 +1611,11 @@ # # "showlegend": False, # "showlegend": showlegend, # -# #__| +# # __| # # } # -# #| - Plot Size Settings +# # | - Plot Size Settings # # bottom_margin_size = 2.5 * 9. * 37.795275591 # plot_size_settings = { # "width": width, @@ -1635,23 +1635,23 @@ # # }), # } # -# #__| +# # __| # # layout = {**layout, **plot_size_settings} # -# #| - Applying Layout override dict +# # | - Applying Layout override dict # if layout_dict is not None: # from misc_modules.misc_methods import dict_merge # dict_merge(layout, layout_dict) # # # layout_i = {**layout_i, **layout_dict} # -# #__| +# # __| # # return(layout) # -# #__| +# # __| # # -# #__| ********************************************************************** -#__| +# # __| ********************************************************************** +# __| diff --git a/oxr_reaction/adsorbate_scaling.py b/oxr_reaction/adsorbate_scaling.py index 072e1ef..7d18076 100644 --- a/oxr_reaction/adsorbate_scaling.py +++ b/oxr_reaction/adsorbate_scaling.py @@ -6,7 +6,7 @@ """ -#| - IMPORT MODULES +# | - IMPORT MODULES # import numpy as np # # import pandas as pd @@ -19,7 +19,7 @@ # # # from orr_reaction.orr_series import ORR_Free_E_Series -#__| +# __| class Adsorbate_Scaling: @@ -28,53 +28,53 @@ class Adsorbate_Scaling: Development Notes: """ - #| - Adsorbate_Scaling **************************************************** + # | - Adsorbate_Scaling **************************************************** def __init__(self, tmp=42 ): """ """ - #| - __init__ + # | - __init__ self.tmp = tmp - #__| + # __| def tmp_meth(self, ): """ """ - #| - tmp + # | - tmp tmp = 42 return(tmp) - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** def get_g_ooh(m_ooh, b_ooh, g_oh): """ """ - #| - get_g_ooh + # | - get_g_ooh g_ooh = m_ooh * g_oh + b_ooh return(g_ooh) - #__| + # __| def get_g_o(m_o, b_o, g_oh): """ """ - #| - get_g_o + # | - get_g_o g_o = m_o * g_oh + b_o return(g_o) - #__| + # __| def get_g_oh(m_oh, b_oh, g_oh): """ """ - #| - get_g_oh + # | - get_g_oh g_oh = m_oh * g_oh + b_oh return(g_oh) - #__| + # __| def lim_U_i( g_oh=None, @@ -95,9 +95,9 @@ def lim_U_i( scaling_dict: rxn_direction: """ - #| - lim_U_i + # | - lim_U_i - #| - Checking Input Types + # | - Checking Input Types if g_oh is None and g_o_minus_g_oh is None: raise ValueError("Need to provide either g_oh or g_o_minus_g_oh") @@ -107,9 +107,9 @@ def lim_U_i( assert gas_molec_dict is not None, "Please provide gas_molec_dict" assert scaling_dict is not None, "Please provide the scaling_dict" assert mech_step is not None, "Please provide the step to calculate" - #__| + # __| - #| - linear fit and gas molecule data + # | - linear fit and gas molecule data m_ooh = scaling_dict["ooh"]["m"] b_ooh = scaling_dict["ooh"]["b"] @@ -123,7 +123,7 @@ def lim_U_i( g_o2 = gas_molec_dict["o2"] g_h2 = gas_molec_dict["h2"] g_h2o = gas_molec_dict["h2o"] - #__| + # __| if g_o_minus_g_oh is not None: """ @@ -136,7 +136,7 @@ def lim_U_i( elif g_oh is not None: g_oh = g_oh - #| - Calculating Limiting Potential for all legs + # | - Calculating Limiting Potential for all legs if mech_step == "o2_to_ooh": lim_U_out = get_g_ooh(m_ooh, b_ooh, g_oh) + \ - g_o2 @@ -155,7 +155,7 @@ def lim_U_i( - get_g_oh(m_oh, b_oh, g_oh) else: raise ValueError("Woops, error here (9sdfijsd9)") - #__| + # __| if rxn_direction == "forward": lim_U_out = - lim_U_out @@ -163,12 +163,12 @@ def lim_U_i( lim_U_out = + lim_U_out return(lim_U_out) - #__| + # __| -#| - __old__ +# | - __old__ # def lim_U_o2_to_ooh(g_oh, # gas_molec_dict, scaling_dict, rxn_direction="forward"): @@ -182,9 +182,9 @@ def lim_U_i( # scaling_dict: # rxn_direction: # """ -# #| - lim_U_o2_to_ooh +# # | - lim_U_o2_to_ooh # -# #| - linear fit and gas molecule data +# # | - linear fit and gas molecule data # m_ooh = scaling_dict["ooh"]["m"] # b_ooh = scaling_dict["ooh"]["b"] # @@ -198,7 +198,7 @@ def lim_U_i( # g_o2 = gas_molec_dict["o2"] # g_h2 = gas_molec_dict["h2"] # g_h2o = gas_molec_dict["h2o"] -# #__| +# # __| # # # if False: @@ -213,7 +213,7 @@ def lim_U_i( # lim_U_out = + lim_U_out # # return(lim_U_out) -# #__| +# # __| # # def lim_U_ooh_to_o(g_oh, # gas_molec_dict, scaling_dict, rxn_direction="forward"): @@ -227,9 +227,9 @@ def lim_U_i( # scaling_dict: # rxn_direction: # """ -# #| - lim_U_ooh_to_o +# # | - lim_U_ooh_to_o # -# #| - linear fit and gas molecule data +# # | - linear fit and gas molecule data # m_ooh = scaling_dict["ooh"]["m"] # b_ooh = scaling_dict["ooh"]["b"] # @@ -243,7 +243,7 @@ def lim_U_i( # g_o2 = gas_molec_dict["o2"] # g_h2 = gas_molec_dict["h2"] # g_h2o = gas_molec_dict["h2o"] -# #__| +# # __| # # lim_U_out = get_g_o(m_o, # b_o, g_oh) + g_h2o - get_g_ooh(m_ooh, b_ooh, g_oh) @@ -254,7 +254,7 @@ def lim_U_i( # lim_U_out = + lim_U_out # # return(lim_U_out) -# #__| +# # __| # # def lim_U_o_to_oh(g_oh, # gas_molec_dict, scaling_dict, rxn_direction="forward"): @@ -268,9 +268,9 @@ def lim_U_i( # scaling_dict: # rxn_direction: # """ -# #| - lim_U_o_to_oh +# # | - lim_U_o_to_oh # -# #| - linear fit and gas molecule data +# # | - linear fit and gas molecule data # m_ooh = scaling_dict["ooh"]["m"] # b_ooh = scaling_dict["ooh"]["b"] # @@ -284,7 +284,7 @@ def lim_U_i( # g_o2 = gas_molec_dict["o2"] # g_h2 = gas_molec_dict["h2"] # g_h2o = gas_molec_dict["h2o"] -# #__| +# # __| # # lim_U_out = get_g_oh(m_oh, b_oh, g_oh) - get_g_o(m_o, b_o, g_oh) # @@ -294,7 +294,7 @@ def lim_U_i( # lim_U_out = + lim_U_out # # return(lim_U_out) -# #__| +# # __| # # def lim_U_oh_to_h2o(g_oh, # gas_molec_dict, scaling_dict, rxn_direction="forward"): @@ -308,9 +308,9 @@ def lim_U_i( # scaling_dict: # rxn_direction: # """ -# #| - lim_U_oh_to_h2o +# # | - lim_U_oh_to_h2o # -# #| - linear fit and gas molecule data +# # | - linear fit and gas molecule data # m_ooh = scaling_dict["ooh"]["m"] # b_ooh = scaling_dict["ooh"]["b"] # @@ -324,7 +324,7 @@ def lim_U_i( # g_o2 = gas_molec_dict["o2"] # g_h2 = gas_molec_dict["h2"] # g_h2o = gas_molec_dict["h2o"] -# #__| +# # __| # # lim_U_out = g_h2o - get_g_oh(m_oh, b_oh, g_oh) # @@ -334,6 +334,6 @@ def lim_U_i( # lim_U_out = + lim_U_out # # return(lim_U_out) -# #__| +# # __| # -#__| +# __| diff --git a/oxr_reaction/oxr_methods.py b/oxr_reaction/oxr_methods.py index 7f56a15..c362d7f 100644 --- a/oxr_reaction/oxr_methods.py +++ b/oxr_reaction/oxr_methods.py @@ -5,7 +5,7 @@ Author: Raul A. Flores """ -#| - IMPORT MODULES +# | - IMPORT MODULES import copy import numpy as np import pandas as pd @@ -15,9 +15,9 @@ pd.options.mode.chained_assignment = None from oxr_reaction.oxr_series import ORR_Free_E_Series -#__| +# __| -#| - __old__ +# | - __old__ def plotly_fed_layout( plot_title="FED", plot_title_size=18, @@ -27,7 +27,7 @@ def plotly_fed_layout( ): """ """ - #| - plotly_fed_layout + # | - plotly_fed_layout xax_labels = ["O2", "OOH", "O", "OH", "H2O"] layout = { @@ -39,7 +39,7 @@ def plotly_fed_layout( "color": "black", }, - #| - Axes -------------------------------------------------------------- + # | - Axes -------------------------------------------------------------- "yaxis": { "title": "Free Energy [eV]", "zeroline": True, @@ -65,27 +65,27 @@ def plotly_fed_layout( size=tick_lab_size, ), }, - #__| ------------------------------------------------------------------- + # __| ------------------------------------------------------------------- - #| - Legend ------------------------------------------------------------ + # | - Legend ------------------------------------------------------------ "legend": { "traceorder": "normal", "font": dict(size=legend_size) }, - #__| ------------------------------------------------------------------- + # __| ------------------------------------------------------------------- - #| - Plot Size + # | - Plot Size # "width": 200 * 4., # "height": 200 * 3., - #__| + # __| } return(layout) - #__| + # __| -#__| +# __| def calc_ads_e( @@ -106,13 +106,13 @@ def calc_ads_e( oxy_ref_e: hyd_ref_e: """ - #| - calc_ads_e + # | - calc_ads_e row = df_row bare_slab = bare_raw_e oxy_ref = oxy_ref_e hyd_ref = hyd_ref_e - #| - Oxygen & Hydrogen Atom Count + # | - Oxygen & Hydrogen Atom Count atoms_col = "atom_type_num_dict" if atoms_col in list(row.index): try: @@ -139,7 +139,7 @@ def calc_ads_e( elif row["adsorbate"] == "bare": num_O = 0 num_H = 0 - #__| + # __| # print("oxy_ref: ", oxy_ref) # print("hyd_ref:", hyd_ref) @@ -155,7 +155,7 @@ def calc_ads_e( ads_e_i = None return(ads_e_i) - #__| + # __| def df_calc_adsorption_e( df, @@ -176,12 +176,12 @@ def df_calc_adsorption_e( Args: df: """ - #| - df_calc_adsorption_e + # | - df_calc_adsorption_e ads_e_list = [] for index, row in df.iterrows(): bare_e = bare_slab_e - #| - Correction + # | - Correction corr = 0. # corr = fe_corr_dict[row["adsorbate"]] @@ -198,7 +198,7 @@ def df_calc_adsorption_e( else: print("No correction being applied") corr = 0. - #__| + # __| if type(bare_slab_e) == dict: bare_e = bare_slab_e[row[bare_slab_var]] @@ -217,7 +217,7 @@ def df_calc_adsorption_e( ads_e_list.append(ads_e_i) df["ads_e"] = np.array(ads_e_list) - #__| + # __| def lowest_e_path( df, @@ -245,9 +245,9 @@ def lowest_e_path( bias: """ - #| - lowest_e_path + # | - lowest_e_path - #| - Grouping By Adsorbate Type + # | - Grouping By Adsorbate Type df = copy.deepcopy(df) groupby = copy.deepcopy(jobs_variables) @@ -276,11 +276,11 @@ def lowest_e_path( df_i = pd.DataFrame.from_items([(s.name, s) for s in series_list]).T data_master[group_i[0]] = df_i - #__| + # __| - #| - Creating Data Sets + # | - Creating Data Sets - #| - Creating FED Datasets + # | - Creating FED Datasets data_list = [] # for i_cnt, (key, fe_dict) in enumerate(data_master.iteritems()): for i_cnt, (key, fe_dict) in enumerate(data_master.items()): @@ -300,9 +300,9 @@ def lowest_e_path( ) data_list.extend(dat_lst) - #__| + # __| - #| - Creating Ideal FED Dataset + # | - Creating Ideal FED Dataset if create_ideal_series: e_list_ideal = ORR.apply_bias(bias, ORR.ideal_energy) @@ -321,22 +321,22 @@ def lowest_e_path( dat_lst = data_list - #__| + # __| # dat_lst = data_list + dat_ideal - #__| + # __| - #| - Plotting + # | - Plotting - #| - Plot Settings + # | - Plot Settings plot_title_size = 18 tick_lab_size = 16 axes_lab_size = 18 legend_size = 18 - #__| + # __| - #| - Plot Layout + # | - Plot Layout # xax_labels = ["O2", "OOH", "O", "OH", "H2O"] # layout = { # @@ -348,7 +348,7 @@ def lowest_e_path( # "color": "black", # }, # - # #| - Axes -------------------------------------------------------------- + # # | - Axes -------------------------------------------------------------- # "yaxis": { # "title": "Free Energy [eV]", # "zeroline": True, @@ -374,30 +374,30 @@ def lowest_e_path( # size=tick_lab_size, # ), # }, - # #__| ------------------------------------------------------------------- + # # __| ------------------------------------------------------------------- # - # #| - Legend ------------------------------------------------------------ + # # | - Legend ------------------------------------------------------------ # "legend": { # "traceorder": "normal", # "font": dict(size=legend_size) # }, - # #__| ------------------------------------------------------------------- + # # __| ------------------------------------------------------------------- # - # #| - Plot Size + # # | - Plot Size # # "width": 200 * 4., # # "height": 200 * 3., - # #__| + # # __| # # } - #__| + # __| layout = plotly_fed_layout(plot_title=plot_title) - #__| + # __| return(dat_lst, layout) - #__| + # __| def plot_all_states( df, @@ -418,9 +418,9 @@ def plot_all_states( bias: plot_title: """ - #| - plot_all_states + # | - plot_all_states - #| - Grouping By Adsorbate Type + # | - Grouping By Adsorbate Type groupby = copy.deepcopy(jobs_variables) # groupby = copy.deepcopy(Jojobs_variablesbs.tree_level_labels) @@ -430,11 +430,11 @@ def plot_all_states( for group_i in df.groupby(groupby): data_master[group_i[0]] = group_i[1] - #__| + # __| - #| - Creating Data Sets + # | - Creating Data Sets - #| - Creating FED Datasets + # | - Creating FED Datasets data_list = [] # for i_cnt, (key, fe_dict) in enumerate(data_master.iteritems()): for i_cnt, (key, fe_dict) in enumerate(data_master.items()): @@ -454,9 +454,9 @@ def plot_all_states( ) data_list.extend(dat_lst) - #__| + # __| - #| - Creating Ideal FED Dataset + # | - Creating Ideal FED Dataset if create_ideal_series: e_list_ideal = ORR.apply_bias(bias, ORR.ideal_energy) @@ -470,23 +470,23 @@ def plot_all_states( ) dat_lst = data_list + dat_ideal - #__| + # __| else: dat_lst = data_list - #__| + # __| - #| - Plotting + # | - Plotting - #| - Plot Settings + # | - Plot Settings plot_title_size = 18 tick_lab_size = 16 axes_lab_size = 18 legend_size = 12 - #__| + # __| - #| - Plot Layout + # | - Plot Layout # xax_labels = ["O2", "OOH", "O", "OH", "H2O"] # layout = { # @@ -498,7 +498,7 @@ def plot_all_states( # "color": "black", # }, # - # #| - Axes -------------------------------------------------------------- + # # | - Axes -------------------------------------------------------------- # "yaxis": { # "title": "Free Energy [eV]", # "zeroline": True, @@ -524,27 +524,27 @@ def plot_all_states( # size=tick_lab_size, # ), # }, - # #__| ------------------------------------------------------------------- + # # __| ------------------------------------------------------------------- # - # #| - Legend ------------------------------------------------------------ + # # | - Legend ------------------------------------------------------------ # "legend": { # "traceorder": "normal", # "font": dict(size=legend_size) # }, - # #__| ------------------------------------------------------------------- + # # __| ------------------------------------------------------------------- # - # #| - Plot Size + # # | - Plot Size # "width": 200 * 4., # "height": 200 * 3., - # #__| + # # __| # # } - #__| + # __| layout = plotly_fed_layout(plot_title=plot_title) return(dat_lst, layout) - #__| + # __| - #__| + # __| diff --git a/oxr_reaction/oxr_plotting_classes/oxr_plot_2d_volcano.py b/oxr_reaction/oxr_plotting_classes/oxr_plot_2d_volcano.py index f44e96e..7301688 100644 --- a/oxr_reaction/oxr_plotting_classes/oxr_plot_2d_volcano.py +++ b/oxr_reaction/oxr_plotting_classes/oxr_plot_2d_volcano.py @@ -5,7 +5,7 @@ Author: Raul A. Flores """ -#| - IMPORT MODULES +# | - IMPORT MODULES import numpy as np import pandas as pd import copy @@ -18,7 +18,7 @@ from oxr_reaction.oxr_series import ORR_Free_E_Series from oxr_reaction.adsorbate_scaling import lim_U_i -#__| +# __| # ██ ██ ██████ ██ ██████ ██████ ██ ██████ ████████ @@ -34,7 +34,7 @@ class Volcano_Plot_2D(): TEMP """ - #| - Volcano_Plot ********************************************************* + # | - Volcano_Plot ********************************************************* def __init__(self, ORR_Free_E_Plot, @@ -42,7 +42,7 @@ def __init__(self, ): """ """ - #| - __init__ + # | - __init__ self.ORR_Free_E_Plot = ORR_Free_E_Plot if plot_range is None: @@ -64,12 +64,12 @@ def __init__(self, data += self.data_point_traces data.insert(0, self.contour_trace) self.traces = data - #__| + # __| def __create_contour_trace__(self): """ """ - #| - __create_contour_trace__ + # | - __create_contour_trace__ x_range_bounds = self.plot_range["x"] y_range_bounds = self.plot_range["y"] @@ -114,15 +114,15 @@ def __create_contour_trace__(self): # ncontours=180, ncontours=20, - #| - Color Scale + # | - Color Scale colorscale='Jet', reversescale=True, autocontour=True, # showscale=False, - #__| + # __| - #| - Colorbar + # | - Colorbar colorbar=go.contour.ColorBar( # x=None, # xanchor=None, @@ -145,9 +145,9 @@ def __create_contour_trace__(self): outlinecolor="black", outlinewidth=1., ), - #__| + # __| - #| - Line + # | - Line line=go.contour.Line( color="white", # dash="dot", @@ -156,24 +156,24 @@ def __create_contour_trace__(self): width=0., ) ) - #__| + # __| return(series_i) - #__| + # __| def __create_data_point_traces__(self): """ """ - #| - __create_data_point_traces__ + # | - __create_data_point_traces__ data_list = [] for sys_i in self.ORR_Free_E_Plot.series_list: trace_i = self.__create_scatter_trace_i__(sys_i) data_list.append(trace_i) return(data_list) - #__| + # __| def __create_scatter_trace_i__(self, sys_i, @@ -181,7 +181,7 @@ def __create_scatter_trace_i__(self, ): """ """ - #| - __create_trace_i__ + # | - __create_trace_i__ trace_i = go.Scatter( x=[sys_i.energy_states_dict["o"] - sys_i.energy_states_dict["oh"]], y=[sys_i.energy_states_dict["oh"]], @@ -205,7 +205,7 @@ def __create_scatter_trace_i__(self, ) return(trace_i) - #__| + # __| @@ -221,7 +221,7 @@ def __create_scatter_trace_i__(self, def ooh_oh_scaling(self, doh): """ooh_oh_scaling equation.""" - #| - ooh_oh_scaling + # | - ooh_oh_scaling #like ambars #dooh=0.5*doh + 3.0 #O #normal one @@ -239,7 +239,7 @@ def ooh_oh_scaling(self, doh): dooh = m * doh + b # TEMP return(dooh) - #__| + # __| def overpotential3(self, x, doh): """Calculate overpotential (version 3). @@ -248,14 +248,14 @@ def overpotential3(self, x, doh): x: doh: """ - #| - overpotential3 + # | - overpotential3 dooh = self.ooh_oh_scaling(doh) dg14 = [doh, x, dooh - (x + doh), -dooh + 4.92] m = max(dg14) return(m - 1.23) #return doh*do - #__| + # __| # ######################################################################### # ######################################################################### @@ -270,7 +270,7 @@ def get_plotly_layout(self, ): """ """ - #| - get_plotly_layout + # | - get_plotly_layout y_range = self.plot_range["y"] x_range = self.plot_range["x"] @@ -281,16 +281,16 @@ def get_plotly_layout(self, "title": None, - #| - Font Settings + # | - Font Settings "font": { "family": "Arial", # "Courier New, monospace" "color": "black", }, - #__| + # __| - #| - Axes --------------------------------------------------------- + # | - Axes --------------------------------------------------------- - #| - yaxis + # | - yaxis "yaxis": { "title": "ΔGOH (eV)", @@ -318,9 +318,9 @@ def get_plotly_layout(self, "ticklen": 2, "tickwidth": 1, }, - #__| + # __| - #| - xaxis + # | - xaxis "xaxis": { "title": "ΔGO - ΔGOH (eV)", "range": x_range, @@ -345,20 +345,20 @@ def get_plotly_layout(self, size=tick_lab_size, ), }, - #__| + # __| - #__| + # __| - #| - Margins ------------------------------------------------------ + # | - Margins ------------------------------------------------------ "margin": go.layout.Margin( b=50., l=50., r=30., t=30., ), - #__| + # __| - #| - Legend --------------------------------------------------- + # | - Legend --------------------------------------------------- "legend": go.layout.Legend( x=1.2, xanchor=None, @@ -382,12 +382,12 @@ def get_plotly_layout(self, "showlegend": True, # "showlegend": showlegend, - #__| + # __| - #| - Plot Size + # | - Plot Size "width": 37 * 37.795275591, "height": 23 * 37.795275591, - #__| + # __| "paper_bgcolor": 'rgba(255,255,255,1.)', # "plot_bgcolor": 'rgba(3,3,3,0.3)', @@ -395,7 +395,7 @@ def get_plotly_layout(self, } - #| - Plot Size Settings + # | - Plot Size Settings # # bottom_margin_size = 2.5 * 9. * 37.795275591 # plot_size_settings = { # @@ -406,7 +406,7 @@ def get_plotly_layout(self, # # "height": 17 * 37.795275591, # } # layout = {**layout, **plot_size_settings} - #__| + # __| layout = go.Layout(**layout) @@ -419,6 +419,6 @@ def get_plotly_layout(self, return(layout) # COMBAK - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** diff --git a/oxr_reaction/oxr_plotting_classes/oxr_plot_fed.py b/oxr_reaction/oxr_plotting_classes/oxr_plot_fed.py index 114e6b2..e783ef8 100644 --- a/oxr_reaction/oxr_plotting_classes/oxr_plot_fed.py +++ b/oxr_reaction/oxr_plotting_classes/oxr_plot_fed.py @@ -5,7 +5,7 @@ Author: Raul A. Flores """ -#| - IMPORT MODULES +# | - IMPORT MODULES import numpy as np import pandas as pd import copy @@ -18,7 +18,7 @@ from oxr_reaction.oxr_series import ORR_Free_E_Series from oxr_reaction.adsorbate_scaling import lim_U_i -#__| +# __| # ███████ ███████ ██████ ██ ██████ ████████ @@ -33,7 +33,7 @@ class Free_Energy_Plot(): Take the FED methods out of the ORR_Free_E_Plot class and into this one """ - #| - Free_Energy_Plot ***************************************************** + # | - Free_Energy_Plot ***************************************************** def __init__(self, ORR_Free_E_Plot, @@ -57,7 +57,7 @@ def __init__(self, mode: "all", "ooh_vs_oh", "o_vs_oh" """ - #| - __init__ + # | - __init__ self.bias = bias self.opt_name = opt_name self.properties = properties @@ -84,14 +84,14 @@ def __init__(self, ) self.mid_state_x_array = self.create_mid_state_x_array() - #__| + # __| def create_fed_plot(self, ): """ """ - #| - create_fed_plot + # | - create_fed_plot plot_data = [] for series_i in self.ORR_Free_E_Plot.series_list: @@ -112,12 +112,12 @@ def create_fed_plot(self, plot_data += data_i return(plot_data) - #__| + # __| def ideal_ORR_series(self): """ """ - #| - ideal_ORR_series + # | - ideal_ORR_series # self.ideal_energy = [4.92, 3.69, 2.46, 1.23, 0] ideal_data_list = [ @@ -156,7 +156,7 @@ def ideal_ORR_series(self): color=None, ) - #__| + # __| def create_rxn_coord_array(self, rxn_steps, @@ -176,7 +176,7 @@ def create_rxn_coord_array(self, Spacing inbetween the energy levels. The default of 0 creates a free energy diagram that looks like steps """ - #| - create_rxn_coord_array + # | - create_rxn_coord_array lst = [] for i in range(1, rxn_steps): if i == 1: @@ -190,7 +190,7 @@ def create_rxn_coord_array(self, lst.append(lst[-1] + step_size) return(lst) - #__| + # __| def plot_fed_series(self, @@ -220,7 +220,7 @@ def plot_fed_series(self, #FIXME | This is fairly rough as of right now """ - #| - plot_fed_series + # | - plot_fed_series e_list = series_i.energy_lst e_list = series_i.apply_bias(bias, e_list) @@ -234,7 +234,7 @@ def plot_fed_series(self, # name_i = self.series_name name_i = series_i.series_name - #| - Hover Text + # | - Hover Text if hover_text_col is not None: if type(hover_text_col) is not list: hover_text_list = self.property_list(hover_text_col) @@ -261,14 +261,14 @@ def plot_fed_series(self, else: hover_text_list = [np.nan for j_cnt in list(range(5))] - #__| + # __| - #| - TEMP Picking color from "color_list" or "color" variable + # | - TEMP Picking color from "color_list" or "color" variable if series_i.color is not None: color_i = self.color else: color_i = color_list[i_cnt - 1] - #__| + # __| dat_lst = self.__create_plotly_series__( series_i, @@ -284,7 +284,7 @@ def plot_fed_series(self, return(dat_lst) - #__| + # __| def __create_plotly_series__(self, @@ -310,23 +310,23 @@ def __create_plotly_series__(self, "states_only" "full_lines" """ - #| - create_plotly_series + # | - create_plotly_series y_dat = self.__convert_to_plotting_list__(energy_lst) if hover_text is None: hover_text = [np.nan for i_ind in range(5)] - #| - Parameters + # | - Parameters if plot_mode == "all": show_leg_2 = False elif plot_mode == "states_only": show_leg_2 = False elif plot_mode == "full_lines": show_leg_2 = True - #__| + # __| - #| - Adding Breaks in Data + # | - Adding Breaks in Data x_dat = self.rxn_x_coord_array new_x_dat = copy.copy(x_dat) @@ -338,9 +338,9 @@ def __create_plotly_series__(self, new_x_dat.insert(cnt, fill) new_y_dat.insert(cnt, None) cnt += 3 - #__| + # __| - #| - Creating x-data in middle of states + # | - Creating x-data in middle of states short_y = np.array(y_dat)[::2] xdat = list(set(new_x_dat)) @@ -351,11 +351,11 @@ def __create_plotly_series__(self, for i_ind in range(int(len(xdat) / 2)): short_x.append(xdat[cnt] + 0.5) # FIXME Replace 0.5 with variable cnt += 2 - #__| + # __| - #| - Smart Format Dict ************************************************ + # | - Smart Format Dict ************************************************ - #| - DICTS + # | - DICTS plot_parameter_dict = { "dash": None, } @@ -389,7 +389,7 @@ def __create_plotly_series__(self, # # ] - #__| + # __| # if self.fe_df is not None and smart_format is not None: if series_i.fe_df is not None and smart_format is not None: @@ -418,23 +418,23 @@ def __create_plotly_series__(self, else: print("Dataframe column " + df_col_name + " not present") - #__| ****************************************************************** + # __| ****************************************************************** - #| - Series Color + # | - Series Color if "color" in list(plot_parameter_dict): color_out = plot_parameter_dict["color"] else: plot_parameter_dict["color"] = color - #__| + # __| if series_i.format_dict: format_i = series_i.format_dict else: format_i = plot_parameter_dict - #| - Plotly Scatter Plot Instances ************************************ + # | - Plotly Scatter Plot Instances ************************************ - #| - Thick horizontal state lines + # | - Thick horizontal state lines data_1 = go.Scatter( x=new_x_dat, y=new_y_dat, @@ -461,9 +461,9 @@ def __create_plotly_series__(self, ), mode="lines", ) - #__| + # __| - #| - Full, thin line + # | - Full, thin line data_2 = go.Scatter( x=new_x_dat, y=new_y_dat, @@ -479,9 +479,9 @@ def __create_plotly_series__(self, ), mode="lines", ) - #__| + # __| - #| - Points in middle of energy states (For convienient hover) + # | - Points in middle of energy states (For convienient hover) data_3 = go.Scatter( x=short_x, @@ -510,9 +510,9 @@ def __create_plotly_series__(self, ), mode="markers", ) - #__| + # __| - #| - Points in middle of RDS states + # | - Points in middle of RDS states # HACK if series_i.limiting_step == ["ooh", "bulk"]: ind_i = 3 @@ -590,11 +590,11 @@ def __create_plotly_series__(self, ), mode="markers", ) - #__| + # __| - #__| ****************************************************************** + # __| ****************************************************************** - #| - Plot Mode (which data series to plot) + # | - Plot Mode (which data series to plot) if plot_mode == "all": data_lst = [ rds_data, @@ -614,10 +614,10 @@ def __create_plotly_series__(self, data_2, data_3, ] - #__| + # __| return(data_lst) - #__| + # __| def __convert_to_plotting_list__(self, energy_lst, @@ -634,7 +634,7 @@ def __convert_to_plotting_list__(self, spacing: step_size: """ - #| - __convert_to_plotting_list__ + # | - __convert_to_plotting_list__ tmp_list = range(len(energy_lst) * 2) energy_dupl_lst = [energy_lst[i // 2] for i in tmp_list] @@ -646,12 +646,12 @@ def __convert_to_plotting_list__(self, # out_list = [rxn_coord_steps, energy_dupl_lst] return(energy_dupl_lst) - #__| + # __| def max_y_value_per_step(self): """ """ - #| - max_y_value_per_step + # | - max_y_value_per_step fe_matrix = [] # for series_i in self.series_list: @@ -670,7 +670,7 @@ def max_y_value_per_step(self): max_y_val_list.append(fe_matrix[:, step_i].max()) return(max_y_val_list) - #__| + # __| def H_e_pairs_annotations(self, @@ -681,7 +681,7 @@ def H_e_pairs_annotations(self, Args: font_size: """ - #| - H_e_pairs_annotations + # | - H_e_pairs_annotations mid_state_x_array = self.mid_state_x_array rxn_x_array = self.rxn_x_coord_array @@ -708,7 +708,7 @@ def add_annot( font_size: text: """ - #| - add_annot + # | - add_annot ann_i = dict( # x=(rxn_x_array[ind] + rxn_x_array[ind + step]) / 2., x=mid_state_x_array[ind], @@ -723,7 +723,7 @@ def add_annot( ), ), annotations += ann_i - #__| + # __| annotations = [] @@ -766,13 +766,13 @@ def add_annot( ) return(annotations) - #__| + # __| def create_mid_state_x_array(self): """ """ - #| - create_mid_state_x_array + # | - create_mid_state_x_array x_array_data = self.rxn_x_coord_array state_width = self.plot_states_width @@ -786,19 +786,19 @@ def create_mid_state_x_array(self): cnt += 2 return(short_x) - #__| + # __| def get_plotly_layout(self, layout_dict=None): """ """ - #| - plotly_fed_layout + # | - plotly_fed_layout tick_lab_size = 16 axes_lab_size = 18 legend_size=18 annotation_size = 12 - #| - OER vs ORR Settings + # | - OER vs ORR Settings if self.rxn_type == "ORR": xax_labels = [ "O2", @@ -817,30 +817,30 @@ def get_plotly_layout(self, layout_dict=None): "*OOH", "O2", ] - #__| + # __| - #| - Layout + # | - Layout layout = { "title": None, - #| - Font Settings + # | - Font Settings "font": { "family": "Arial", # "Courier New, monospace" "color": "black", }, - #__| + # __| - #| - Margins ------------------------------------------------------ + # | - Margins ------------------------------------------------------ "margin": go.layout.Margin( b=50., l=50., r=30., t=30., ), - #__| + # __| - #| - Axes --------------------------------------------------------- + # | - Axes --------------------------------------------------------- "yaxis": { "title": "Free Energy (eV)", "zeroline": False, @@ -888,9 +888,9 @@ def get_plotly_layout(self, layout_dict=None): size=tick_lab_size, ), }, - #__| -------------------------------------------------------------- + # __| -------------------------------------------------------------- - #| - Legend ------------------------------------------------------- + # | - Legend ------------------------------------------------------- "legend": go.layout.Legend( x=1.1, @@ -915,22 +915,22 @@ def get_plotly_layout(self, layout_dict=None): "showlegend": self.show_legend, - #__| -------------------------------------------------------------- + # __| -------------------------------------------------------------- "paper_bgcolor": 'rgba(240,240,240,0.9)', } - #__| + # __| - #| - H/e Count Annotations + # | - H/e Count Annotations if self.show_H_e_pairs_annotations: annotations = self.H_e_pairs_annotations(font_size=annotation_size) if "annotations" in list(layout): layout["annotations"] += annotations else: layout["annotations"] = annotations - #__| + # __| layout = go.Layout(**layout) @@ -938,6 +938,6 @@ def get_plotly_layout(self, layout_dict=None): layout.update(layout_dict) return(layout) - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** diff --git a/oxr_reaction/oxr_plotting_classes/oxr_plot_scaling.py b/oxr_reaction/oxr_plotting_classes/oxr_plot_scaling.py index ed37fcd..23f88c0 100644 --- a/oxr_reaction/oxr_plotting_classes/oxr_plot_scaling.py +++ b/oxr_reaction/oxr_plotting_classes/oxr_plot_scaling.py @@ -31,7 +31,7 @@ class Scaling_Relations_Plot(): IDEA: Add vertical lines to connect *O, *OH, and *OOH data points """ - #| - Scaling_Relations_Plot *********************************************** + # | - Scaling_Relations_Plot *********************************************** def __init__(self, ORR_Free_E_Plot, @@ -56,7 +56,7 @@ def __init__(self, mode: "all", "ooh_vs_oh", "o_vs_oh" """ - #| - __init__ + # | - __init__ self.ORR_Free_E_Plot = ORR_Free_E_Plot assert (x_ax_species == "oh"), "Only *OH as the x-axis is allowed now" @@ -102,7 +102,7 @@ def __init__(self, self.annotations_list = [] - #__| + # __| def create_scaling_relations_plot(self, smart_format_dict=None, @@ -113,9 +113,9 @@ def create_scaling_relations_plot(self, y_ax_spec: x_ax_spec: """ - #| - create_scaling_relations_plot + # | - create_scaling_relations_plot - #| - Default Smart Format Dict + # | - Default Smart Format Dict if smart_format_dict is None: print("No smart format given!") smart_format_dict = [ @@ -129,9 +129,9 @@ def create_scaling_relations_plot(self, [{"facet": "211"}, {self.marker_border_color_key: "green"}], [{"facet": "100"}, {self.marker_border_color_key: "black"}], ] - #__| + # __| - #| - Processing Data Points + # | - Processing Data Points for series_i in self.ORR_Free_E_Plot.series_list: e_oh = series_i.energy_states_dict["oh"] @@ -161,7 +161,7 @@ def create_scaling_relations_plot(self, smart_format_i = series_i.format_dict - #| - ooh_vs_oh + # | - ooh_vs_oh trace_i = self.__create_trace_i__( e_oh, e_ooh, @@ -171,9 +171,9 @@ def create_scaling_relations_plot(self, ) # self.data_ooh_oh.append(trace_i) self.data_points["ooh_vs_oh"].append(trace_i) - #__| + # __| - #| - o_vs_oh + # | - o_vs_oh trace_i = self.__create_trace_i__( e_oh, e_o, @@ -183,9 +183,9 @@ def create_scaling_relations_plot(self, ) # self.data_o_oh.append(trace_i) self.data_points["o_vs_oh"].append(trace_i) - #__| + # __| - #| - oh_vs_oh + # | - oh_vs_oh trace_i = self.__create_trace_i__( e_oh, e_oh, @@ -195,11 +195,11 @@ def create_scaling_relations_plot(self, ) # self.data_oh_oh.append(trace_i) self.data_points["oh_vs_oh"].append(trace_i) - #__| + # __| - #__| + # __| - #__| + # __| # Deprecated, delete this later def __create_smart_format_dict__(self, property_dict, smart_format_dict): @@ -209,7 +209,7 @@ def __create_smart_format_dict__(self, property_dict, smart_format_dict): property_dict: smart_format_dict: """ - #| - __create_smart_format_dict__ + # | - __create_smart_format_dict__ format_dict = {} for key_i, value_i in property_dict.items(): for format_i in smart_format_dict: @@ -218,12 +218,12 @@ def __create_smart_format_dict__(self, property_dict, smart_format_dict): format_dict.update(format_i[1]) return(format_dict) - #__| + # __| def __create_series_name__(self, series_i): """ """ - #| - create_series_name + # | - create_series_name name_i = "" for key, value in series_i.properties.items(): if key == "coverage": @@ -232,7 +232,7 @@ def __create_series_name__(self, series_i): name_i += str(key) + ": " + str(value) + " | " return(name_i) - #__| + # __| def __create_trace_i__(self, x_energy, @@ -243,7 +243,7 @@ def __create_trace_i__(self, ): """ """ - #| - create_trace_i + # | - create_trace_i # NOTE Looks like I need to put these in a list here x_energy = [x_energy] y_energy = [y_energy] @@ -272,7 +272,7 @@ def __create_trace_i__(self, ) return(trace_i) - #__| + # __| def __series_excluded__(self, properties_i, @@ -288,7 +288,7 @@ def __series_excluded__(self, properties_i: exclude_dict: """ - #| - series_excluded + # | - series_excluded exclude_dict_keys = list(exclude_dict.keys()) properties_i_keys = list(properties_i.keys()) @@ -316,7 +316,7 @@ def __series_excluded__(self, return(all_props_match) - #__| + # __| def fit_scaling_lines(self, dependent_species, # 'ooh', 'o', 'oh' @@ -328,14 +328,14 @@ def fit_scaling_lines(self, dependent_species: y-axis species 'ooh' or 'o' """ - #| - fit_scaling_lines + # | - fit_scaling_lines - #| - LOOP + # | - LOOP oh_list = [] dependent_e_list = [] for series_i in self.ORR_Free_E_Plot.series_list: - #| - Excluding series from fitting + # | - Excluding series from fitting if exclude_dict is not None: properties_i = series_i.properties exclude_series = self.__series_excluded__( @@ -344,13 +344,13 @@ def fit_scaling_lines(self, ) if exclude_series: continue - #__| + # __| energy_i = series_i.energy_states_dict[dependent_species] dependent_e_list.append(energy_i) oh_list.append(series_i.energy_states_dict["oh"]) - #__| + # __| X = np.array([[i] for i in oh_list]) y = np.array(dependent_e_list) @@ -373,7 +373,7 @@ def fit_scaling_lines(self, } # print("_------__)_Z(*XF(8))") - #| - Equation Annotations + # | - Equation Annotations if dependent_species == "ooh": eqn_str_i = ("" + "GOOH=" + @@ -423,15 +423,15 @@ def fit_scaling_lines(self, ) self.annotations_list.append(annotation_i) - #__| + # __| return(out) - #__| + # __| def add_ideal_lines(self): """Add ideal scaling liknes to plot.""" - #| - add_ideal_lines + # | - add_ideal_lines self.add_line({"slope": 1, "intercept": 3.2}, name="*OOH vs *OH Scaling", color="black", @@ -452,7 +452,7 @@ def add_ideal_lines(self): width=1, dash="dash", ) - #__| + # __| def add_line(self, slope_intercept_dict, @@ -470,7 +470,7 @@ def add_line(self, width: dash: """ - #| - add_line + # | - add_line # print(slope_intercept_dict) @@ -480,11 +480,11 @@ def add_line(self, def scaling_meth(E_OH): """ """ - #| - scaling_meth + # | - scaling_meth out = slope * E_OH + intercept return(out) - #__| + # __| LH_bound = self.x_range[0] - 0.2 RH_bound = self.x_range[1] + 0.2 @@ -525,7 +525,7 @@ def scaling_meth(E_OH): # "" # ) - #__| + # __| # NOTE | This shouldn't be an internal method # I don't remember why I wrote the above note @@ -543,7 +543,7 @@ def get_plotly_layout(self, showlegend: layout_dict: """ - #| - create_layout + # | - create_layout # if x_ax_spec == "" if self.x_ax_species == "oh": x_ax_title = "Gads,*OH (eV)" @@ -559,7 +559,7 @@ def get_plotly_layout(self, # legend_size = 18 - #| - Common Axis Dict + # | - Common Axis Dict common_axis_dict = { # "range": y_axis_range, @@ -580,7 +580,7 @@ def get_plotly_layout(self, "ticklen": 2, "tickwidth": 1, } - #__| + # __| x_range = self.x_range y_range = self.y_range @@ -612,14 +612,14 @@ def get_plotly_layout(self, }, ), - #| - Margins ------------------------------------------------------ + # | - Margins ------------------------------------------------------ "margin": go.layout.Margin( b=50., l=50., r=30., t=30., ), - #__| + # __| # # Margin @@ -636,7 +636,7 @@ def get_plotly_layout(self, color='black', ), - #| - Plot Size + # | - Plot Size # "width": 37 * 37.795275591, # "height": 23 * 37.795275591, # @@ -644,9 +644,9 @@ def get_plotly_layout(self, # # "height": 18.7 * 37.795275591, # # "width": 1.5 * 18.7 * 37.795275591, # # "height": 18.7 * 37.795275591, - #__| + # __| - #| - Legend + # | - Legend "showlegend": showlegend, "legend": go.layout.Legend( @@ -676,7 +676,7 @@ def get_plotly_layout(self, # ), # ), - #__| + # __| } @@ -686,22 +686,22 @@ def get_plotly_layout(self, layout.update(layout_dict) return(layout) - #__| + # __| - #| - __old__ + # | - __old__ # def __ideal_ooh_oh_scaling__(self, E_OH): # """Return the *OOH adsorption energy given DG_*OH by scaling. # # Args: # E_OH:DG_*OH energy of adsorption # """ - # #| - __ideal_ooh_oh_scaling__ + # # | - __ideal_ooh_oh_scaling__ # return(E_OH + 3.2) - # #__| + # # __| # # def __ideal_h_oh_scaling__(self, E_OH): # """Return the *OOH adsorption energy given DG_*OH by scaling. @@ -709,9 +709,9 @@ def get_plotly_layout(self, # Args: # E_OH: DG_*OH energy of adsorption. # """ - # #| - __ideal_h_oh_scaling__ + # # | - __ideal_h_oh_scaling__ # return(2 * E_OH) - # #__| + # # __| # # def __ideal_oh_oh_scaling__(self, E_OH): # """Return the *OH adsorption energy given DG_*OH by scaling. @@ -721,18 +721,18 @@ def get_plotly_layout(self, # Args: # E_OH: DG_*OH energy of adsorption. # """ - # #| - __ideal_oh_oh_scaling__ + # # | - __ideal_oh_oh_scaling__ # return(E_OH) - # #__| + # # __| # - #__| + # __| -#__| ********************************************************************** +# __| ********************************************************************** -#| - __old__ +# | - __old__ # x_range_ooh_vs_oh=[0., 3.5], # y_range_ooh_vs_oh=[0., 5.], # x_range_o_vs_oh=[0., 3.5], @@ -755,4 +755,4 @@ def get_plotly_layout(self, # y_range = self.y_range_oh_vs_oh # else: # print("Woops - create_layout") -#__| +# __| diff --git a/oxr_reaction/oxr_plotting_classes/oxr_plot_volcano.py b/oxr_reaction/oxr_plotting_classes/oxr_plot_volcano.py index 0771caa..aff4c0e 100644 --- a/oxr_reaction/oxr_plotting_classes/oxr_plot_volcano.py +++ b/oxr_reaction/oxr_plotting_classes/oxr_plot_volcano.py @@ -5,7 +5,7 @@ Author: Raul A. Flores """ -#| - IMPORT MODULES +# | - IMPORT MODULES import numpy as np import pandas as pd @@ -14,7 +14,7 @@ from oxr_reaction.oxr_series import ORR_Free_E_Series from oxr_reaction.adsorbate_scaling import lim_U_i -#__| +# __| # ██ ██ ██████ ██ ██████ ██████ ██ ██████ ████████ @@ -30,7 +30,7 @@ class Volcano_Plot(): TEMP """ - #| - Volcano_Plot ********************************************************* + # | - Volcano_Plot ********************************************************* def __init__(self, ORR_Free_E_Plot, @@ -56,7 +56,7 @@ def __init__(self, } """ - #| - __init__ + # | - __init__ self.ORR_Free_E_Plot = ORR_Free_E_Plot self.x_ax_species = x_ax_species self.plot_range = plot_range @@ -69,7 +69,7 @@ def __init__(self, self.marker_border_color_key = marker_border_color_key self.marker_shape_key = marker_shape_key - #__| + # __| # NOTE | Rename this create_volcano_plot def create_volcano_relations_plot(self, @@ -82,9 +82,9 @@ def create_volcano_relations_plot(self, smart_format_dict: Optional dictionary that will format data points """ - #| - create_volcano_relations_plot + # | - create_volcano_relations_plot - #| - Default Smart Format Dict + # | - Default Smart Format Dict smart_format_dict = self.smart_format_dict if smart_format_dict is None: @@ -100,15 +100,15 @@ def create_volcano_relations_plot(self, [{"facet": "211"}, {"color1": "green"}], [{"facet": "100"}, {"color1": "black"}], ] - #__| + # __| - #| - Processing Data Points + # | - Processing Data Points x_data_list = [] y_data_list = [] for series_i in self.ORR_Free_E_Plot.series_list: - #| - x-axis energy + # | - x-axis energy x_spec = self.x_ax_species if x_spec == "o-oh": e_o = series_i.energy_states_dict["o"] @@ -116,9 +116,9 @@ def create_volcano_relations_plot(self, x_ax_energy = e_o - e_oh else: x_ax_energy = series_i.energy_states_dict[x_spec] - #__| + # __| - #| - y-axis limiting potential + # | - y-axis limiting potential if self.ORR_Free_E_Plot.rxn_type == "ORR": lim_pot_i = 1.23 - series_i.overpotential @@ -126,9 +126,9 @@ def create_volcano_relations_plot(self, lim_pot_i = 1.23 + series_i.overpotential_OER else: print("LSDJFlksdj") - #__| + # __| - #| - Process series_i + # | - Process series_i x_data_list.append(x_ax_energy) y_data_list.append(lim_pot_i) @@ -159,11 +159,11 @@ def create_volcano_relations_plot(self, ) self.data_points.append(trace_i) - #__| + # __| - #__| + # __| - #| - Finding plot axis limits + # | - Finding plot axis limits if self.plot_range is None: y_axis_range = [min(y_data_list) - 0.2, max(y_data_list) + 0.2] if self.ORR_Free_E_Plot.rxn_type == "OER": @@ -177,9 +177,9 @@ def create_volcano_relations_plot(self, } self.plot_range = plot_range - #__| + # __| - #__| + # __| def create_volcano_lines(self, gas_molec_dict=None, @@ -207,11 +207,11 @@ def create_volcano_lines(self, plot, if 'bottom' then the data points will by on top of the volcano """ - #| - create_volcano_lines + # | - create_volcano_lines out_data = [] x_range = self.plot_range["x"] - #| - Volcano Legs + # | - Volcano Legs volc_legs = [ 'o2_to_ooh', 'ooh_to_o', @@ -226,7 +226,7 @@ def create_volcano_lines(self, 'oh_to_h2o': [], } - #| - Create Volcano Legs (LOOP) + # | - Create Volcano Legs (LOOP) x_axis = np.linspace(x_range[0], x_range[1], num=500) for leg_i in volc_legs: for x_energy_i in x_axis: @@ -250,11 +250,11 @@ def create_volcano_lines(self, rxn_direction="forward", ), ) - #__| + # __| if plot_all_legs: - #| - plot_all_legs + # | - plot_all_legs # hoverinfo_type = "none" hoverinfo_type = "name" @@ -317,11 +317,11 @@ def create_volcano_lines(self, out_data.insert(0, trace_ooh_to_o) out_data.insert(0, trace_o_to_oh) out_data.insert(0, trace_oh_to_h2o) - #__| + # __| - #__| + # __| - #| - Minimum Energy Legs + # | - Minimum Energy Legs energy_lists= [] for leg_i in legs_to_plot: energy_lists.append(energy_dict[leg_i]) @@ -355,10 +355,10 @@ def create_volcano_lines(self, elif trace_priority == "bottom": out_data.insert(0, trace_volcano) - #__| + # __| return(out_data) - #__| + # __| def __create_trace_i__(self, x_energy, @@ -371,7 +371,7 @@ def __create_trace_i__(self, ): """ """ - #| - __create_trace_i__ + # | - __create_trace_i__ if show_data_labels is True: mode_i = "markers+text" @@ -428,7 +428,7 @@ def __create_trace_i__(self, ) return(trace_i) - #__| + # __| def get_plotly_layout(self, showlegend=False, @@ -438,9 +438,9 @@ def get_plotly_layout(self, ): """ """ - #| - get_plotly_layout + # | - get_plotly_layout - #| - Properties + # | - Properties # plot_title="FED" plot_title = None # plot_title_size = 18 @@ -450,7 +450,7 @@ def get_plotly_layout(self, legend_size = 18 # font_family="Computer Modern" # "Courier New, monospace" font_family = "Arial" # "Courier New, monospace" - #__| + # __| if self.x_ax_species == "oh": xaxis_title = "dGOH (eV)" @@ -465,9 +465,9 @@ def get_plotly_layout(self, "color": "black", }, - #| - Axes ----------------------------------------------------- + # | - Axes ----------------------------------------------------- - #| - yaxis + # | - yaxis "yaxis": { "title": "Limiting Potential (V)", # "title": "$\\Delta G (ev)$", @@ -492,9 +492,9 @@ def get_plotly_layout(self, "ticklen": 2, "tickwidth": 1, }, - #__| + # __| - #| - xaxis + # | - xaxis "xaxis": { # "title": "$\\Delta G_{OH} (ev)$", "title": xaxis_title, @@ -517,9 +517,9 @@ def get_plotly_layout(self, size=tick_lab_size, ), }, - #__| + # __| - #__| + # __| "margin": go.layout.Margin( b=50., @@ -531,7 +531,7 @@ def get_plotly_layout(self, # "paper_bgcolor": 'rgba(0,0,0,0)', "plot_bgcolor": 'rgba(0,0,0,0)', - #| - Legend --------------------------------------------------- + # | - Legend --------------------------------------------------- "legend": { "traceorder": "normal", "font": dict(size=legend_size), @@ -544,13 +544,13 @@ def get_plotly_layout(self, # "showlegend": False, "showlegend": showlegend, - #__| + # __| "paper_bgcolor": 'rgba(250,250,250,0.9)', } - #| - Plot Size Settings + # | - Plot Size Settings # # bottom_margin_size = 2.5 * 9. * 37.795275591 # plot_size_settings = { # "width": width, @@ -570,21 +570,21 @@ def get_plotly_layout(self, # # }), # } # layout = {**layout, **plot_size_settings} - #__| + # __| - #| - Applying Layout override dict + # | - Applying Layout override dict if layout_dict is not None: from misc_modules.misc_methods import dict_merge dict_merge(layout, layout_dict) # layout_i = {**layout_i, **layout_dict} - #__| + # __| return(layout) - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** diff --git a/oxr_reaction/oxr_rxn.py b/oxr_reaction/oxr_rxn.py index a1d83ae..4126364 100644 --- a/oxr_reaction/oxr_rxn.py +++ b/oxr_reaction/oxr_rxn.py @@ -5,7 +5,7 @@ Author: Raul A. Flores """ -#| - IMPORT MODULES +# | - IMPORT MODULES import numpy as np import pandas as pd @@ -18,7 +18,7 @@ from oxr_reaction.oxr_series import ORR_Free_E_Series from oxr_reaction.adsorbate_scaling import lim_U_i # from oxr_reaction.oxr_rxn import ORR_Free_E_Plot -#__| +# __| class ORR_Free_E_Plot: @@ -32,7 +32,7 @@ class ORR_Free_E_Plot: we have to normalize all of the species energies by it? """ - #| - ORR_Free_E_Plot ****************************************************** + # | - ORR_Free_E_Plot ****************************************************** def __init__(self, free_energy_df=None, @@ -70,9 +70,9 @@ def __init__(self, rxn_type: ORR or OER """ - #| - __init__ + # | - __init__ - #| - Setting Instance Attributes + # | - Setting Instance Attributes self.fe_df = free_energy_df # self.sys_props = system_properties self.state_title = state_title @@ -96,7 +96,7 @@ def __init__(self, self.plot_states_width = 1. self.rxn_type = rxn_type - #__| + # __| if self.rxn_type == "ORR": @@ -121,7 +121,7 @@ def __init__(self, else: self.series_list = ORR_Free_E_series_list - #| - __old__ + # | - __old__ # if free_energy_df is not None: # self.add_bulk_entry() # self.fill_missing_data() @@ -134,14 +134,14 @@ def __init__(self, # # self.ideal_energy = [4.92, 3.69, 2.46, 1.23, 0] # self.energy_lst_h2o2 = self.rxn_energy_lst_h2o2() # self.overpotential_h2o2 = self.calc_overpotential_h2o2() - #__| + # __| - #__| + # __| def __create_series_name__(self, series_i): """ """ - #| - __create_series_name__ + # | - __create_series_name__ if series_i.properties is not None: name_i = "" @@ -157,7 +157,7 @@ def __create_series_name__(self, series_i): name_i = "" return(name_i) - #__| + # __| def __create_smart_format_dict__(self, property_dict, smart_format_dict): """Create smart format dictionary. @@ -166,7 +166,7 @@ def __create_smart_format_dict__(self, property_dict, smart_format_dict): property_dict: smart_format_dict: """ - #| - __create_smart_format_dict__ + # | - __create_smart_format_dict__ if property_dict is None: return({}) @@ -178,7 +178,7 @@ def __create_smart_format_dict__(self, property_dict, smart_format_dict): format_dict.update(format_i[1]) return(format_dict) - #__| + # __| def add_series(self, fe_df, @@ -203,7 +203,7 @@ def add_series(self, Args: TEMP """ - #| - add_series + # | - add_series if smart_format: smart_format_i = self.smart_format else: @@ -237,6 +237,6 @@ def add_series(self, ) self.series_list.append(ORR_Series) - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** diff --git a/oxr_reaction/oxr_series.py b/oxr_reaction/oxr_series.py index 991560f..b0fdb31 100644 --- a/oxr_reaction/oxr_series.py +++ b/oxr_reaction/oxr_series.py @@ -5,7 +5,7 @@ Author: Raul A. Flores """ -#| - IMPORT MODULES +# | - IMPORT MODULES import copy import numpy as np @@ -13,7 +13,7 @@ from plotly.graph_objs import Scatter pd.options.mode.chained_assignment = None -#__| +# __| class ORR_Free_E_Series(): """ORR free energy diagram series class. @@ -29,7 +29,7 @@ class ORR_Free_E_Series(): # TODO | The color_list approach is not good """ - #| - ORR_Free_E_Series **************************************************** + # | - ORR_Free_E_Series **************************************************** def __init__(self, free_energy_df=None, state_title="adsorbate", @@ -76,9 +76,9 @@ def __init__(self, If true then the overpotential is given explicitely in the input dataframe with name "overpotential" """ - #| - __init__ + # | - __init__ - #| - Setting Instance Attributes + # | - Setting Instance Attributes self.fe_df = free_energy_df # self.sys_props = system_properties @@ -110,7 +110,7 @@ def __init__(self, self.overpotential_given = overpotential_given # self.i_cnt = i_cnt - #__| + # __| if self.rxn_type == "ORR": self.rxn_mech_states = ["bulk", "ooh", "o", "oh", "bulk"] @@ -157,11 +157,11 @@ def __init__(self, overpotential_type=self.rxn_type, add_overpot=self.add_overpot, ) - #__| + # __| def __fill_nan_values__(self): """Fill nan adsorption energy values from scaling relations.""" - #| - tmp + # | - tmp energy_dict = self.energy_states_dict if True in list(np.isnan(list(energy_dict.values()))): @@ -186,7 +186,7 @@ def __fill_nan_values__(self): energy_dict["oh"] = oh_new self.energy_states_dict = energy_dict - #__| + # __| def __num_of_states__(self): """Return number of unique states. @@ -195,7 +195,7 @@ def __num_of_states__(self): data frame. The correct number of states for the OER and/or ORR reaction are 4, only 2 states are needed for the peroxide reaction. """ - #| - __num_of_states + # | - __num_of_states df_i = self.fe_df num_of_states = len(set(df_i["adsorbate"].tolist())) @@ -204,12 +204,12 @@ def __num_of_states__(self): assert num_of_states >= 4, err_mess return(num_of_states) - #__| + # __| def __energy_states_dict__(self): """ """ - #| - __energy_states_dict__ + # | - __energy_states_dict__ energy_lst = self.energy_lst rxn_mech_states = self.rxn_mech_states @@ -219,18 +219,18 @@ def __energy_states_dict__(self): return(energy_states_dict) # energy_states_dict - #__| + # __| def __create_property_dict__(self): """ """ - #| - __create_property_dict__ + # | - __create_property_dict__ df_i = self.fe_df def all_same_val(df_i, prop_i, val_1): """ """ - #| - all_same_val + # | - all_same_val out_list = [] for i in df_i[prop_i].tolist(): if i == val_1: @@ -243,7 +243,7 @@ def all_same_val(df_i, prop_i, val_1): # [True if i == val_1 else False for i in # df_i[prop_i].tolist()], - #__| + # __| if self.property_key_list is not None: prop_dict_i = {} @@ -264,7 +264,7 @@ def all_same_val(df_i, prop_i, val_1): return(prop_dict_i) else: return({}) - #__| + # __| def add_bulk_entry(self, bulk_e=0.0, @@ -275,7 +275,7 @@ def add_bulk_entry(self, Args: bulk_e: """ - #| - add_bulk_entry + # | - add_bulk_entry df = self.fe_df bulk_df = pd.DataFrame([{ "adsorbate": "bulk", @@ -287,11 +287,11 @@ def add_bulk_entry(self, df = df.append(bulk_df, ignore_index=True, sort=True) self.fe_df = df - #__| + # __| def rxn_energy_lst_h2o2(self): """Construct energy list of h2o2 FED.""" - #| - rxn_energy_lst_h2o2 + # | - rxn_energy_lst_h2o2 # h2o2_e = 3.52 df = self.fe_df @@ -311,7 +311,7 @@ def rxn_energy_lst_h2o2(self): free_energy_list.append(3.52) return(free_energy_list) - #__| + # __| def property_list(self, column_name): """General method to create a list from a column in the dataframe. @@ -322,7 +322,7 @@ def property_list(self, column_name): Args: column_name: """ - #| - property_list + # | - property_list df = self.fe_df property_list = [] @@ -334,35 +334,35 @@ def property_list(self, column_name): # free_energy_list[0] += 4.92 return(property_list) - #__| + # __| def fill_missing_data(self): """ """ - #| - fill_missing_data + # | - fill_missing_data df = self.fe_df df_missing_data = pd.DataFrame() for state in self.rxn_mech_states: df_state = df.loc[df[self.state_title] == state] - #| - If df is missing state fill in row with NaN for energy + # | - If df is missing state fill in row with NaN for energy if df_state.empty: df_state = pd.DataFrame([{ self.state_title: state, self.fe_title: np.nan, }]) df_missing_data = df_missing_data.append(df_state) - #__| + # __| self.fe_df = self.fe_df.append(df_missing_data, sort=True) - #__| + # __| def rxn_energy_lst(self): """List corresponding to the steps of ORR. (1. O2, 2. *OOH, 3. *O, 4. *OH, 5. 2H2O) """ - #| - rxn_energy_lst + # | - rxn_energy_lst df = self.fe_df free_energy_list = [] @@ -371,29 +371,29 @@ def rxn_energy_lst(self): # print(df_state) - #| - __old__ + # | - __old__ # Not sure what this was trying to accomplish # if len(df_state) == 2: # state_energy_list = [] # for j_cnt, row_j in df_state.iterrows(): # energy_j = row_j[self.fe_title] # state_energy_list.append(energy_j) - #__| + # __| - #| - If df is missing state fill in row with NaN for energy + # | - If df is missing state fill in row with NaN for energy if df_state.empty: df_state = pd.DataFrame([{ self.state_title: state, self.fe_title: np.nan, }]) - #__| + # __| # This just takes the first species # If you feed a df with more than one entry per species, then # this will stupidly choose the first one state_energy_1 = df_state.iloc[0][self.fe_title] - #| - __old__ + # | - __old__ # if type(state_energy_1) != float: # print(type(state_energy_1)) # print("DSKFJKLSDJFSjk_d--_d-d-_D_D_d-d-d-d-d___D_D_D_") @@ -413,7 +413,7 @@ def rxn_energy_lst(self): # print(np.isnan(state_energy_1)) # if isinstance(state_energy_1, np.float) is False: # print("lkjfksjd") - #__| + # __| free_energy_list.append(state_energy_1) @@ -425,24 +425,24 @@ def rxn_energy_lst(self): free_energy_list[0] += 4.92 return(free_energy_list) - #__| + # __| def rxn_energy_lst_new(self): """ """ - #| - rxn_energy_lst_new + # | - rxn_energy_lst_new df = self.fe_df free_energy_list = [] for state in self.rxn_mech_states: df_state = df.loc[df[self.state_title] == state] - #| - If df is missing state fill in row with NaN for energy + # | - If df is missing state fill in row with NaN for energy if df_state.empty: df_state = pd.DataFrame([{ self.state_title: state, self.fe_title: np.nan, }]) - #__| + # __| state_energy_list = [] for j_cnt, row_j in df_state.iterrows(): @@ -469,7 +469,7 @@ def rxn_energy_lst_new(self): free_energy_list[0] += 4.92 return(free_energy_list) - #__| + # __| def apply_bias(self, bias, energy_list): """Apply bias to free energies. @@ -477,7 +477,7 @@ def apply_bias(self, bias, energy_list): Applies a potential to every species in the 4 and 2-electron process and adjusts their free energies accordingly """ - #| - apply_bias + # | - apply_bias if self.rxn_type == "ORR": flip_energy_list = False bool_flip = -1 @@ -492,7 +492,7 @@ def apply_bias(self, bias, energy_list): mod_free_e_lst.append(en - elec * bias) return(mod_free_e_lst) - #__| + # __| def calc_overpotential(self): """ @@ -502,7 +502,7 @@ def calc_overpotential(self): limiting reaction step in the form of a list, species_A -> species_B is [species_A, species_B] """ - #| - calc_overpotential + # | - calc_overpotential if self.overpotential_given: out_list = [None, None] if "overpotential" in list(self.fe_df): @@ -529,11 +529,11 @@ def calc_overpotential(self): out_list = [overpotential, limiting_step] return(out_list) - #__| + # __| def calc_overpotential_OER(self): """Calculate the OER overpotential of a ORR series.""" - #| - calc_overpotential_OER + # | - calc_overpotential_OER rxn_spec = self.rxn_mech_states overpotential_lst = [] @@ -549,7 +549,7 @@ def calc_overpotential_OER(self): out_list = [overpotential, limiting_step] return(out_list) - #__| + # __| def calc_overpotential_h2o2(self): """ @@ -558,7 +558,7 @@ def calc_overpotential_h2o2(self): The overpotential for the 2e- process depends only on the energy of the *OOH intermediate """ - #| - calc_overpotential_h2o2 + # | - calc_overpotential_h2o2 df = self.fe_df ooh_row = df[df["adsorbate"] == "ooh"] ooh_ads_e = ooh_row.iloc[0]["ads_e"] @@ -566,7 +566,7 @@ def calc_overpotential_h2o2(self): op_4e = ooh_ads_e - 4.22 return(op_4e) - #__| + # __| def __series_plot_name__(self, opt_name=None, @@ -587,9 +587,9 @@ def __series_plot_name__(self, smart_format: overpotential_type: """ - #| - __series_plot_name__ + # | - __series_plot_name__ - #| - Getting appropriate Overpotential + # | - Getting appropriate Overpotential if add_overpot: if overpotential_type == "ORR": overpot_i = self.overpotential @@ -601,9 +601,9 @@ def __series_plot_name__(self, overpot_i = self.overpotential else: overpot_i = "" - #__| + # __| - #| - Connecting properties key: values into string + # | - Connecting properties key: values into string if properties is not None: properties_string_name = "" for key_i, value_i in properties.items(): @@ -619,9 +619,9 @@ def __series_plot_name__(self, properties_string_name = properties_string_name[0:-3] else: properties_string_name = "" - #__| + # __| - #| - Data Series Name + # | - Data Series Name if opt_name is not None: name_i = opt_name + ": " + properties_string_name @@ -631,13 +631,13 @@ def __series_plot_name__(self, if add_overpot: name_i += " (OP: " + str(round(overpot_i, 2)) + ")" - #__| + # __| # NEW | If name_i given, then just use that if self.name_i is not None: return(self.name_i) return(name_i) - #__| + # __| - #__| + # __| diff --git a/plotting/my_plotly.py b/plotting/my_plotly.py index eafbbf8..78f1d75 100644 --- a/plotting/my_plotly.py +++ b/plotting/my_plotly.py @@ -5,7 +5,7 @@ Author: Raul A. Flores """ -#| - Import Modules +# | - Import Modules import os import copy @@ -17,12 +17,12 @@ import plotly.graph_objs as go from plotly import io as pyio -#__| +# __| def get_xy_axis_info(fig): """ """ - #| - get_xy_axis_info + # | - get_xy_axis_info xy_axis_list = [i for i in fig.layout._props if "axis" in i] x_axis_list = [i for i in xy_axis_list if i[0] == "x"] @@ -67,7 +67,7 @@ def get_xy_axis_info(fig): ) return(out_dict) - #__| + # __| @@ -79,7 +79,7 @@ def add_duplicate_axes( ): """ """ - #| - add_duplicate_axes + # | - add_duplicate_axes # This is necessary to make sure that the original traces are still visible after adding the new traces fig.update_layout( @@ -134,10 +134,10 @@ def add_duplicate_axes( axis_type + "axis": axis_type + str(new_index) }).to_plotly_json()) - #__| + # __| -#| - OLD | add_duplicate_axes +# | - OLD | add_duplicate_axes # def add_duplicate_axes( # fig, # axis_type="x", # 'x' or 'y' @@ -145,7 +145,7 @@ def add_duplicate_axes( # ): # """ # """ -# #| - add_duplicate_axes +# # | - add_duplicate_axes # # # This is necessary to make sure that the original traces are still visible after adding the new traces # fig.update_layout( @@ -189,8 +189,8 @@ def add_duplicate_axes( # **go.Scatter({ # axis_type + "axis": axis_type + str(new_index) # }).to_plotly_json()) -# #__| -#__| +# # __| +# __| def add_minor_ticks( @@ -201,7 +201,7 @@ def add_minor_ticks( ): """ """ - #| - add_minor_ticks + # | - add_minor_ticks dummy_trace = go.Scatter( x=[0], y=[0], @@ -288,7 +288,7 @@ def add_minor_ticks( return(fig) - #__| + # __| def my_plotly_plot( @@ -323,7 +323,7 @@ def my_plotly_plot( plotly data object """ - #| - my_plotly_plot + # | - my_plotly_plot assert figure is not None, "Must pass a plot.ly figure object" fig = figure @@ -334,7 +334,7 @@ def my_plotly_plot( os.makedirs(plot_dir) - #| - Local write to HTML + # | - Local write to HTML if write_html: pyio.write_html( fig, @@ -351,9 +351,9 @@ def my_plotly_plot( # default_height='100%', # auto_open=False, ) - #__| + # __| - #| - Write pdf and svg (if ORCA is installed and working) + # | - Write pdf and svg (if ORCA is installed and working) # Getting the hostname of computer import socket hostname = socket.gethostbyaddr(socket.gethostname())[0] @@ -388,11 +388,11 @@ def my_plotly_plot( except: print("Couldn't write png") - #__| + # __| # return(fig) - #__| + # __| @@ -405,7 +405,7 @@ def reapply_colors(data): Args: plotly data series (list of graph objects to be plotted) """ - #| - reapply_colors + # | - reapply_colors from colors.colors import generate_color_palette dat_lst_master = data @@ -436,23 +436,23 @@ def reapply_colors(data): series_i.line["color"] = tmp return(dat_lst_master) - #__| + # __| def plot_layout( # xax_labels = ): """ """ - #| - plot_layout + # | - plot_layout - #| - Plot Settings + # | - Plot Settings plot_title_size = 18 tick_lab_size = 16 axes_lab_size = 18 legend_size = 18 - #__| + # __| - #| - Plot Layout + # | - Plot Layout xax_labels = ["O2", "OOH", "O", "OH", "H2O"] layout = { @@ -464,7 +464,7 @@ def plot_layout( "color": "black", }, - #| - Axes -------------------------------------------------------------- + # | - Axes -------------------------------------------------------------- "yaxis": { "title": "Free Energy [eV]", "zeroline": True, @@ -490,22 +490,22 @@ def plot_layout( size=tick_lab_size, ), }, - #__| ------------------------------------------------------------------- + # __| ------------------------------------------------------------------- - #| - Legend ------------------------------------------------------------ + # | - Legend ------------------------------------------------------------ "legend": { "traceorder": "normal", "font": dict(size=legend_size) }, - #__| ------------------------------------------------------------------- + # __| ------------------------------------------------------------------- - #| - Plot Size + # | - Plot Size "width": 200 * 4., "height": 200 * 3., - #__| + # __| } - #__| + # __| fig = Figure(data=dat_lst, layout=layout) # plotly.plotly.image.save_as(fig, filename="pl_hab_opda_raman.png") @@ -522,4 +522,4 @@ def plot_layout( return(layout) - #__| + # __| diff --git a/plotting/plotly_layout_template.py b/plotting/plotly_layout_template.py index 1c5bfe4..3a755cb 100644 --- a/plotting/plotly_layout_template.py +++ b/plotting/plotly_layout_template.py @@ -1,14 +1,14 @@ """ """ -#| - Import Modules +# | - Import Modules import plotly.graph_objs as go -#__| +# __| # ############################################################################# -#| - Main layout object +# | - Main layout object layout = go.Layout( angularaxis=None, annotations=None, @@ -101,12 +101,12 @@ xaxis=None, yaxis=None, ) -#__| +# __| -#| - Axis Layout options +# | - Axis Layout options -#| - shared axis dict +# | - shared axis dict shared_axis_dict = dict( anchor=None, automargin=None, @@ -185,7 +185,7 @@ zerolinecolor=None, zerolinewidth=None, ) -#__| +# __| xaxis_layout = go.layout.XAxis(shared_axis_dict) xaxis_layout.update(go.layout.XAxis( @@ -201,13 +201,13 @@ layout.xaxis = xaxis_layout layout.yaxis = yaxis_layout -#__| +# __| -#| - Plot Annotations +# | - Plot Annotations annotations = [ - #| - Axis Titles + # | - Axis Titles { # 'font': {'size': axis_label_font_size}, 'font': {'size': 12}, @@ -236,9 +236,9 @@ 'yanchor': 'middle', 'yref': 'paper' }, - #__| + # __| ] layout.annotations = annotations -#__| +# __| diff --git a/pourbaix_pymatgen/data_exp_form_e.py b/pourbaix_pymatgen/data_exp_form_e.py index e4493b2..5cc3830 100644 --- a/pourbaix_pymatgen/data_exp_form_e.py +++ b/pourbaix_pymatgen/data_exp_form_e.py @@ -6,7 +6,7 @@ def formation_e_data_dict(): All energies are in [eV] """ - #| - - formation_e_data_dict + # | - - formation_e_data_dict import ast direct_0 = '/home/flores12/01_ORR-MatStabScreen/01_virenv-pymatgen/01_data/03_formation_e/' List = open(direct_0+'data.py').read().splitlines() @@ -54,7 +54,7 @@ def formation_e_data_dict(): dict[frozenset(i)].append(j) return dict - #__| + # __| def get_entry(chemical_formula_list, data_dict): """ @@ -63,7 +63,7 @@ def get_entry(chemical_formula_list, data_dict): chemical_formula_list: data_dict: """ - #| - - get_entry + # | - - get_entry # print chemical_formula_list key = frozenset(chemical_formula_list) @@ -75,7 +75,7 @@ def get_entry(chemical_formula_list, data_dict): return entry - #__| + # __| # form_e_data = formation_e_data_dict() # temp = get_entry(['Pt1','O2'],form_e_data) @@ -86,7 +86,7 @@ def oxygen_stoich(exp_entry): Args: exp_entry """ - #| - - oxygen_stoich + # | - - oxygen_stoich formula = exp_entry['chem_formula'] for i in formula: if i[0]=='O' and i[1].isdigit()==True: @@ -98,10 +98,10 @@ def oxygen_stoich(exp_entry): break return oxy_num - #__| + # __| -#| - - Possible code to take "Pt2O3 and extract out the elements and stoicheometric numbers" +# | - - Possible code to take "Pt2O3 and extract out the elements and stoicheometric numbers" # import re # # chem_form_lst = [] @@ -119,4 +119,4 @@ def oxygen_stoich(exp_entry): # elem_stoich = re.match(r"([a-z]+)([0-9]+)", element, re.I).groups() # elem = elem_stoich[0] # stoich = elem_stoich[1] -#__| +# __| diff --git a/pourbaix_pymatgen/element_list.py b/pourbaix_pymatgen/element_list.py index 9509caf..984ad43 100644 --- a/pourbaix_pymatgen/element_list.py +++ b/pourbaix_pymatgen/element_list.py @@ -6,7 +6,7 @@ class ElementList(object): Args: N/A """ - #| - - ElementList + # | - - ElementList def __init__(self, atom_num_lst=[[]]): self.atom_num_lst = atom_num_lst if not atom_num_lst==[[]]: @@ -47,12 +47,12 @@ def mk_lst_trans_met(self): elements.append(element) return elements - #__| + # __| class ElemList_mod(object): """ """ - #| - - ElemList_mod + # | - - ElemList_mod def __init__(self,elem_lst): self.elem_lst = elem_lst @property @@ -70,14 +70,14 @@ def sort(self,srt_type='group_num'): elem_lst_sorted.sort(key=lambda x: x.group) return elem_lst_sorted - #| - - d-band filling + # | - - d-band filling if srt_type == 'd-band': #COMBAK tmp = 7 - #__| + # __| def remove(self,element): """ @@ -108,7 +108,7 @@ def remove(self,element): elem_0 = next((x for x in self.elem_lst if x.number == element), None) elem_lst_new = [x for x in self.elem_lst if not x.number == element] return elem_lst_new - #__| + # __| def elem_str_mke(elem_lst): """ @@ -116,10 +116,10 @@ def elem_str_mke(elem_lst): Args: elem_lst: """ - #| - - elem_str_mke + # | - - elem_str_mke elem_str=[] for i in elem_lst: elem_str.append(i.symbol) return elem_str - #__| + # __| diff --git a/pourbaix_pymatgen/energy_scheme.py b/pourbaix_pymatgen/energy_scheme.py index b63be25..fcee4f2 100644 --- a/pourbaix_pymatgen/energy_scheme.py +++ b/pourbaix_pymatgen/energy_scheme.py @@ -6,7 +6,7 @@ def ref_atoms_dict(): Args: """ - #| - - ref_atoms_dict + # | - - ref_atoms_dict # e_o = -4.93552791875 # mp-12957, half of O2 - No entropy term # e_h = -3.2397 # mp-754417, half of H2 - No entropy term @@ -20,7 +20,7 @@ def ref_atoms_dict(): e_o = -5.21 e_h = -3.6018845 - #| - - H2O, H2 Reference State + # | - - H2O, H2 Reference State # From Michal Badjich o2=-9.88557216 h2o=-14.23091949 @@ -37,10 +37,10 @@ def ref_atoms_dict(): # e_o = o_ref2 # e_h = h2/2. - #__| + # __| - #| - - Entropic Corrections + # | - - Entropic Corrections entr_o = -0.3167 entr_h = -0.36218 @@ -51,9 +51,9 @@ def ref_atoms_dict(): return ref_dict - #__| + # __| - #__| + # __| def h2o_energy(): """ @@ -78,19 +78,19 @@ def form_e_all_oxides(element, only_pd_entries=False): 4. Apply correction scheme to entries 5. Calculate the formation energy for the oxides per oxygen molecule """ - #| - - form_e_all_oxides + # | - - form_e_all_oxides - #| - - Imported Modules + # | - - Imported Modules from pd_make import entry_data, aq_correction, stable_entr, form_e from entry_methods import entry_remove, pure_atoms_return,contains_element, norm_e from energy_scheme import ref_atoms_dict - #__| + # __| - #| - - Script parameters + # | - - Script parameters mprester_key = 'ZJhfHmMTTwbW29Sr' # Input your materials project id direct_0 = '/home/flores12/01_ORR-MatStabScreen/01_virenv-pymatgen/01_data/01-1_local_MP_entry/' correction_=True - #__| + # __| all_entries = entry_data(element,element,direct_0,mprester_key)['entries'] oxygen_entries = contains_element(all_entries,'O') @@ -100,7 +100,7 @@ def form_e_all_oxides(element, only_pd_entries=False): oxides = entry_remove(entries_no_h, 'O2') - #| - - Finding the Entries Used in the Pourbaix Diagram + # | - - Finding the Entries Used in the Pourbaix Diagram entry_ion_data = entry_data(element, element, direct_0, mprester_key) entries = entry_ion_data["entries"] @@ -111,27 +111,27 @@ def form_e_all_oxides(element, only_pd_entries=False): entry_id_lst = [] for i in pbx_solid_entries: entry_id_lst.append(i.entry_id) - #__| + # __| - #| - - Deleting Entries in Oxides Which Aren't in Pourbaix Entry List + # | - - Deleting Entries in Oxides Which Aren't in Pourbaix Entry List if only_pd_entries==True: oxides_temp = [] for i in oxides: if i.entry_id in entry_id_lst: oxides_temp.append(i) oxides = oxides_temp - #__| + # __| - #| - - Metallic References + # | - - Metallic References elem_ref = pure_atoms_return(all_entries)[0] elem_ref_e = norm_e(elem_ref) if not elem_ref.name==element: print 'calc_form_e - The reference atom is not the same as the element' - #__| + # __| - #| - - Formation Energy + # | - - Formation Energy ref_atom_energies = ref_atoms_dict() e_o = ref_atom_energies['e_o'] e_h = ref_atom_energies['e_h'] @@ -156,7 +156,7 @@ def form_e_all_oxides(element, only_pd_entries=False): form_e_lst.append(entry_dict) - #__| + # __| return form_e_lst - #__| + # __| diff --git a/pourbaix_pymatgen/entry_methods.py b/pourbaix_pymatgen/entry_methods.py index 19aaa37..b10b2b1 100644 --- a/pourbaix_pymatgen/entry_methods.py +++ b/pourbaix_pymatgen/entry_methods.py @@ -12,14 +12,14 @@ def get_entries_MP(material_name): Args: material_name: Material's formula unit """ - #| - - get_entries_MP + # | - - get_entries_MP from pymatgen.matproj.rest import MPRester # MP API key to access species in MP import warnings warnings.filterwarnings('ignore') mpr = MPRester('ZJhfHmMTTwbW29Sr') # entries = mpr.get_entries - #__| + # __| def get_entry_MP(entry_s): """ @@ -27,7 +27,7 @@ def get_entry_MP(entry_s): Args: ent: """ - #| - - get_entry_MP + # | - - get_entry_MP from pymatgen.matproj.rest import MPRester # MP API key to access species in MP import warnings warnings.filterwarnings('ignore') @@ -43,7 +43,7 @@ def get_entry_MP(entry_s): else: out = mpr.get_entries(entry_s) return out - #__| + # __| def norm_e(entry, correction=True): """ @@ -52,7 +52,7 @@ def norm_e(entry, correction=True): entry: correction: Apply default MP corrections """ - #| - - norm_e + # | - - norm_e if correction==True: e_raw = entry.energy elif correction==False: @@ -63,7 +63,7 @@ def norm_e(entry, correction=True): e_norm = e_raw/stoich_fact return e_norm - #__| + # __| def return_entry(entry_list, entryid): """ @@ -73,7 +73,7 @@ def return_entry(entry_list, entryid): entry_list: List of pymatgen entries entryid: Materials Project id (ex. mp-715572) or entry name """ - #| - - return_entry + # | - - return_entry entryid_prefix = entryid[:3] if entryid_prefix=='mp-': for ent in entry_list: @@ -86,7 +86,7 @@ def return_entry(entry_list, entryid): out_lst.append(ent) return out_lst - #__| + # __| def entry_remove(entries, entry_to_remove): @@ -97,7 +97,7 @@ def entry_remove(entries, entry_to_remove): entries: List of entries to be processed entry_to_remove: Name of entry to be removed from the entries list """ - #| - - entry_remove + # | - - entry_remove import numpy as np index_lst = [] @@ -112,7 +112,7 @@ def entry_remove(entries, entry_to_remove): entries_copy = np.delete(entries_copy, index_lst).tolist() return entries_copy - #__| + # __| def base_atom(entries): """ @@ -122,7 +122,7 @@ def base_atom(entries): Args: entries: List of entries """ - #| - - base_atom + # | - - base_atom from pymatgen import Element elem_lst = [] @@ -149,7 +149,7 @@ def base_atom(entries): elem_lst = elem_lst_nme return elem_lst - #__| + # __| def pure_atoms_remove(entries): """ @@ -160,7 +160,7 @@ def pure_atoms_remove(entries): Args: entries: List of entries """ - #| - - pure_atoms_remove + # | - - pure_atoms_remove from pymatgen import Element from entry_methods import base_atom @@ -177,12 +177,12 @@ def pure_atoms_remove(entries): for entry in pure_met_lst: entries.remove(entry) return entries - #__| + # __| def pure_atoms_return(entries): """ """ - #| - - pure_atoms_return + # | - - pure_atoms_return from pymatgen import Element from entry_methods import base_atom @@ -196,18 +196,18 @@ def pure_atoms_return(entries): if len(entr_elem_lst)==1 and entr_elem_lst[0].name in base_atoms: pure_met_lst.append(entry) - #| - - If there are more pure atomic species than there are base atoms return an error + # | - - If there are more pure atomic species than there are base atoms return an error if len(pure_met_lst)>len(base_atoms): tmp = 4 # print 'entry_methods.pure_atoms_return - There is more than one option for the reference metallic atom' - #__| + # __| # for i in pure_met_lst: # print norm_e(i) # print '' return pure_met_lst - #__| + # __| # ████████ ███████ ███ ███ ██████ @@ -223,7 +223,7 @@ def alloy_entries(entries): Args: entries: List of entries """ - #| - - alloy_entries + # | - - alloy_entries from pymatgen import Element from entry_methods import base_atom @@ -249,7 +249,7 @@ def alloy_entries(entries): x.composition.fractional_composition.get_atomic_fraction(elem_0)) return alloy_lst - #__| + # __| def contains_element(entries,element_symbol): @@ -259,20 +259,20 @@ def contains_element(entries,element_symbol): Args: entries: List of pymatgen objects """ - #| - - contains_element + # | - - contains_element from pymatgen import Element - #| - - Get rid of entries without oxygen + # | - - Get rid of entries without oxygen element = Element(element_symbol) lst = [entry for entry in entries if element in entry.composition.elements] # for i in entries: return lst - #__| + # __| - #__| + # __| def number_of_elem_in_entry(entry,element): @@ -284,8 +284,8 @@ def number_of_elem_in_entry(entry,element): entry: entry/species to be processes element: element to be counted in the target entry """ - #| - - number_of_elem_in_entry - #__| + # | - - number_of_elem_in_entry + # __| def element_formula_list(entry): @@ -295,10 +295,10 @@ def element_formula_list(entry): Args: entry: entry to be proccessed """ - #| - - element_formula_list + # | - - element_formula_list formula_list = [] for element in entry.composition.get_el_amt_dict(): elem_coef = entry.composition.get_el_amt_dict()[element]/entry.composition.get_integer_formula_and_factor()[1] formula_list.append(str(element)+str(elem_coef)[:1]) return formula_list - #__| + # __| diff --git a/pourbaix_pymatgen/find_closest_point_pd.py b/pourbaix_pymatgen/find_closest_point_pd.py index 53441e4..0339063 100644 --- a/pourbaix_pymatgen/find_closest_point_pd.py +++ b/pourbaix_pymatgen/find_closest_point_pd.py @@ -14,7 +14,7 @@ def find_closest_point_pd(pourbaix_diagram_object): plotter = PourbaixPlotter(pourbaix_diagram_object) (stable, unstable) = plotter.pourbaix_plot_data(limits) -#| - - Ambar's Region Filter Check Function +# | - - Ambar's Region Filter Check Function def screening_check_desirable(entry,criteria='only-solid'): is_desired = False if criteria not in ['only-solid']: @@ -27,9 +27,9 @@ def screening_check_desirable(entry,criteria='only-solid'): if not criteria: print "Not desired entry", entry.name return is_desired -#__| +# __| -#| - - Ambar's Function for Water and Hydrogen Lines +# | - - Ambar's Function for Water and Hydrogen Lines def get_water_stability_lines(limits): from pymatgen.analysis.pourbaix.maker import PREFAC xlim = limits[0] @@ -39,9 +39,9 @@ def get_water_stability_lines(limits): o_line = np.transpose([[xlim[0], -xlim[0] * PREFAC + 1.23], [xlim[1], -xlim[1] * PREFAC + 1.23]]) return (h_line, o_line) -#__| +# __| -#| - - Returns the Desirable Regions of PD in "vertices" +# | - - Returns the Desirable Regions of PD in "vertices" vertices = [] import time for entry, lines in stable.items(): @@ -59,9 +59,9 @@ def get_water_stability_lines(limits): vertices.append(point1) if point2 not in vertices and is_desired: vertices.append(point2) -#__| +# __| -#| - - Placing the desired phase's name in the diagram +# | - - Placing the desired phase's name in the diagram center_x=0 center_y=0 count = 0 @@ -74,14 +74,14 @@ def get_water_stability_lines(limits): center_x = center_x /count center_y = center_y /count plt.annotate(str(desired_entry.name), xy=(center_x, center_y)) -#__| +# __| -#| - - Plotting Water and Hydrogen Equilibrium Lines +# | - - Plotting Water and Hydrogen Equilibrium Lines # Get water line h_line, o_line = get_water_stability_lines(limits) plt.plot(h_line[0], h_line[1], "r--", linewidth=lw) plt.plot(o_line[0], o_line[1], "r--", linewidth=lw) -#__| +# __| # Getting distances print "Getting distances of vertices" diff --git a/pourbaix_pymatgen/heat_map.py b/pourbaix_pymatgen/heat_map.py index 8810b4e..a9211a8 100644 --- a/pourbaix_pymatgen/heat_map.py +++ b/pourbaix_pymatgen/heat_map.py @@ -1,4 +1,4 @@ -#| - - Import Modules +# | - - Import Modules # -*- coding: utf-8 -*- from pourdiag import pd_entries from entry_methods import pure_atoms_remove, alloy_entries, base_atom @@ -8,7 +8,7 @@ import numpy as np import matplotlib.colors as colors -#__| +# __| def process(i,j,comp=0.5, heat_map_scale='Pt_ref', pt_oxid_V=0.6470339): """ @@ -21,7 +21,7 @@ def process(i,j,comp=0.5, heat_map_scale='Pt_ref', pt_oxid_V=0.6470339): j: Second element in the system comp: Composition loading of the two elements (default is 0.5) """ - #| - - process + # | - - process entries = pd_entries(i.symbol,j.symbol) coord = phase_coord(entries, comp, prim_elem=i.symbol) filt1 = phase_filter(coord,'metallic') @@ -36,7 +36,7 @@ def process(i,j,comp=0.5, heat_map_scale='Pt_ref', pt_oxid_V=0.6470339): msp = most_stable_phase(filt, scale=heat_map_scale, pt_oxid_V=pt_oxid_V) # msp = most_stable_phase(filt,pH=10.5,scale=heat_map_scale) return msp - #__| + # __| def process_alloy(i,j): # Deprecated ******************************************* """ @@ -50,7 +50,7 @@ def process_alloy(i,j): # Deprecated ******************************************* i: First element in the system j: Second element in the system """ - #| - - process_alloy + # | - - process_alloy entries = pd_entries(i.symbol,j.symbol) # entries = pure_atoms_remove(entries) alloy_entr = alloy_entries(entries) @@ -85,7 +85,7 @@ def process_alloy(i,j): # Deprecated ******************************************* except: best_alloy = [-1,'placeholder'] #NOTE Make this better return best_alloy - #__| + # __| def construct_output_matrix(elem): """ @@ -95,7 +95,7 @@ def construct_output_matrix(elem): Args: elem: List of elements to be screened over """ - #| - - construct_output_matrix + # | - - construct_output_matrix o_lst = [] i_cnt = 0 @@ -105,7 +105,7 @@ def construct_output_matrix(elem): o_lst[i_cnt].append([]) i_cnt = i_cnt+1 return o_lst - #__| + # __| def finish_symmetric_matrix(elem, output_lst): """ @@ -115,7 +115,7 @@ def finish_symmetric_matrix(elem, output_lst): elem: Element list being screened over output_lst: Half filled matrix to be filled in """ - #| - - finish_symmetric_matrix + # | - - finish_symmetric_matrix i_cnt = 0 for i in elem[:-1]: j_cnt = 1 @@ -124,7 +124,7 @@ def finish_symmetric_matrix(elem, output_lst): j_cnt = j_cnt+1 i_cnt=i_cnt+1 return output_lst - #__| + # __| ################################################################################ @@ -138,7 +138,7 @@ def finish_symmetric_matrix(elem, output_lst): def run_all_binary_combinations(elements, loop_funct, scale, pt_oxid_V=0.6470339): """ """ - #| - - run_all_binary_combinations + # | - - run_all_binary_combinations o_lst = construct_output_matrix(elements) print 'constructing output V_crit matrix from scratch' @@ -166,13 +166,13 @@ def run_all_binary_combinations(elements, loop_funct, scale, pt_oxid_V=0.6470339 o_lst = finish_symmetric_matrix(elements,o_lst) return o_lst - #__| + # __| def oxidation_dissolution_product_0(i, j, scale): """ Creates Pourbaix Diagrams for single or binary systems """ - #| - - oxidation_dissolution_product_0 + # | - - oxidation_dissolution_product_0 # from pourdiag import pd_entries from pymatgen.analysis.pourbaix.maker import PourbaixDiagram @@ -212,19 +212,19 @@ def oxidation_dissolution_product_0(i, j, scale): """ return entry_lst - #__| + # __| # TEMP def ref_atoms(i,j, scale): """ TEMP """ - #| - - ref_atoms + # | - - ref_atoms ref_atoms = pd_entries(i.symbol,j.symbol) print ref_atoms #TEMP_PRINT return ref_atoms print ref_atoms # TEMP_PRINT - #__| + # __| ################################################################################ # ██████ ██ ██████ ████████ @@ -238,7 +238,7 @@ class MidpointNormalize(colors.Normalize): """ Used with diverging color schemes to set the white color to 0 """ - #| - - MidpointNormalize + # | - - MidpointNormalize def __init__(self, vmin=None, vmax=None, midpoint=None, clip=False): self.midpoint = midpoint colors.Normalize.__init__(self, vmin, vmax, clip) @@ -248,7 +248,7 @@ def __call__(self, value, clip=None): # simple example... x, y = [self.vmin, self.midpoint, self.vmax], [0, 0.5, 1] return np.ma.masked_array(np.interp(value, x, y)) - #__| + # __| def extract_data_from_matrix(output_lst): """ @@ -259,7 +259,7 @@ def extract_data_from_matrix(output_lst): Args: output_lst: Matrix containing numerical data paired with entry data """ - #| - - extract_data_from_matrix + # | - - extract_data_from_matrix data_matrix=[] cnt=0 for i in output_lst: @@ -271,7 +271,7 @@ def extract_data_from_matrix(output_lst): data_matrix[cnt].append(0) cnt=cnt+1 return data_matrix - #__| + # __| def plot_heat_map(data_matrix, elem, text_overlay=None, composition=False, show_plot=False, save_file=True, file_type='.pdf', @@ -287,24 +287,24 @@ def plot_heat_map(data_matrix, elem, text_overlay=None, composition=False, text_overlay: Matrix of data which will be overlayed on the heatmap, if ='data_value' it will overlay the numerical value for each grid point """ - #| - - plot_heat_map + # | - - plot_heat_map - #| - - Import modules + # | - - Import modules from heat_map import MidpointNormalize import matplotlib.pyplot as plt; import numpy as np from element_list import elem_str_mke - #__| + # __| - #| - - Setting colorbar range + # | - - Setting colorbar range if lock_cbar_rnge == None: vmin_=None vmax_=None elif lock_cbar_rnge != None: vmin_=lock_cbar_rnge[0] vmax_=lock_cbar_rnge[1] - #__| + # __| - #| - - Main, create heatmap, set labels, colorbar, scale plot + # | - - Main, create heatmap, set labels, colorbar, scale plot font_color = 'White' fig, ax1 = plt.subplots(1,1) @@ -319,7 +319,7 @@ def plot_heat_map(data_matrix, elem, text_overlay=None, composition=False, img = ax1.imshow( data_matrix, cmap=cmap_, interpolation='nearest', norm=norm_, vmin=vmin_, vmax=vmax_) - #| - - TEMP - Plotting heat map with either Pt_ref or RHE scaling + # | - - TEMP - Plotting heat map with either Pt_ref or RHE scaling # if heat_map_scale=='Pt_ref': # cmap_='seismic' # img = ax1.imshow( data_matrix, cmap=cmap_, interpolation='nearest', @@ -336,7 +336,7 @@ def plot_heat_map(data_matrix, elem, text_overlay=None, composition=False, # norm=MidpointNormalize(midpoint=0.)) #TEMP # img = ax1.imshow(data_matrix, cmap=cmap_, interpolation='nearest') #cmap='hot'; cmap='seismic'; cmap='Reds' - #__| + # __| elem_str = elem_str_mke(elem) @@ -377,7 +377,7 @@ def plot_heat_map(data_matrix, elem, text_overlay=None, composition=False, ################################################################################ scl = 20./23.*len(elem)+2; fig.set_figheight(scl); fig.set_figwidth(scl) - #__| + # __| if composition != False: # Adding text to plot @@ -387,7 +387,7 @@ def plot_heat_map(data_matrix, elem, text_overlay=None, composition=False, va='center', color=font_color, transform=ax1.transAxes).set_path_effects([PathEffects.withStroke(linewidth=2, foreground='w')]) - #| - - Setting text in heatmaps squares + # | - - Setting text in heatmaps squares ###################### SETTING TEXT IN EACH SQUARE ######################### ############################################################################ if text_overlay == 'data_value': @@ -431,7 +431,7 @@ def plot_heat_map(data_matrix, elem, text_overlay=None, composition=False, c = str(text_overlay[row_val.astype(int),col_val.astype(int)]) fontsize_0=54/2 - #| - - TEMP + # | - - TEMP # if row_val.astype(int)==col_val.astype(int): # c = '' # fontsize_0=10 @@ -441,16 +441,16 @@ def plot_heat_map(data_matrix, elem, text_overlay=None, composition=False, # else: # c = str(text_overlay[row_val.astype(int),col_val.astype(int)]) # fontsize_0=26/4 - #__| + # __| ax1.text(col_val, row_val, c, fontsize=fontsize_0, color=font_color, va='center', ha='center').set_path_effects([PathEffects.withStroke(linewidth=2, foreground='black')]) ############################################################################ - #__| + # __| - #| - - Saving Figure, showing figure + # | - - Saving Figure, showing figure import fnmatch; import sys; import os num_fle = len(fnmatch.filter(os.listdir('.'),'*'+file_type)) fle_nme = 'fig_heat_map'+'_'+str(num_fle)+file_type @@ -466,6 +466,6 @@ def plot_heat_map(data_matrix, elem, text_overlay=None, composition=False, fig.savefig(fle_nme, format=file_type[1:],spi=1200,transparent=transparency) if show_plot==True: fig.patch.set_facecolor('black'); plt.show() - #__| + # __| - #__| + # __| diff --git a/pourbaix_pymatgen/pd_make.py b/pourbaix_pymatgen/pd_make.py index 804922f..ef1b812 100644 --- a/pourbaix_pymatgen/pd_make.py +++ b/pourbaix_pymatgen/pd_make.py @@ -10,7 +10,7 @@ def entry_data(mtnme_1, mtnme_2, direct_0, mprester_key): mtnme_2: Material 2 direct_0: Directory of entry database for binary systems """ - #| - entry_data + # | - entry_data from pymatgen.matproj.rest import MPRester # Access species in MP import warnings; import json; from monty.json import MontyDecoder warnings.filterwarnings('ignore') # Ignore errors related to HTTP request @@ -62,12 +62,12 @@ def entry_data(mtnme_1, mtnme_2, direct_0, mprester_key): ion_dict_2 = mpr._make_request('/pourbaix_diagram/reference_data/'+mtnme_2) out_dict = {"entries" : entries, "ion_dict_1" : ion_dict_1, "ion_dict_2" : ion_dict_2} return out_dict - #__| + # __| def remove_duplicate_entries(entry_list): """ """ - #| - remove_duplicate_entries + # | - remove_duplicate_entries def contains_entry(entry_list, ent): """ Helpful to filter duplicate entries, if entry @@ -91,7 +91,7 @@ def contains_entry(entry_list, ent): entry_list_new.append(entry) return entry_list_new - #__| + # __| def aq_correction(entries): """ @@ -102,7 +102,7 @@ def aq_correction(entries): Args: entries: List of entries on which the correction will be applied """ - #| - aq_correction + # | - aq_correction from pymatgen.entries.compatibility import MaterialsProjectAqueousCompatibility def contains_entry(entry_list, ent): @@ -132,7 +132,7 @@ def contains_entry(entry_list, ent): entries_aqcorr.append(aq_corrected_entry) return entries_aqcorr -#__| +# __| def stable_entr(entries_aqcorr): """ @@ -144,7 +144,7 @@ def stable_entr(entries_aqcorr): entries_aqcorr: List of entries, usually they have been run through the aqueous compatibility module """ - #| - stable_entr + # | - stable_entr from pymatgen.analysis.pourbaix.entry import PourbaixEntry from pymatgen.phasediagram.maker import PhaseDiagram @@ -155,7 +155,7 @@ def stable_entr(entries_aqcorr): entry.composition.reduced_formula not in ["H2", "O2", "H2O", "H2O2"]] return stable_solids_minus_h2o - #__| + # __| def form_e(stable_solids_minus_h2o, entries_aqcorr, gas_gibbs=True): """ @@ -167,14 +167,14 @@ def form_e(stable_solids_minus_h2o, entries_aqcorr, gas_gibbs=True): H2O2 (from stable_entr) entries_aqcorr: entries list before being modified by stable_entr """ - #| - form_e + # | - form_e - #| - Imported Modules + # | - Imported Modules from pymatgen.analysis.pourbaix.entry import PourbaixEntry from pymatgen.phasediagram.maker import PhaseDiagram from entry_methods import base_atom from energy_scheme import ref_atoms_dict - #__| + # __| ref_atom_energies = ref_atoms_dict() e_o = ref_atom_energies['e_o'] @@ -230,7 +230,7 @@ def form_energy(entry, solid_ref_energy_dict, gas_gibbs=True): pbx_solid_entries.append(pbx_entry) return pbx_solid_entries - #__| + # __| ############################################################################### # ██ ██████ ███ ██ ███████ @@ -246,7 +246,7 @@ def ref_entry_find(stable_solids_minus_h2o, ref_state): stable_solids_minus_h2o: ref_state: """ - #| - ref_entry_find + # | - ref_entry_find # print ref_state+'_______________' # TEMP #Chk# Ion solid material reference state (defined by the dict 'Reference Solid' entry in ion_dict) @@ -261,20 +261,20 @@ def ref_entry_find(stable_solids_minus_h2o, ref_state): return '05 - Error with '+ref_state+' solid reference data' return ref_entry - #__| + # __| def ref_entry_stoich(ref_entry): """ """ - #| - ref_entry_stoich + # | - ref_entry_stoich ref_stoich_fact=ref_entry.composition.get_reduced_composition_and_factor()[1] return ref_stoich_fact - #__| + # __| def mke_pour_ion_entr(mtnme, ion_dict, stable_solids_minus_h2o, ref_state, entries_aqcorr, ref_dict): """ """ - #| - mke_pour_ion_entr + # | - mke_pour_ion_entr from pymatgen import Element # Accesses properties of element from pymatgen.core.ion import Ion from pymatgen.phasediagram.maker import PhaseDiagram @@ -310,4 +310,4 @@ def mke_pour_ion_entr(mtnme, ion_dict, stable_solids_minus_h2o, ref_state, entri pbx_ion_entries_1.append(pbx_entry_ion) return pbx_ion_entries_1 - #__| + # __| diff --git a/pourbaix_pymatgen/pd_screen_tools.py b/pourbaix_pymatgen/pd_screen_tools.py index 24d17a2..5fb68f9 100644 --- a/pourbaix_pymatgen/pd_screen_tools.py +++ b/pourbaix_pymatgen/pd_screen_tools.py @@ -1,13 +1,13 @@ def ORR_line(pH): """ """ - #| - - ORR_line + # | - - ORR_line intercept = 1.23 slope = -0.0591 V = slope*pH + intercept return V - #__| + # __| def stable_mat_one_elem(pH,V,mat): """ @@ -17,7 +17,7 @@ def stable_mat_one_elem(pH,V,mat): pH: pH of system V: Potential with reference to the Standard Hydrogen Electrode (SHE) """ - #| - - stable_mat_one_elem + # | - - stable_mat_one_elem from pourdiag_single import pourdiag_single as pd_sgle from pymatgen.analysis.pourbaix.maker import PourbaixDiagram # Access entry Gibbs(pH, V) @@ -43,7 +43,7 @@ def stable_mat_one_elem(pH,V,mat): StableSpec=i return StableSpec # Returns the entries object of the stable species - #__| + # __| def plot_reg(coord_data): """ @@ -53,7 +53,7 @@ def plot_reg(coord_data): coord_data: Coordinate data of region of interest, must be in the form of [[[x-points],[y-points]],[], ...] """ - #| - - plot_reg + # | - - plot_reg import numpy as np import matplotlib.pyplot as plt @@ -61,7 +61,7 @@ def plot_reg(coord_data): for line in coord_data: ax.plot(line[0],line[1]) plt.show() - #__| + # __| def phase_coord(entries, atom_comp, prim_elem=False): """ @@ -77,7 +77,7 @@ def phase_coord(entries, atom_comp, prim_elem=False): prim_elem is left to its default prim_elem: Primary element to which the atom_comp is assigned """ - #| - - phase_coord + # | - - phase_coord from pymatgen.analysis.pourbaix.maker import PourbaixDiagram from pymatgen.analysis.pourbaix.plotter import PourbaixPlotter from entry_methods import base_atom @@ -108,7 +108,7 @@ def phase_coord(entries, atom_comp, prim_elem=False): return pd_lst - #__| + # __| def phase_filter(phase_coord,phase_type): """ @@ -122,7 +122,7 @@ def phase_filter(phase_coord,phase_type): metallic_aqueous oxide_aqueous, aqueous_aqueous """ - #| - - phase_filter + # | - - phase_filter ## METALLIC: 1 METALLIC PHASE # For a binary system this is necessarily an alloy #TMP @@ -164,7 +164,7 @@ def phase_filter(phase_coord,phase_type): met_met_phase_lst.append(region) return met_met_phase_lst - #__| + # __| def is_solid_phase(mat1, mat2, mat1_co=0.5): """ @@ -177,7 +177,7 @@ def is_solid_phase(mat1, mat2, mat1_co=0.5): mat1_co: """ - #| - - is_solid_phase + # | - - is_solid_phase from pourdiag import pourdiag # Returns Pourbaix entries for binary system from pymatgen.analysis.pourbaix.maker import PourbaixDiagram from pymatgen.analysis.pourbaix.plotter import PourbaixPlotter @@ -209,4 +209,4 @@ def is_solid_phase(mat1, mat2, mat1_co=0.5): solidphase=True return solidphase - #__| + # __| diff --git a/pourbaix_pymatgen/read_coord_data.py b/pourbaix_pymatgen/read_coord_data.py index 974d1db..5eb07b8 100644 --- a/pourbaix_pymatgen/read_coord_data.py +++ b/pourbaix_pymatgen/read_coord_data.py @@ -6,7 +6,7 @@ def read_coord_data(elem_1,elem_2, mat1_co=0.5): elem_2: mat1_co: """ - #| - - read_coord_data + # | - - read_coord_data from monty.json import MontyEncoder, MontyDecoder import json import os @@ -62,4 +62,4 @@ def read_coord_data(elem_1,elem_2, mat1_co=0.5): reg_cnt = reg_cnt+1 return coord - #__| + # __| diff --git a/pourbaix_pymatgen/save_data.py b/pourbaix_pymatgen/save_data.py index 654acea..a6dc2ae 100644 --- a/pourbaix_pymatgen/save_data.py +++ b/pourbaix_pymatgen/save_data.py @@ -5,11 +5,11 @@ def save_data(data,file_name): data: data to be saved file_name: Desired filename including extension (.json) """ - #| - - save_data + # | - - save_data import json # Storing and Retrieving Matrix data json_dumps = json.dumps(data) f = open(file_name,'w') f.write(json_dumps); f.close() print 'Saving file: '+str(file_name) - #__| + # __| diff --git a/pourbaix_pymatgen/stability_crit.py b/pourbaix_pymatgen/stability_crit.py index 415973d..7ff7c37 100644 --- a/pourbaix_pymatgen/stability_crit.py +++ b/pourbaix_pymatgen/stability_crit.py @@ -13,16 +13,16 @@ def most_stable_phase(phase_regions,pH=None, scale='RHE', pt_oxid_V=0.6470339): Pt_ref = True, difference between the Pt V_crit and system's V_crit RHE = V_crit on an RHE scale """ - #| - - most_stable_phase + # | - - most_stable_phase - #| - - Imported Modules + # | - - Imported Modules from pd_screen_tools import ORR_line # ORR/OER V_equil, function of pH - #__| + # __| # print phase_regions[0] if pH==None: - #| - - pH==None - Considers Entire pH range + # | - - pH==None - Considers Entire pH range point_dis_ORR_lst = [] # Smallest distance from ORR paired with # Pourbaix entry object for each region @@ -46,10 +46,10 @@ def most_stable_phase(phase_regions,pH=None, scale='RHE', pt_oxid_V=0.6470339): cnt = cnt+1 most_stable_reg = min(point_dis_ORR_lst) # Closest point to ORR for # inputed regions - #__| + # __| if pH!=None: - #| - - pH is Specified + # | - - pH is Specified point_dis_ORR_lmost = [] cnt = 0 @@ -90,7 +90,7 @@ def most_stable_phase(phase_regions,pH=None, scale='RHE', pt_oxid_V=0.6470339): V_dist_from_ORR = ORR_line(pH) - V_max_total[0] V_max_total[0] = V_dist_from_ORR most_stable_reg = V_max_total - #__| + # __| # start_fold - Other Voltage References @@ -111,7 +111,7 @@ def most_stable_phase(phase_regions,pH=None, scale='RHE', pt_oxid_V=0.6470339): # end_fold return most_stable_reg - #__| + # __| def oxidation_dissolution_product(phase_regions_all, most_stable_phase): @@ -122,7 +122,7 @@ def oxidation_dissolution_product(phase_regions_all, most_stable_phase): Graphically, this is the phase which is above the stable phase (In terms of voltage), near the pH region at which stable phasae has the highest V_crit """ - #| - - oxidation_dissolution_product + # | - - oxidation_dissolution_product from pymatgen.analysis.pourbaix.maker import PREFAC import numpy as np slope = -0.0591 @@ -242,7 +242,7 @@ def binary_region_entries(region): return name_lst - #__| + # __| def is_nonox_phase(phase_regions): @@ -253,7 +253,7 @@ def is_nonox_phase(phase_regions): Args: phase_regions: PD regions which will be analyzed """ - #| - - is_nonox_phase + # | - - is_nonox_phase from pymatgen.analysis.pourbaix.entry import PourbaixEntry, IonEntry ## CHECKING FOR NON-OXIDE SOLID PHASES ## @@ -279,4 +279,4 @@ def is_nonox_phase(phase_regions): non_oxide = True break return non_oxide - #__| + # __| diff --git a/prototype_ML_proj/lennard_jones_regress/lj_regression.py b/prototype_ML_proj/lennard_jones_regress/lj_regression.py index 6fd82b2..8181a9b 100644 --- a/prototype_ML_proj/lennard_jones_regress/lj_regression.py +++ b/prototype_ML_proj/lennard_jones_regress/lj_regression.py @@ -5,7 +5,7 @@ Author: Raul A. Flores """ -#| - Import Modules +# | - Import Modules import gc import pickle @@ -23,7 +23,7 @@ from IPython.display import display, clear_output -#| - __old__ +# | - __old__ # import os # import copy # from scipy import stats @@ -37,9 +37,9 @@ # import math # from an_data_processing import load_df # from ase_modules.ase_methods import create_species_element_dict -#__| +# __| -#__| +# __| def calc_lennard_jones_form_e( atoms_i=None, @@ -62,7 +62,7 @@ def calc_lennard_jones_form_e( epsilon: sigma: """ - #| - calc_lennard_jones_form_e + # | - calc_lennard_jones_form_e E_H = lennard_jones_sp(epsilon, sigma, atoms_H2) E_O = lennard_jones_sp(epsilon, sigma, atoms_O2) E_Ir = lennard_jones_sp(epsilon, sigma, atoms_Ir) @@ -105,7 +105,7 @@ def calc_lennard_jones_form_e( E_out = E_form_per_atom_i return(E_out) - #__| + # __| def calc_lennard_jones_all_atoms( pars, @@ -123,7 +123,7 @@ def calc_lennard_jones_all_atoms( reference_atoms: return_quantity: """ - #| - calc_lennard_jones_all_atoms + # | - calc_lennard_jones_all_atoms epsilon = pars[0] sigma = pars[1] @@ -139,7 +139,7 @@ def calc_lennard_jones_all_atoms( if return_quantity == "energies": for atoms_i in atoms_list: - #| - Energy + # | - Energy lj_energy_i = calc_lennard_jones_form_e( atoms_i=atoms_i, atoms_H2=atoms_H2, @@ -150,7 +150,7 @@ def calc_lennard_jones_all_atoms( ) predicted_energies.append(lj_energy_i) - #__| + # __| predicted_energies = np.array(predicted_energies) return(predicted_energies) @@ -158,7 +158,7 @@ def calc_lennard_jones_all_atoms( if return_quantity == "forces": for atoms_i in atoms_list: - #| - Forces + # | - Forces lj_forces_i = lennard_jones_sp( epsilon, sigma, @@ -169,12 +169,12 @@ def calc_lennard_jones_all_atoms( ) predicted_forces.append(lj_forces_i) - #__| + # __| predicted_forces = np.array(predicted_forces) return(predicted_forces) - #__| + # __| def objective( pars, @@ -199,7 +199,7 @@ def objective( sig_shape: reference_atoms: """ - #| - objective + # | - objective epsilon, sigma = unflatten_eps_sig_array( pars, eps_shape, @@ -207,7 +207,7 @@ def objective( elem_list, ) - #| - Energy Term + # | - Energy Term err = known_energies - \ calc_lennard_jones_all_atoms( (epsilon, sigma), @@ -221,9 +221,9 @@ def objective( energy_term = numerator_i / denominator_i MSE = np.mean(err ** 2) - #__| + # __| - #| - Force Term + # | - Force Term def calc_sum_of_normals_of_forces(forces): """Calculate sum of normals of forces on each atom. @@ -231,13 +231,13 @@ def calc_sum_of_normals_of_forces(forces): forces: List of 3 components of force for each atom """ - #| - calc_sum_of_normals_of_forces + # | - calc_sum_of_normals_of_forces sum_of_normals = 0. for atom_forces_i in forces: sum_of_normals += np.linalg.norm(atom_forces_i) return(sum_of_normals) - #__| + # __| sum_of_structures_forces_known = 0. @@ -259,18 +259,18 @@ def calc_sum_of_normals_of_forces(forces): force_term = sum_of_structures_forces / sum_of_structures_forces_known - #__| + # __| - #| - Score Function + # | - Score Function w_energy = 1. w_force = 0.0001 score_function = w_energy * energy_term + w_force * force_term print(score_function) - #__| + # __| - #| - Display Real-time Info + # | - Display Real-time Info clear_output(wait=True) display("Iter: " + str(info["Nfeval"])) @@ -294,12 +294,12 @@ def calc_sum_of_normals_of_forces(forces): display(sigma.values) info["Nfeval"] += 1 - #__| + # __| return(score_function) # return(MSE) - #__| + # __| def flatten_eps_sig_triangular_matrices( epsilon, @@ -312,7 +312,7 @@ def flatten_eps_sig_triangular_matrices( epsilon: sigma: """ - #| - flatten_eps_sig_triangular_matrices + # | - flatten_eps_sig_triangular_matrices epsilon = epsilon.values sigma = sigma.values @@ -336,7 +336,7 @@ def flatten_eps_sig_triangular_matrices( ) return(flat_eps_sig_diag) - #__| + # __| def unflatten_eps_sig_array( flat_eps_sig, @@ -351,9 +351,9 @@ def unflatten_eps_sig_array( eps_shape: sig_shape: """ - #| - unflatten_eps_sig_array + # | - unflatten_eps_sig_array - #| - Array Dimension Check + # | - Array Dimension Check assert eps_shape[0] == eps_shape[1] assert sig_shape[0] == sig_shape[1] @@ -363,7 +363,7 @@ def unflatten_eps_sig_array( assert N_eps == N_sig N = N_eps - #__| + # __| len_pars = len(flat_eps_sig) half_way = int(len_pars / 2) @@ -376,7 +376,7 @@ def unflatten_eps_sig_array( else: pars_mode = "triangular" - #| - Methods + # | - Methods def unflatten_tri_matrix_with_defined_cross_terms( flat_array, N, @@ -392,7 +392,7 @@ def unflatten_tri_matrix_with_defined_cross_terms( cross_terms_mode: "geo" or "ave" """ - #| - unflatten_tri_matrix_with_defined_cross_terms + # | - unflatten_tri_matrix_with_defined_cross_terms matrix = np.diag(flat_array) # Return list of i, j indices corresponding to the off diagonal @@ -432,7 +432,7 @@ def unflatten_tri_matrix_with_defined_cross_terms( matrix[i_ind][j_ind] = average_ij return(matrix) - #__| + # __| def unflatten_single_triangular_matrix(flat_array, N): """Unflatten a single triangular matrix. @@ -441,7 +441,7 @@ def unflatten_single_triangular_matrix(flat_array, N): flat_array: N: """ - #| - unflatten_single_traingular_matrix + # | - unflatten_single_traingular_matrix start_index_list = [] stop_index_list = [] j_cnt = 0 @@ -468,9 +468,9 @@ def unflatten_single_triangular_matrix(flat_array, N): rebuilt_matrix = np.array(rebuilt_matrix) return(rebuilt_matrix) - #__| + # __| - #__| + # __| if pars_mode == "triangular": epsilon = unflatten_single_triangular_matrix(epsilon_short, N) @@ -504,7 +504,7 @@ def unflatten_single_triangular_matrix(flat_array, N): return(epsilon, sigma) - #__| + # __| def fit_LJ_to_DFT( objective=None, @@ -533,7 +533,7 @@ def fit_LJ_to_DFT( maxiter: maxfun: """ - #| - fit_LJ_to_DFT + # | - fit_LJ_to_DFT atoms_H2 = reference_atoms[0] atoms_O2 = reference_atoms[1] atoms_Ir = reference_atoms[2] @@ -551,7 +551,7 @@ def fit_LJ_to_DFT( mode=params_mode, # 'triangular' or 'diagonal' ) - #| - Minimize Method + # | - Minimize Method opt_out = minimize( objective, pars, @@ -593,7 +593,7 @@ def fit_LJ_to_DFT( "disp": True, }, ) - #__| + # __| LJ_pars = opt_out.x @@ -608,7 +608,7 @@ def fit_LJ_to_DFT( pickle.dump((epsilon_out, sigma_out), fle) return(epsilon_out, sigma_out) - #__| + # __| def calc_MSE( pars, @@ -624,7 +624,7 @@ def calc_MSE( DFT_energies_col: ref_atoms_list: """ - #| - calc_MSE + # | - calc_MSE # df_i = df_orig.iloc[cv_folds_indices[0]["testing"]] epsilon = pars[0] sigma = pars[1] @@ -643,7 +643,7 @@ def calc_MSE( MSE = np.mean(err ** 2) return(MSE) - #__| + # __| def k_fold_cross_validation(data, k=5): """k-fold cross-validation indices list. @@ -652,7 +652,7 @@ def k_fold_cross_validation(data, k=5): data: k: """ - #| - k_fold_cross_validation + # | - k_fold_cross_validation folds = np.array_split(data, k) cv_data = [] @@ -669,4 +669,4 @@ def k_fold_cross_validation(data, k=5): }) return(cv_data) - #__| + # __| diff --git a/prototype_ML_proj/lennard_jones_regress/old.lj_regression_180724.py b/prototype_ML_proj/lennard_jones_regress/old.lj_regression_180724.py index 34babd0..4d08b97 100644 --- a/prototype_ML_proj/lennard_jones_regress/old.lj_regression_180724.py +++ b/prototype_ML_proj/lennard_jones_regress/old.lj_regression_180724.py @@ -5,7 +5,7 @@ Author: Raul A. Flores """ -#| - Import Modules +# | - Import Modules import gc import pickle @@ -21,7 +21,7 @@ from IPython.display import display, clear_output -#| - __old__ +# | - __old__ # import os # import copy # from scipy import stats @@ -35,9 +35,9 @@ # import math # from an_data_processing import load_df # from ase_modules.ase_methods import create_species_element_dict -#__| +# __| -#__| +# __| def calc_lennard_jones_form_e( atoms_i=None, @@ -60,7 +60,7 @@ def calc_lennard_jones_form_e( epsilon: sigma: """ - #| - calc_lennard_jones_form_e + # | - calc_lennard_jones_form_e E_H = lennard_jones_sp(epsilon, sigma, atoms_H2) E_O = lennard_jones_sp(epsilon, sigma, atoms_O2) E_Ir = lennard_jones_sp(epsilon, sigma, atoms_Ir) @@ -103,7 +103,7 @@ def calc_lennard_jones_form_e( E_out = E_form_per_atom_i return(E_out) - #__| + # __| def calc_lennard_jones_all_atoms( pars, @@ -121,7 +121,7 @@ def calc_lennard_jones_all_atoms( reference_atoms: return_quantity: """ - #| - calc_lennard_jones_all_atoms + # | - calc_lennard_jones_all_atoms epsilon = pars[0] sigma = pars[1] @@ -137,7 +137,7 @@ def calc_lennard_jones_all_atoms( if return_quantity == "energies": for atoms_i in atoms_list: - #| - Energy + # | - Energy lj_energy_i = calc_lennard_jones_form_e( atoms_i=atoms_i, atoms_H2=atoms_H2, @@ -148,7 +148,7 @@ def calc_lennard_jones_all_atoms( ) predicted_energies.append(lj_energy_i) - #__| + # __| predicted_energies = np.array(predicted_energies) return(predicted_energies) @@ -156,7 +156,7 @@ def calc_lennard_jones_all_atoms( if return_quantity == "forces": for atoms_i in atoms_list: - #| - Forces + # | - Forces lj_forces_i = lennard_jones_sp( epsilon, sigma, @@ -167,7 +167,7 @@ def calc_lennard_jones_all_atoms( ) predicted_forces.append(lj_forces_i) - #__| + # __| predicted_forces = np.array(predicted_energies) @@ -175,7 +175,7 @@ def calc_lennard_jones_all_atoms( # predicted_energies = np.array(predicted_energies) # return(predicted_energies) - #__| + # __| def objective( pars, @@ -200,7 +200,7 @@ def objective( sig_shape: reference_atoms: """ - #| - objective + # | - objective epsilon, sigma = unflatten_eps_sig_array( pars, eps_shape, @@ -208,7 +208,7 @@ def objective( elem_list, ) - #| - Energy Term + # | - Energy Term err = known_energies - \ calc_lennard_jones_all_atoms( (epsilon, sigma), @@ -217,9 +217,9 @@ def objective( ) MSE = np.mean(err ** 2) - #__| + # __| - #| - Force Term + # | - Force Term def calc_sum_of_normals_of_forces(forces): """Calculate sum of normals of forces on each atom. @@ -227,13 +227,13 @@ def calc_sum_of_normals_of_forces(forces): forces: List of 3 components of force for each atom """ - #| - calc_sum_of_normals_of_forces + # | - calc_sum_of_normals_of_forces sum_of_normals = 0. for atom_forces_i in forces: sum_of_normals += np.linalg.norm(atom_forces_i) return(sum_of_normals) - #__| + # __| known_sum_of_normals = calc_sum_of_normals_of_forces(known_forces) @@ -255,7 +255,7 @@ def calc_sum_of_normals_of_forces(forces): # print(tmp) print(30 * "&") - #__| + # __| # clear_output(wait=True) @@ -280,7 +280,7 @@ def calc_sum_of_normals_of_forces(forces): info["Nfeval"] += 1 return(MSE) - #__| + # __| def flatten_eps_sig_triangular_matrices( epsilon, @@ -293,7 +293,7 @@ def flatten_eps_sig_triangular_matrices( epsilon: sigma: """ - #| - flatten_eps_sig_triangular_matrices + # | - flatten_eps_sig_triangular_matrices epsilon = epsilon.values sigma = sigma.values @@ -317,7 +317,7 @@ def flatten_eps_sig_triangular_matrices( ) return(flat_eps_sig_diag) - #__| + # __| def unflatten_eps_sig_array( flat_eps_sig, @@ -332,9 +332,9 @@ def unflatten_eps_sig_array( eps_shape: sig_shape: """ - #| - unflatten_eps_sig_array + # | - unflatten_eps_sig_array - #| - Array Dimension Check + # | - Array Dimension Check assert eps_shape[0] == eps_shape[1] assert sig_shape[0] == sig_shape[1] @@ -344,7 +344,7 @@ def unflatten_eps_sig_array( assert N_eps == N_sig N = N_eps - #__| + # __| len_pars = len(flat_eps_sig) half_way = int(len_pars / 2) @@ -357,7 +357,7 @@ def unflatten_eps_sig_array( else: pars_mode = "triangular" - #| - Methods + # | - Methods def unflatten_tri_matrix_with_defined_cross_terms( flat_array, N, @@ -373,7 +373,7 @@ def unflatten_tri_matrix_with_defined_cross_terms( cross_terms_mode: "geo" or "ave" """ - #| - unflatten_tri_matrix_with_defined_cross_terms + # | - unflatten_tri_matrix_with_defined_cross_terms matrix = np.diag(flat_array) # Return list of i, j indices corresponding to the off diagonal @@ -413,7 +413,7 @@ def unflatten_tri_matrix_with_defined_cross_terms( matrix[i_ind][j_ind] = average_ij return(matrix) - #__| + # __| def unflatten_single_triangular_matrix(flat_array, N): """Unflatten a single triangular matrix. @@ -422,7 +422,7 @@ def unflatten_single_triangular_matrix(flat_array, N): flat_array: N: """ - #| - unflatten_single_traingular_matrix + # | - unflatten_single_traingular_matrix start_index_list = [] stop_index_list = [] j_cnt = 0 @@ -449,9 +449,9 @@ def unflatten_single_triangular_matrix(flat_array, N): rebuilt_matrix = np.array(rebuilt_matrix) return(rebuilt_matrix) - #__| + # __| - #__| + # __| if pars_mode == "triangular": epsilon = unflatten_single_triangular_matrix(epsilon_short, N) @@ -485,7 +485,7 @@ def unflatten_single_triangular_matrix(flat_array, N): return(epsilon, sigma) - #__| + # __| def fit_LJ_to_DFT( objective=None, @@ -514,7 +514,7 @@ def fit_LJ_to_DFT( maxiter: maxfun: """ - #| - fit_LJ_to_DFT + # | - fit_LJ_to_DFT atoms_H2 = reference_atoms[0] atoms_O2 = reference_atoms[1] atoms_Ir = reference_atoms[2] @@ -532,7 +532,7 @@ def fit_LJ_to_DFT( mode=params_mode, # 'triangular' or 'diagonal' ) - #| - Minimize Method + # | - Minimize Method opt_out = minimize( objective, pars, @@ -574,7 +574,7 @@ def fit_LJ_to_DFT( "disp": True, }, ) - #__| + # __| LJ_pars = opt_out.x @@ -589,7 +589,7 @@ def fit_LJ_to_DFT( pickle.dump((epsilon_out, sigma_out), fle) return(epsilon_out, sigma_out) - #__| + # __| def calc_MSE( pars, @@ -605,7 +605,7 @@ def calc_MSE( DFT_energies_col: ref_atoms_list: """ - #| - calc_MSE + # | - calc_MSE # df_i = df_orig.iloc[cv_folds_indices[0]["testing"]] epsilon = pars[0] sigma = pars[1] @@ -624,7 +624,7 @@ def calc_MSE( MSE = np.mean(err ** 2) return(MSE) - #__| + # __| def k_fold_cross_validation(data, k=5): """k-fold cross-validation indices list. @@ -633,7 +633,7 @@ def k_fold_cross_validation(data, k=5): data: k: """ - #| - k_fold_cross_validation + # | - k_fold_cross_validation folds = np.array_split(data, k) cv_data = [] @@ -650,4 +650,4 @@ def k_fold_cross_validation(data, k=5): }) return(cv_data) - #__| + # __| diff --git a/quantum_espresso/qe_methods.py b/quantum_espresso/qe_methods.py index 199d547..aec06ef 100644 --- a/quantum_espresso/qe_methods.py +++ b/quantum_espresso/qe_methods.py @@ -5,14 +5,14 @@ Author: Raul A. Flores """ -#| - Import Modules +# | - Import Modules import os import pandas as pd import numpy as np -#__| +# __| -#| - Log File Methods +# | - Log File Methods def number_of_atoms(path_i=".", log="log"): """Return number of atoms from QE log file. @@ -21,7 +21,7 @@ def number_of_atoms(path_i=".", log="log"): path_i: log: """ - #| - number_of_atoms + # | - number_of_atoms file_name = path_i + "/" + log with open(file_name, "r") as fle: fle.seek(0) # just in case @@ -57,7 +57,7 @@ def tot_abs_magnetization(path_i=".", log="log"): path_i log """ - #| - tot_abs_magnetization + # | - tot_abs_magnetization fle = open(log, "r") fle.seek(0) # just in case @@ -70,7 +70,7 @@ def tot_abs_magnetization(path_i=".", log="log"): if not line: break - #| - Searching for Atomic Magmoms + # | - Searching for Atomic Magmoms if "total magnetization" in line: line_list = line.strip().split(" ") line = [i for i in line_list if i != ""] @@ -83,7 +83,7 @@ def tot_abs_magnetization(path_i=".", log="log"): abs_mag = float(line[3]) abs_mag_list.append(abs_mag) - #__| + # __| df_tot = pd.DataFrame(tot_mag_list, columns=["tot_mag"]) df_abs = pd.DataFrame(abs_mag_list, columns=["abs_mag"]) @@ -91,7 +91,7 @@ def tot_abs_magnetization(path_i=".", log="log"): df = pd.concat([df_tot, df_abs], axis=1) return(df) - #__| + # __| def element_index_dict(path_i=".", log="log"): """Return index: element dictionary. @@ -102,7 +102,7 @@ def element_index_dict(path_i=".", log="log"): path_i log """ - #| - element_index_dict + # | - element_index_dict elem_ind_dict = {} file_name = path_i + "/" + log with open(file_name, "r") as fle: @@ -115,7 +115,7 @@ def element_index_dict(path_i=".", log="log"): if not line: break - #| - Atom Index <---> Atom Type + # | - Atom Index <---> Atom Type if "Cartesian axes" in line: fle.readline() # Blank line fle.readline() # Column header line @@ -138,10 +138,10 @@ def element_index_dict(path_i=".", log="log"): else: break - #__| + # __| return(elem_ind_dict) - #__| + # __| def magmom_charge_data(path_i=".", log="log"): """Return charge and magmom data per atom for all SCF iterations. @@ -150,9 +150,9 @@ def magmom_charge_data(path_i=".", log="log"): path_i log """ - #| - magmom_charge_data + # | - magmom_charge_data - #| - Reading Log File + # | - Reading Log File file_name = path_i + "/" + log fle = open(file_name, "r") @@ -166,7 +166,7 @@ def magmom_charge_data(path_i=".", log="log"): if not line: break - #| - Searching for Atomic Magmoms + # | - Searching for Atomic Magmoms if "Magnetic moment per site" in line: list_i = [] @@ -193,11 +193,11 @@ def magmom_charge_data(path_i=".", log="log"): break master_list.append(list_i) - #__| + # __| - #__| + # __| - #| - Creating Pandas DataFrame + # | - Creating Pandas DataFrame if master_list == []: print("Magmom/charge data not found, ", "calculation probably not spin-polarized" @@ -221,10 +221,10 @@ def magmom_charge_data(path_i=".", log="log"): # Resetting index df.reset_index() - #__| + # __| return(df) - #__| + # __| def estimate_magmom( path_i=".", @@ -242,7 +242,7 @@ def estimate_magmom( and assign to atoms object to assist with calculation restart upon unexpected interruption. """ - #| - estimate_magmom + # | - estimate_magmom num_atoms = number_of_atoms(path_i=path_i, log=log) # print(num_atoms) @@ -254,7 +254,7 @@ def estimate_magmom( i = len(lines) - 1 while True: - #| - If magmom/charge data is not found in log file + # | - If magmom/charge data is not found in log file # The calculation is probably not spin-polarized if i == 0: print("estimate_magmom - Could not find magmom/charge data \n", @@ -264,7 +264,7 @@ def estimate_magmom( break # raise IOError("Could not identify espresso magmoms") - #__| + # __| line = lines[i].split() if len(line) > 3: @@ -315,7 +315,7 @@ def estimate_magmom( atoms.info.update({"qe_log_charges": charge_list}) return(magmom_list, charge_list) - #__| + # __| def scf_convergence(path_i=".", log="log"): """Return SCF convergence vs iteration. @@ -328,7 +328,7 @@ def scf_convergence(path_i=".", log="log"): Args: log """ - #| - scf_convergence + # | - scf_convergence rydberg = 13.6057 # rydberg to eV conversion filename = os.path.join( @@ -374,6 +374,6 @@ def scf_convergence(path_i=".", log="log"): os.system("rm %s" % filename) return(scf, iter, pw) - #__| + # __| -#__| +# __| diff --git a/raman_dft/vasp_raman_job_methods.py b/raman_dft/vasp_raman_job_methods.py index 2bcc0e5..dbc6dd9 100644 --- a/raman_dft/vasp_raman_job_methods.py +++ b/raman_dft/vasp_raman_job_methods.py @@ -1,6 +1,6 @@ """Methods to run VASP-Raman jobs.""" -#| - IMPORT MODULES +# | - IMPORT MODULES import os import sys from math import sqrt @@ -10,12 +10,12 @@ from plotly.graph_objs import Scatter import re -#__| +# __| -#| - Methods from vasp_raman script (Github) +# | - Methods from vasp_raman script (Github) def get_modes_from_OUTCAR(outcar_fh, nat=None, free_nat=None, path_i="."): - #| - get_modes_from_OUTCAR + # | - get_modes_from_OUTCAR if nat == None: name = "OUTCAR.phon" @@ -76,10 +76,10 @@ def get_modes_from_OUTCAR(outcar_fh, nat=None, free_nat=None, path_i="."): " division by SQRT(mass)'' in OUTCAR.", " Use 'NWRITE=3' in INCAR. Exiting...") sys.exit(1) - #__| + # __| def parse_env_params(params): - #| - parse_env_params + # | - parse_env_params tmp = params.strip().split("_") if len(tmp) != 4: print("[parse_env_params]: ERROR there should be exactly four parameters") @@ -88,10 +88,10 @@ def parse_env_params(params): [first, last, nderiv, step_size] = [int(tmp[0]), int(tmp[1]), int(tmp[2]), float(tmp[3])] return first, last, nderiv, step_size - #__| + # __| def parse_poscar(poscar_fh, cons_atoms=0): - #| - parse_poscar + # | - parse_poscar # modified subroutine from phonopy 1.8.3 (New BSD license) poscar_fh.seek(0) # just in case @@ -141,25 +141,25 @@ def parse_poscar(poscar_fh, cons_atoms=0): poscar_header = "".join(lines[1:line_at-1]) # will add title and "Cartesian" later return nat, free_nat, vol, b, positions, poscar_header - #__| + # __| def MAT_m_VEC(m, v): - #| - MAT_m_VEC + # | - MAT_m_VEC p = [ 0.0 for i in range(len(v)) ] for i in range(len(m)): assert len(v) == len(m[i]), "Length of the matrix row is not equal to the length of the vector" p[i] = sum( [ m[i][j]*v[j] for j in range(len(v)) ] ) return p - #__| + # __| def T(m): - #| - T + # | - T p = [[ m[i][j] for i in range(len( m[j] )) ] for j in range(len( m )) ] return p - #__| + # __| def get_epsilon_from_OUTCAR(outcar_fh): - #| - get_epsilon_from_OUTCAR + # | - get_epsilon_from_OUTCAR epsilon = [] outcar_fh.seek(0) # just in case @@ -178,13 +178,13 @@ def get_epsilon_from_OUTCAR(outcar_fh): raise RuntimeError("[get_epsilon_from_OUTCAR]: ERROR Couldn't find dielectric tensor in OUTCAR") return 1 - #__| + # __| -#| - Substitute Functions for VTST +# | - Substitute Functions for VTST def parse_freqdat(freqdat_fh, nat): - #| - parse_freqdat + # | - parse_freqdat freqdat_fh.seek(0) # just in case # eigvals = [ 0.0 for i in range(nat*3) ] @@ -194,10 +194,10 @@ def parse_freqdat(freqdat_fh, nat): eigvals[i] = float(tmp[0]) # return eigvals - #__| + # __| def parse_modesdat(modesdat_fh, nat): - #| - parse_modesdat + # | - parse_modesdat # from math import sqrt modesdat_fh.seek(0) # just in case # @@ -215,11 +215,11 @@ def parse_modesdat(modesdat_fh, nat): norms[i] = sqrt( sum( [abs(x)**2 for sublist in eigvec for x in sublist] ) ) # return eigvecs, norms - #__| + # __| -#__| +# __| -#__| +# __| def modes_list(num_modes, step, mode_0=1, modes_to_run="All"): """Returns list of strings representing number of modes to run per job @@ -228,7 +228,7 @@ def modes_list(num_modes, step, mode_0=1, modes_to_run="All"): atoms: modes_to_run: """ - #| - modes_list + # | - modes_list # atoms = io.read("POSCAR.phon") @@ -266,7 +266,7 @@ def modes_list(num_modes, step, mode_0=1, modes_to_run="All"): return(modes_list) - #__| + # __| def vasp_raman_input_file(modes, dir="."): """ @@ -276,7 +276,7 @@ def vasp_raman_input_file(modes, dir="."): dir: Directory to write the vasp_raman input file to """ - #| - vasp_raman_input_file + # | - vasp_raman_input_file VASP_RAMAN_RUN = "python 3_vasp_raman_run_ibrion_n1.py" #| Changing "-" character in mode to "_" @@ -289,15 +289,15 @@ def vasp_raman_input_file(modes, dir="."): with open(dir + "/" + filename, "w") as file: file.write(VASP_RAMAN_PARAMS + "\n" + VASP_RAMAN_RUN + "\n") - #__| + # __| def concatenate_mode_files(): """ """ - #| - concatenate_mode_filess + # | - concatenate_mode_filess tmp = 7 - #__| + # __| def to_plot(hw, ab, gam=0.05, type="Gaussian", scaling=1.): """ @@ -309,7 +309,7 @@ def to_plot(hw, ab, gam=0.05, type="Gaussian", scaling=1.): type: scaling: """ - #| - to_plot + # | - to_plot ab /= np.max(np.abs(ab), axis=0) ab = ab * scaling @@ -331,7 +331,7 @@ def to_plot(hw, ab, gam=0.05, type="Gaussian", scaling=1.): spectrum += ab[i]*1/np.pi*gam/((hw[i]-erange)**2+gam**2) return erange, spectrum - #__| + # __| def proc_data( data, @@ -351,7 +351,7 @@ def proc_data( gauss_gamma=8.0: color_palette=None: """ - #| - proc_data + # | - proc_data data_sets = data plot_data_list = [] @@ -365,14 +365,14 @@ def proc_data( x_dat = np.array(x_dat) + freq_shift - #| - Creating Gaussian Curves From Frequencies and Activities + # | - Creating Gaussian Curves From Frequencies and Activities x_dat, y_dat = to_plot( x_dat, y_dat, gam=gauss_gamma, scaling=scaling ) - #__| + # __| if color_palette is None: data_i = Scatter( @@ -398,4 +398,4 @@ def proc_data( plot_data_list.append(data_i) return(plot_data_list) - #__| + # __| diff --git a/readthedocs.py b/readthedocs.py index 84f3c2d..cb47ae2 100644 --- a/readthedocs.py +++ b/readthedocs.py @@ -8,8 +8,8 @@ def tmp_method(var): Args: var: """ - #| - tmp_method + # | - tmp_method var + 2 return("hello world") - #__| + # __| diff --git a/surface_energy/surface_energy.py b/surface_energy/surface_energy.py index e0f67bc..1dfd082 100644 --- a/surface_energy/surface_energy.py +++ b/surface_energy/surface_energy.py @@ -5,7 +5,7 @@ Author: Raul A. Flores """ -#| - IMPORT MODULES +# | - IMPORT MODULES import numpy as np import pandas as pd @@ -14,7 +14,7 @@ from ase_modules.ase_methods import create_species_element_dict from pymatgen.core.composition import Composition -#__| +# __| class SurfaceEnergy: @@ -25,11 +25,11 @@ class SurfaceEnergy: try to add check for this """ - #| - SurfaceEnergy ******************************************************** + # | - SurfaceEnergy ******************************************************** _TEMP = "TEMP" def __init__(self, - #| - TEMP | ARGS ------------------------------------------------------ + # | - TEMP | ARGS ------------------------------------------------------ atoms=None, electronic_energy=None, # Only needed if not within atoms object @@ -47,13 +47,13 @@ def __init__(self, pH=0., verbose=True, - #__| ------------------------------------------------------------------ + # __| ------------------------------------------------------------------ ): """ """ - #| - __init__ + # | - __init__ - #| - Setting Argument Instance Attributes + # | - Setting Argument Instance Attributes self.atoms = atoms self.electronic_energy = electronic_energy @@ -73,9 +73,9 @@ def __init__(self, self.pH = pH self.verbose = verbose - #__| + # __| - #| - Initializing Internal Instance Attributes + # | - Initializing Internal Instance Attributes self.__num_atoms_reduced_bulk__ = None self.non_stoich_comp = None @@ -85,7 +85,7 @@ def __init__(self, self.__bulk_energy_per_atom__ = None self.non_stoich_comp_new = dict() - #__| + # __| self.surface_area = self.__calc_surface_area__() @@ -120,7 +120,7 @@ def __init__(self, self.__calc_std_surface_energy_per_surface_atom__() self.slab_thickness = self.__calc_slab_thickness__() - #__| + # __| def calc_surface_energy(self, bias, pH, norm_type="area"): """ @@ -128,7 +128,7 @@ def calc_surface_energy(self, bias, pH, norm_type="area"): Args: norm_type: 'area', 'surface_atom', None """ - #| - calc_surface_energy + # | - calc_surface_energy surface_e_per_side = self.std_surface_e_per_side non_stoich_comp = self.non_stoich_comp @@ -163,12 +163,12 @@ def calc_surface_energy(self, bias, pH, norm_type="area"): return(surf_e_V_ph__norm) - #__| + # __| def calc_std_surface_energy(self): """ """ - #| - calc_std_surface_energy + # | - calc_std_surface_energy electronic_energy = self.electronic_energy bulk_formula_units_in_slab = self.__bulk_formula_units_in_slab__ @@ -219,7 +219,7 @@ def calc_std_surface_energy(self): surf_e_0 /= 2 return(surf_e_0) - #__| + # __| def __count_special_surface_species__(self): @@ -228,7 +228,7 @@ def __count_special_surface_species__(self): Taks the non-stoich dict and pairs the max number of O and H pairs into *OH and the remaining O's into *O """ - #| - __count_special_surface_species__ + # | - __count_special_surface_species__ import copy non_stoich_comp = self.non_stoich_comp @@ -291,11 +291,11 @@ def __count_special_surface_species__(self): self.non_stoich_comp_new = non_stoich_comp_new return(special_species_dict) - #__| + # __| def __calc_surface_energy_per_area__(self, unnorm_surface_e=None): """Normalize the surface energy to surface area (A^2).""" - #| - __calc_surface_energy_per_area__ + # | - __calc_surface_energy_per_area__ surface_area = self.surface_area # surface_e_per_side = self.std_surface_e_per_side surface_e_per_side = unnorm_surface_e @@ -303,25 +303,25 @@ def __calc_surface_energy_per_area__(self, unnorm_surface_e=None): surface_e_per_area = surface_e_per_side / surface_area return(surface_e_per_area) - #__| + # __| def __calc_std_surface_energy_per_surface_atom__(self): """Normalize the surface area to a per surface atom basis.""" - #| - calc_std_surface_energy_per_area + # | - calc_std_surface_energy_per_area num_surface_atoms = self.num_surface_atoms surface_e_per_side = self.std_surface_e_per_side surface_e_per_surface_atoms = surface_e_per_side / num_surface_atoms return(surface_e_per_surface_atoms) - #__| + # __| def __calc_surface_area__(self): """ """ - #| - __calc_surface_area__ + # | - __calc_surface_area__ atoms = self.atoms cell = atoms.cell @@ -330,7 +330,7 @@ def __calc_surface_area__(self): area_i = np.linalg.norm(cross_prod_i) return(area_i) - #__| + # __| def __get_electronic_energy__(self, @@ -339,7 +339,7 @@ def __get_electronic_energy__(self, per_atom=False): """ """ - #| - __get_electronic_energy__ + # | - __get_electronic_energy__ energy_out = None if electronic_energy is not None: @@ -361,12 +361,12 @@ def __get_electronic_energy__(self, raise ValueError('No where to get energy from!!!') return(energy_out) - #__| + # __| def __calc_units_of_bulk_in_slab__(self): """ """ - #| - __calc_units_of_bulk_in_slab__ + # | - __calc_units_of_bulk_in_slab__ # TODO main_atom = "Ir" # Make this a class attribute @@ -418,12 +418,12 @@ def __calc_units_of_bulk_in_slab__(self): # print(non_stoich_comp) return(bulk_formula_units_in_slab) - #__| + # __| def __calc_units_of_reduced_bulk_in_bulk__(self): """ """ - #| - __calc_units_of_reduced_bulk_in_bulk__ + # | - __calc_units_of_reduced_bulk_in_bulk__ bulk_atoms = self.bulk_atoms bulk_atoms.get_chemical_formula() @@ -456,12 +456,12 @@ def __calc_units_of_reduced_bulk_in_bulk__(self): # print(bulk_reduction_factor) return(bulk_reduction_factor) - #__| + # __| def __calc_bulk_energy_per_formula_unit__(self): """ """ - #| - __calc_bulk_energy_per_formula_unit__ + # | - __calc_bulk_energy_per_formula_unit__ # bulk_formula_reduction = self.__bulk_formula_reduction__ bulk_electronic_energy = self.bulk_electronic_energy num_atoms_reduced_bulk = self.__num_atoms_reduced_bulk__ @@ -477,11 +477,11 @@ def __calc_bulk_energy_per_formula_unit__(self): bulk_electronic_energy / num_atoms_bulk return(bulk_electronic_energy_per_formula) - #__| + # __| def __calc_slab_thickness__(self): """Calculate the thickness of the atoms slab.""" - #| - __calc_slab_thickness__ + # | - __calc_slab_thickness__ atoms = self.atoms positions = atoms.positions @@ -489,11 +489,11 @@ def __calc_slab_thickness__(self): slab_thickness = max(positions[:, 2]) - min(positions[:, 2]) return(slab_thickness) - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** @@ -505,7 +505,7 @@ class SurfaceEnergyConvergence: https://iopscience.iop.org/article/10.1088/0953-8984/8/36/005 """ - #| - SurfaceEnergyConvergence ********************************************* + # | - SurfaceEnergyConvergence ********************************************* _TEMP = "TEMP" def __init__(self, @@ -523,21 +523,21 @@ def __init__(self, Thin slabs will suffer from finite size effects, so they should not be included in regression """ - #| - __init__ + # | - __init__ - #| - Setting Argument Instance Attributes + # | - Setting Argument Instance Attributes self.SurfaceEnergy_instances = SurfaceEnergy_instances self.num_points_to_exclude = num_points_to_exclude self.bulk_electronic_energy_per_atom = bulk_electronic_energy_per_atom self.verbose = verbose - #__| + # __| - #| - Initializing Internal Instance Attributes + # | - Initializing Internal Instance Attributes self.sufficient_data_to_fit_bulk = True self.fitted_bulk_energy = None self.new_SurfaceEnergy_instances = [] self.new_ave_surface_energy_per_area = None - #__| + # __| self.df = self.__init_dataframe__() @@ -548,13 +548,13 @@ def __init__(self, # Enough data to fit? self.__sufficient_data_to_fit_bulk() - #__| + # __| def calculate_surface_energies(self, bulk_energy=None): """ """ - #| - calculate_surface_energies + # | - calculate_surface_energies self.new_SurfaceEnergy_instances = \ self.__recalc_SurfaceEnergy_w_new_bulk__( bulk_energy) @@ -564,21 +564,21 @@ def calculate_surface_energies(self, bulk_energy=None): self.new_ave_surface_energy_per_area = \ self.__calc_ave_surface_energy__( self.new_SurfaceEnergy_instances) - #__| + # __| def fit_bulk_energy(self): """ """ - #| - fit_bulk_energy + # | - fit_bulk_energy if self.sufficient_data_to_fit_bulk: self.fitted_bulk_energy = self.__calc_regressed_bulk_energy__() - #__| + # __| def inst_surf_e_with_fitted_bulk(self): """ COMBAK: This isn't being used now """ - #| - tmp_meth + # | - tmp_meth # fitted_bulk_energy = self.fitted_bulk_energy # SurfaceEnergy_instances = self.SurfaceEnergy_instances # @@ -602,13 +602,13 @@ def inst_surf_e_with_fitted_bulk(self): # # pH=0., # ) - #__| + # __| def __init_dataframe__(self): """ """ - #| - __init_dataframe__ + # | - __init_dataframe__ SurfaceEnergy_instances = self.SurfaceEnergy_instances atoms_list = [i.atoms for i in SurfaceEnergy_instances] @@ -623,12 +623,12 @@ def __init_dataframe__(self): df = df.sort_values("number_of_atoms") return(df) - #__| + # __| def __sufficient_data_to_fit_bulk(self): """ """ - #| - __sufficient_data_to_fit_bulk + # | - __sufficient_data_to_fit_bulk df = self.df num_points_to_exclude = self.num_points_to_exclude @@ -636,7 +636,7 @@ def __sufficient_data_to_fit_bulk(self): # Need at least 3 points after removing points to do fit if len(df) - num_points_to_exclude < 3: - #| - Less than 3 data points, don't fit + # | - Less than 3 data points, don't fit if len(df) <= 2: self.num_points_to_exclude = 0 self.sufficient_data_to_fit_bulk = False @@ -644,9 +644,9 @@ def __sufficient_data_to_fit_bulk(self): if self.verbose: print("Only ", str(len(df)), " data points, in dataframe") print("Will not fit bulk energy") - #__| + # __| - #| - Modifiying num_points_to_exclude to have at least 3 points + # | - Modifiying num_points_to_exclude to have at least 3 points else: num_points_to_exclude = int(len(df) - 3) self.num_points_to_exclude = num_points_to_exclude @@ -656,14 +656,14 @@ def __sufficient_data_to_fit_bulk(self): "Changed num_points_to_exclude to ", num_points_to_exclude) # print("") - #__| + # __| - #__| + # __| def __calc_regressed_bulk_energy__(self): """ """ - #| - __calc_regressed_bulk_energy__ + # | - __calc_regressed_bulk_energy__ df = self.df num_points_to_exclude = self.num_points_to_exclude nbpe = num_points_to_exclude @@ -679,13 +679,13 @@ def __calc_regressed_bulk_energy__(self): bulk_energy = z[0] return(bulk_energy) - #__| + # __| def __recalc_SurfaceEnergy_w_new_bulk__(self, new_bulk_energy): """ """ - #| - __recalc_SurfaceEnergy_w_new_bulk__ + # | - __recalc_SurfaceEnergy_w_new_bulk__ SurfaceEnergy_instances = self.SurfaceEnergy_instances # new_bulk_energy = self.new_bulk_energy verbose = self.verbose @@ -711,13 +711,13 @@ def __recalc_SurfaceEnergy_w_new_bulk__(self, new_bulk_energy): new_SurfaceEnergy_instances.append(SE_new) return(new_SurfaceEnergy_instances) - #__| + # __| def __calc_ave_surface_energy__(self, SurfaceEnergy_instances): """ """ - #| - __calc_ave_surface_energy__ + # | - __calc_ave_surface_energy__ # new_SurfaceEnergy_instances = self.new_SurfaceEnergy_instances # SurfaceEnergy_instances = self.SurfaceEnergy_instances # if @@ -743,14 +743,14 @@ def __calc_ave_surface_energy__(self, SurfaceEnergy_instances): ave_surface_e = df.iloc[-nptoa:]["surface_energy_per_area"].mean() return(ave_surface_e) - #__| + # __| def plot_E_vs_N_convergence(self): """Plot raw slab energy vs number of atoms. The slope of this plot is the fitted bulk energy """ - #| - plot_E_vs_N_convergence + # | - plot_E_vs_N_convergence df = self.df number_of_atoms = df["number_of_atoms"] @@ -775,7 +775,7 @@ def plot_E_vs_N_convergence(self): ) return(trace_i) - #__| + # __| def plot_surface_energy(self, @@ -784,10 +784,10 @@ def plot_surface_energy(self, ): """ """ - #| - plot_surface_energy + # | - plot_surface_energy data = [] - #| - Surface Energy (DFT Bulk) ######################################## + # | - Surface Energy (DFT Bulk) ######################################## y_surface_e = []; x_slab_thickness = [] for SE_inst_i in self.SurfaceEnergy_instances: y_surface_e.append(SE_inst_i.std_surface_e_per_area) @@ -817,9 +817,9 @@ def plot_surface_energy(self, ) data.append(trace_i) - #__| + # __| - #| - Surface Energy (Fitted Bulk) ##################################### + # | - Surface Energy (Fitted Bulk) ##################################### y_surface_e = []; x_slab_thickness = [] for SE_inst_i in self.new_SurfaceEnergy_instances: y_surface_e.append(SE_inst_i.std_surface_e_per_area) @@ -849,9 +849,9 @@ def plot_surface_energy(self, ), ) data.append(trace_i) - #__| + # __| - #| - Average Surface Energy (DFT Bulk) ################################ + # | - Average Surface Energy (DFT Bulk) ################################ ave_surface_energy = self.ave_surface_energy_per_area trace_i = go.Scatter( x=[0, 30], @@ -877,9 +877,9 @@ def plot_surface_energy(self, ), ) data.append(trace_i) - #__| + # __| - #| - Average Surface Energy (Fitted Bulk) ############################# + # | - Average Surface Energy (Fitted Bulk) ############################# ave_surface_energy = self.new_ave_surface_energy_per_area trace_i = go.Scatter( x=[0, 30], @@ -904,17 +904,17 @@ def plot_surface_energy(self, ), ) data.append(trace_i) - #__| + # __| return(data) - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** -#| - METHODS +# | - METHODS def surface_energy_2( df_i, @@ -926,7 +926,7 @@ def surface_energy_2( Calculates the "average" surface energy, but with a bulk_e_per_atom that is fitted to a range of slab thicknesses. """ - #| - surface_energy_2 + # | - surface_energy_2 nbpe = num_points_to_exclude y_i = df_i["elec_energy"].tolist() @@ -956,7 +956,7 @@ def surface_energy_2( ) return(trace) - #__| + # __| def surf_e_4( @@ -977,9 +977,9 @@ def surf_e_4( Calculate surface energy assuming a water reference state and using the computational hydrogen electrode. """ - #| - surf_e_4 + # | - surf_e_4 - #| - Read info from row_i + # | - Read info from row_i # COMBAK This shouldn't be hard coded in metal = "Ir" @@ -997,9 +997,9 @@ def surf_e_4( elems_dict.get("O", 0) - nonstoich_Os nonstoich_Hs = elems_dict.get("H", 0) - #__| + # __| - #| - Calculate Standard State Surface Energy + # | - Calculate Standard State Surface Energy if "surf_e_0" in row_i: surf_e_0 = row_i.get("surf_e_0", default=0.) else: @@ -1018,9 +1018,9 @@ def surf_e_4( elif norm_mode == "atoms" and num_atoms is not None: norm_term = 2 * num_atoms surf_e_0 = surf_e_0 / norm_term - #__| + # __| - #| - Calculate V, pH Dependant Surface Energy + # | - Calculate V, pH Dependant Surface Energy slope = 2 * nonstoich_Os - nonstoich_Hs surf_e = 0. + \ @@ -1033,10 +1033,10 @@ def surf_e_4( # -(slope * bias) / (2 * row_i["slab_area"]) + \ # surf_e = surf_e / (2 * row_i["slab_area"]) - #__| + # __| - #| - Unit conversion + # | - Unit conversion units="eV/A^2", # 'eV/A^2' or 'J/m^2' if norm_mode == "area": @@ -1050,6 +1050,6 @@ def surf_e_4( # __| return(surf_e) - #__| + # __| -#__| +# __| diff --git a/surface_energy/surface_energy_pourbaix_plot.py b/surface_energy/surface_energy_pourbaix_plot.py index 15953c8..fe4ecb2 100644 --- a/surface_energy/surface_energy_pourbaix_plot.py +++ b/surface_energy/surface_energy_pourbaix_plot.py @@ -3,37 +3,37 @@ """ """ -#| - IMPORT MODULES -#__| +# | - IMPORT MODULES +# __| class SurfaceEnergyPourbaixPlot: """ """ - #| - TEMP ******************************************************** + # | - TEMP ******************************************************** _TEMP = "TEMP" def __init__(self, ): """ """ - #| - __init__ + # | - __init__ - #| - Setting Argument Instance Attributes - #__| + # | - Setting Argument Instance Attributes + # __| - #| - Initializing Internal Instance Attributes - #__| + # | - Initializing Internal Instance Attributes + # __| - #__| + # __| def method_0(): """ """ - #| - TEMP + # | - TEMP - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** diff --git a/vasp/vasp_methods.py b/vasp/vasp_methods.py index eb3609f..7f713f2 100644 --- a/vasp/vasp_methods.py +++ b/vasp/vasp_methods.py @@ -1,4 +1,4 @@ -#| - IMPORT MODULES +# | - IMPORT MODULES from raman_dft.vasp_raman_job_methods import get_modes_from_OUTCAR # from raman_dft.vasp_raman_job_methods import parse_poscar @@ -9,12 +9,12 @@ import numpy as np import os import itertools -#__| +# __| def num_of_atoms_OUTCAR_tmp(outcar_fh): """Parses OUTCAR for number of atoms in atoms object """ - #| - num_of_atoms_OUTCAR + # | - num_of_atoms_OUTCAR outcar_fh.seek(0) num_atoms = 0 while True: @@ -32,7 +32,7 @@ def num_of_atoms_OUTCAR_tmp(outcar_fh): # ion_entry = next_line.split()[0].isdigit() num_atoms += 1 return(num_atoms) - #__| + # __| def create_vib_modes_atoms( path_i=".", @@ -52,18 +52,18 @@ def create_vib_modes_atoms( step_size: number_images: """ - #| - create_vib_modes_atoms + # | - create_vib_modes_atoms - #| - SCRIPT INPUTS + # | - SCRIPT INPUTS # step_size = 1.2 # number_images = 31 vis_dir = "/mode_movies" # path_i = "/mnt/c/Users/raul_desktop/Dropbox/01_acad_folder # /01_grad_school/01_norskov/04_comp_clusters/00_scripts/ # 09_raman_dft/view_modes" - #__| + # __| - #| - Reading in OUTCAR + # | - Reading in OUTCAR file_name = "OUTCAR" with open(path_i + "/" + file_name, "r") as fle: atoms = io.read(path_i + "/" + file_name) @@ -71,12 +71,12 @@ def create_vib_modes_atoms( pos = atoms.positions eigvals, eigvecs, norms = get_modes_from_OUTCAR(fle, path_i=path_i) - #__| + # __| - #| - Creating Visualization Directory + # | - Creating Visualization Directory if not os.path.isdir(path_i + vis_dir): os.makedirs(path_i + vis_dir) - #__| + # __| iterator = enumerate(itertools.izip(eigvals, eigvecs, norms)) for index, (eigval_i, eigvec_i, norm_i) in iterator: @@ -143,14 +143,14 @@ def create_vib_modes_atoms( with open(".FINISHED", "w") as fle: fle.write("") - #__| + # __| def create_vdw_kernel_symlink(): """ If on the SLAC cluster, symlinks the vdw vasp kernel into the job directory, otherwise does nothing """ - #| - create_vdw_kernel_symlink + # | - create_vdw_kernel_symlink if os.getenv("COMPENV") == "slac": print("TEMP - 180313 !@#") if not (os.path.exists("vdw_kernel.bindat")): @@ -172,7 +172,7 @@ def create_vdw_kernel_symlink(): if os.getenv("AWS_BATCH_JOB_ID") is None: pass - #__| + # __| def parse_incar(incar_list): @@ -183,7 +183,7 @@ def parse_incar(incar_list): INCAR file in python list where each line represents a line from the file. """ - #| - parse_incar + # | - parse_incar incar_1 = [line for line in incar_list if " = " in line] incar_dict = {} @@ -194,7 +194,7 @@ def parse_incar(incar_list): assert len(line_i) == 2, mess incar_dict.update({line_i[0].strip(): line_i[1].strip()}) - #| - Incar keys list + # | - Incar keys list # incar_keys = [ # "ENCUT", # "AMIX_MAG", @@ -231,9 +231,9 @@ def parse_incar(incar_list): # "LDAUU", # "LDAUJ", # ] - #__| + # __| - #| - Incar Types Dict + # | - Incar Types Dict incar_types_dict = { "ENCUT": "float", @@ -283,9 +283,9 @@ def parse_incar(incar_list): "LDAUU": "list", "LDAUJ": "list", } - #__| + # __| - #| - Formatting Dict to Proper Data Types + # | - Formatting Dict to Proper Data Types formatted_incar_dict = {} for key, value in incar_dict.items(): @@ -322,9 +322,9 @@ def parse_incar(incar_list): value_new = value formatted_incar_dict.update({key: value_new}) - #__| + # __| return(formatted_incar_dict) # return(incar_dict) - #__| + # __| diff --git a/xrd_spectra/xrd.py b/xrd_spectra/xrd.py index bd9aa1b..f37b1e5 100644 --- a/xrd_spectra/xrd.py +++ b/xrd_spectra/xrd.py @@ -15,7 +15,7 @@ Author: Raul A. Flores """ -#| - IMPORT MODULES +# | - IMPORT MODULES import os import math @@ -37,14 +37,14 @@ from plotly import io as pyio from scipy.signal import find_peaks -#__| +# __| class XRD_Spectra: """TEMP. """ - #| - XRD_Spectra ****************************************************** + # | - XRD_Spectra ****************************************************** _SAVE_FOLDER_NAME = ".xrd_save_dir" _SAVE_FILE_NAME = "xrd_save_state.pickle" _SAVE_ATTRIBUTES = ["df_peaks", "temp_0"] @@ -71,20 +71,20 @@ def __init__(self, peak_broadening: float Dictates the broadness of peaks (smaller is thinner) """ - #| - __init__ + # | - __init__ self.temp_0 = "TEMP TEMP TEMP TEMP" - #| - Setting Argument Instance Attributes + # | - Setting Argument Instance Attributes self.reflections_table_path = reflections_table_path self.theta_range = theta_range self.theta_spacing = theta_spacing self.load_from_saved_state = load_from_saved_state self.peak_broadening = peak_broadening - #__| + # __| - #| - Initializing Internal Instance Attributes + # | - Initializing Internal Instance Attributes self.df_peaks = None - #__| + # __| # Try to load previous saved state self.loaded_state_data = self.__load_state__() @@ -100,13 +100,13 @@ def __init__(self, self.summed_lorentz = self.__create_summed_lorentz_function__() self.spectrum = self.__create_total_spectrum__() - #__| + # __| def __read_reflections_table__(self): """ """ - #| - __read_reflections_table__ + # | - __read_reflections_table__ file_path_i = self.reflections_table_path # file_path_i = "/home/raulf2012/Dropbox/01_norskov/00_projects/columbite_iro2_oer/workflow/01_latt_const_opt/an_xrd_pattern/vesta_xrd_gen/optimized_bulk.txt" @@ -144,27 +144,27 @@ def __read_reflections_table__(self): # df.columns = column_headers # df = pd.read_csv("../vesta_xrd_gen/reflections_table.csv") # df = df.sort_values("I", ascending=False) - #__| + # __| def __process_reflections_table__(self): """ """ - #| - __process_reflections_table__ + # | - __process_reflections_table__ self.__create_simplified_facet_string__() - #__| + # __| def __create_simplified_facet_string__(self): """ """ - #| - __create_simplified_facet_string__ + # | - __create_simplified_facet_string__ df = self.df # Create string facet representation df["facet_mine"] = abs(df["h"]).astype("str") + abs(df["k"]).astype("str") + abs(df["l"]).astype("str") - #__| + # __| def __create_lorentz_for_all_signals__(self): @@ -172,7 +172,7 @@ def __create_lorentz_for_all_signals__(self): Adds column to df """ - #| - __create_lorentz_for_all_signals__ + # | - __create_lorentz_for_all_signals__ df = self.df peak_broadening = self.peak_broadening @@ -195,25 +195,25 @@ def __create_lorentz_for_all_signals__(self): funct_list.append(funct_i) df["function"] = funct_list - #__| + # __| def __create_summed_lorentz_function__(self): """ """ - #| - __create_summed_lorentz_function__ + # | - __create_summed_lorentz_function__ df = self.df sum_funct = np.sum(df["function"].tolist()) return(sum_funct) - #__| + # __| def __create_theta_array__(self): """ """ - #| - __create_theta_array__ + # | - __create_theta_array__ # Compute Signal theta_range = self.theta_range theta_spacing = self.theta_spacing @@ -227,13 +227,13 @@ def __create_theta_array__(self): x_range = np.arange(min_theta, max_theta, theta_spacing) return(x_range) - #__| + # __| def __create_total_spectrum__(self): """ """ - #| - __create_total_spectrum__ + # | - __create_total_spectrum__ summed_lorentz = self.summed_lorentz # theta_range = self.theta_range theta_array = self.theta_array @@ -246,15 +246,15 @@ def __create_total_spectrum__(self): spectrum = 100 * (spectrum / spectrum.max()) return(spectrum) - #__| + # __| def compute_peak_positions(self): """ """ - #| - compute_peak_positions + # | - compute_peak_positions - #| - Class Attributes + # | - Class Attributes spectrum = self.spectrum df = self.df theta_array = self.theta_array @@ -262,7 +262,7 @@ def compute_peak_positions(self): theta_range = self.theta_range loaded_state_data = self.loaded_state_data - #__| + # __| bool_0 = (loaded_state_data is not None) @@ -282,7 +282,7 @@ def compute_peak_positions(self): x = Symbol("x") - #| - Computing Peak Positions + # | - Computing Peak Positions # Used for peak_finder to set width grid_to_theta = len(theta_array) / (max_theta - min_theta) @@ -294,10 +294,10 @@ def compute_peak_positions(self): distance=grid_to_theta * 1.) peaks_x = [theta_array[i] for i in peaks[0]] - #__| + # __| - #| - Computing the Main Facets for the Prominant Peaks + # | - Computing the Main Facets for the Prominant Peaks main_facets_list = [] peaks_x_tqdm = tqdm(peaks_x) for peak_x_i in peaks_x_tqdm: @@ -328,7 +328,7 @@ def compute_peak_positions(self): main_facets_i = "_".join(major_facets) main_facets_list.append(main_facets_i) - #__| + # __| df_peaks = pd.DataFrame() df_peaks["main_facets"] = main_facets_list @@ -346,23 +346,23 @@ def compute_peak_positions(self): # # df[df[peak_x_i].isin(highest_contributions)].sort_values( # peak_x_i, ascending=False) - #__| + # __| def __create_save_dir__(self): """ """ - #| - __create_save_dir__ + # | - __create_save_dir__ save_folder_name = self._SAVE_FOLDER_NAME if not os.path.exists(save_folder_name): os.makedirs(save_folder_name) - #__| + # __| def __save_state__(self): """ """ - #| - __save_states__ + # | - __save_states__ save_folder_name = self._SAVE_FOLDER_NAME save_file_name = self._SAVE_FILE_NAME @@ -376,13 +376,13 @@ def __save_state__(self): with open(os.path.join(save_folder_name, save_file_name), "wb") as fle: pickle.dump(save_attributes, fle) - #__| + # __| def __load_state__(self): """ """ - #| - __load_state__ + # | - __load_state__ save_folder_name = self._SAVE_FOLDER_NAME save_file_name = self._SAVE_FILE_NAME load_from_saved_state = self.load_from_saved_state @@ -399,16 +399,16 @@ def __load_state__(self): pass return(save_data) - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** class XRD_Plot(): """TEMP. """ - #| - XRD_Plot ************************************************************* + # | - XRD_Plot ************************************************************* def __init__(self, XRD_Spectra, @@ -416,16 +416,16 @@ def __init__(self, ): """ """ - #| - __init__ + # | - __init__ - #| - Setting Instance Attributes + # | - Setting Instance Attributes self.XRD_Spectra = XRD_Spectra - #__| + # __| def create_figure(self): """ """ - #| - create_figure + # | - create_figure data = [] trace_i = self.__trace_peak_labels__() @@ -435,12 +435,12 @@ def create_figure(self): data.append(trace_i) return(data) - #__| + # __| def __trace_peak_labels__(self): """ """ - #| - __trace_peak_labels__ + # | - __trace_peak_labels__ df_peaks = self.XRD_Spectra.df_peaks trace_i = go.Scatter( @@ -465,13 +465,13 @@ def __trace_peak_labels__(self): return(trace_i) # data.append(trace_1) - #__| + # __| def __trace_spectra__(self): """ """ - #| - __trace_spectra__ + # | - __trace_spectra__ theta_array = self.XRD_Spectra.theta_array spectrum = self.XRD_Spectra.spectrum @@ -483,12 +483,12 @@ def __trace_spectra__(self): ) return(trace_i) - #__| + # __| def get_layout(self): """ """ - #| - get_layout + # | - get_layout layout = go.Layout() layout.width = 18.4 * 37.795275591 @@ -563,20 +563,20 @@ def get_layout(self): ) return(layout) - #__| + # __| - #__| + # __| - #__| ********************************************************************** + # __| ********************************************************************** -#| - METHODS +# | - METHODS def get_weighted_xrange(x_bounds, gamma, x0, min_step_size=1): """ """ - #| - get_weighted_xrange + # | - get_weighted_xrange x_i = x_bounds[0] x_range= [x_i] while x_i < x_bounds[-1]: @@ -592,12 +592,12 @@ def get_weighted_xrange(x_bounds, gamma, x0, min_step_size=1): x_range.append(x_i) return(x_range) - #__| + # __| def Lorentz_i(x0, x, gamma, peak_height): """ """ - #| - Lorentz_i + # | - Lorentz_i pi = math.pi term_0 = (1 / (pi * gamma)) term_1 = (gamma ** 2) * ((x - x0) ** 2 + gamma ** 2) ** (-1) @@ -605,12 +605,12 @@ def Lorentz_i(x0, x, gamma, peak_height): y_i = peak_height * term_1 return(y_i) - #__| + # __| def Lorentz_distr_i(x0, x_bounds, gamma, intensity): """ """ - #| - Lorentz_distr_i + # | - Lorentz_distr_i x_range = get_weighted_xrange(x_bounds, gamma, x0, min_step_size=2) @@ -620,6 +620,6 @@ def Lorentz_distr_i(x0, x_bounds, gamma, intensity): lorentz_distr.append(y_i) return(x_range, lorentz_distr) - #__| + # __| -#__| +# __| From 9511c95222c8a705150533d975083034d804531e Mon Sep 17 00:00:00 2001 From: Raul Flores Date: Wed, 8 Jan 2020 17:08:14 -0800 Subject: [PATCH 05/10] Update `add_duplicate_axes` to add y and x axis ref to scatter instance There was a bug where the xaxis would bleed into other subplots --- plotting/my_plotly.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/plotting/my_plotly.py b/plotting/my_plotly.py index 78f1d75..3853111 100644 --- a/plotting/my_plotly.py +++ b/plotting/my_plotly.py @@ -81,6 +81,12 @@ def add_duplicate_axes( """ # | - add_duplicate_axes + if axis_type == "x": + axis_type_other = "y" + elif axis_type == "y": + axis_type_other = "x" + + # This is necessary to make sure that the original traces are still visible after adding the new traces fig.update_layout( # paper_bgcolor="white", @@ -92,33 +98,23 @@ def add_duplicate_axes( # ######################################################################### if axis_num_list is None: - # axis_list = axis_info_dict["axis_list"] axis_num_list = axis_info_dict["axis_num_list"] - # axis_num_list_new = [i + len(axis_num_list) for i in axis_num_list] axis_num_list_new = [i + num_of_axis + 1 for i, j in enumerate(axis_num_list)] - - # print("num_of_axis:", num_of_axis) - # print("axis_num_list_new:", axis_num_list_new) - - # [(i, j) for i, j in enumerate(mylist)] - iterator = enumerate(zip(axis_num_list, axis_num_list_new)) for i_cnt, (old_index, new_index) in iterator: old_Axis = fig.layout[axis_type + "axis" + str(old_index)] new_axis = copy.deepcopy(old_Axis) new_axis = new_axis.update( - # dtick=0.1, showticklabels=False, title=dict( font=None, standoff=None, text="", - ), - ) + )) new_axis = new_axis.update(**axis_data) @@ -131,7 +127,10 @@ def add_duplicate_axes( fig.add_scatter( **go.Scatter({ - axis_type + "axis": axis_type + str(new_index) + axis_type + "axis": axis_type + str(new_index), + + # I added this to fix some issues + axis_type_other + "axis": axis_type_other + str(new_index), }).to_plotly_json()) # __| From 93ba53dd7a9a3a9eb7af8009d2d10990f55060c2 Mon Sep 17 00:00:00 2001 From: Raul Flores Date: Wed, 8 Jan 2020 17:08:22 -0800 Subject: [PATCH 06/10] Add all --- .../oxr_plotting_classes/oxr_plot_scaling.py | 97 ++++++--- oxr_reaction/oxr_rxn.py | 3 +- oxr_reaction/oxr_series.py | 4 +- plotting/my_plotly.py | 193 ++++++------------ 4 files changed, 135 insertions(+), 162 deletions(-) diff --git a/oxr_reaction/oxr_plotting_classes/oxr_plot_scaling.py b/oxr_reaction/oxr_plotting_classes/oxr_plot_scaling.py index 23f88c0..47cfd94 100644 --- a/oxr_reaction/oxr_plotting_classes/oxr_plot_scaling.py +++ b/oxr_reaction/oxr_plotting_classes/oxr_plot_scaling.py @@ -13,8 +13,8 @@ import plotly.graph_objs as go -from oxr_reaction.oxr_series import ORR_Free_E_Series -from oxr_reaction.adsorbate_scaling import lim_U_i +# from oxr_reaction.oxr_series import ORR_Free_E_Series +# from oxr_reaction.adsorbate_scaling import lim_U_i # __| @@ -47,6 +47,7 @@ def __init__(self, marker_color_key="color2", marker_border_color_key="color1", marker_shape_key="symbol", + num_round=4, ): """ Input variables to class instance. @@ -55,6 +56,9 @@ def __init__(self, ORR_Free_E_Plot: mode: "all", "ooh_vs_oh", "o_vs_oh" + num_round: + Amount by which to round values when writing to string (when making plots for example) + """ # | - __init__ self.ORR_Free_E_Plot = ORR_Free_E_Plot @@ -64,7 +68,7 @@ def __init__(self, self.marker_color_key = marker_color_key self.marker_border_color_key = marker_border_color_key self.marker_shape_key = marker_shape_key - + self.num_round = num_round # ################################################################# self.data_points = { @@ -260,8 +264,7 @@ def __create_trace_i__(self, size=smart_format_i.get("marker_size", 9), symbol=smart_format_i.get( self.marker_shape_key, "circle"), - color=smart_format_i.get( - self.marker_color_key, "pink"), + color=smart_format_i.get(self.marker_color_key, "pink"), line=dict( # color=smart_format_i[marker_border_color_key], color=smart_format_i.get( @@ -330,6 +333,8 @@ def fit_scaling_lines(self, """ # | - fit_scaling_lines + num_round = self.num_round + # | - LOOP oh_list = [] dependent_e_list = [] @@ -376,28 +381,30 @@ def fit_scaling_lines(self, # | - Equation Annotations if dependent_species == "ooh": eqn_str_i = ("" + - "GOOH=" + - str(round(slope_i, 4)) + - " GOH+" + - str(round(intercept_i, 4)) + + "ΔGOOH=" + + str(round(slope_i, num_round)) + + " ΔGOH+" + + str(round(intercept_i, num_round)) + "" ) + # num_round + elif dependent_species == "o": eqn_str_i = ("" + - "GO = " + - str(round(slope_i, 4)) + - " GOH+" + - str(round(intercept_i, 4)) + + "ΔGO = " + + str(round(slope_i, num_round)) + + " ΔGOH+" + + str(round(intercept_i, num_round)) + "" ) elif dependent_species == "oh": eqn_str_i = ("" + - "GOH = " + - str(round(slope_i, 4)) + - " GOH+" + - str(round(intercept_i, 4)) + + "ΔGOH = " + + str(round(slope_i, num_round)) + + " ΔGOH+" + + str(round(intercept_i, num_round)) + "" ) @@ -405,16 +412,32 @@ def fit_scaling_lines(self, eqn_str_i = "TEMP TEMP TEMP TEMP | 190213 | RF" raise ValueError('A very specific bad thing happened.') - annotation_i = dict( + + + + + + + + + + + + + + + # annotation_i = dict( + annotation_i = go.layout.Annotation( x=0., y=1., xref="paper", yref="paper", text=eqn_str_i, font=dict( - color="red", - family="Droid Sans Mono,Overpass", - size=9. * (4. / 3.), + color="black", + # family="Droid Sans Mono,Overpass", + family="Arial,Droid Sans Mono,Overpass", + size=8. * (4. / 3.), ), showarrow=False, xanchor="left", @@ -423,6 +446,28 @@ def fit_scaling_lines(self, ) self.annotations_list.append(annotation_i) + + + + + + + + + + + + + + + + + + + + + + # __| @@ -530,10 +575,10 @@ def scaling_meth(E_OH): # NOTE | This shouldn't be an internal method # I don't remember why I wrote the above note def get_plotly_layout(self, - # x_ax_spec="oh", title="Scaling Relations", showlegend=True, layout_dict=None, + # x_ax_spec="oh", ): """Create plotly layout dict. @@ -689,6 +734,9 @@ def get_plotly_layout(self, # __| +# __| ********************************************************************** + + @@ -727,11 +775,6 @@ def get_plotly_layout(self, # # __| -# __| ********************************************************************** - - - - # | - __old__ # x_range_ooh_vs_oh=[0., 3.5], # y_range_ooh_vs_oh=[0., 5.], diff --git a/oxr_reaction/oxr_rxn.py b/oxr_reaction/oxr_rxn.py index 4126364..7f4c4d3 100644 --- a/oxr_reaction/oxr_rxn.py +++ b/oxr_reaction/oxr_rxn.py @@ -180,7 +180,8 @@ def __create_smart_format_dict__(self, property_dict, smart_format_dict): return(format_dict) # __| - def add_series(self, + def add_series( + self, fe_df, plot_mode="all", name_i=None, diff --git a/oxr_reaction/oxr_series.py b/oxr_reaction/oxr_series.py index b0fdb31..31bece2 100644 --- a/oxr_reaction/oxr_series.py +++ b/oxr_reaction/oxr_series.py @@ -15,6 +15,7 @@ pd.options.mode.chained_assignment = None # __| + class ORR_Free_E_Series(): """ORR free energy diagram series class. @@ -30,7 +31,8 @@ class ORR_Free_E_Series(): """ # | - ORR_Free_E_Series **************************************************** - def __init__(self, + def __init__( + self, free_energy_df=None, state_title="adsorbate", free_e_title="ads_e", diff --git a/plotting/my_plotly.py b/plotting/my_plotly.py index 3853111..769db23 100644 --- a/plotting/my_plotly.py +++ b/plotting/my_plotly.py @@ -19,6 +19,7 @@ from plotly import io as pyio # __| + def get_xy_axis_info(fig): """ """ @@ -70,7 +71,6 @@ def get_xy_axis_info(fig): # __| - def add_duplicate_axes( fig, axis_type="x", # 'x' or 'y' @@ -136,62 +136,6 @@ def add_duplicate_axes( # __| -# | - OLD | add_duplicate_axes -# def add_duplicate_axes( -# fig, -# axis_type="x", # 'x' or 'y' -# axis_data=dict(), -# ): -# """ -# """ -# # | - add_duplicate_axes -# -# # This is necessary to make sure that the original traces are still visible after adding the new traces -# fig.update_layout( -# # paper_bgcolor="white", -# plot_bgcolor="rgba(255,255,255,0.)", -# ) -# -# # ######################################################################### -# axis_info_dict = get_xy_axis_info(fig)[axis_type] -# -# num_of_axis = axis_info_dict["num_of_axis"] -# axis_list = axis_info_dict["axis_list"] -# axis_num_list = axis_info_dict["axis_num_list"] -# -# axis_num_list_new = [i + num_of_axis for i in axis_num_list] -# iterator = enumerate(zip(axis_num_list, axis_num_list_new)) -# for i_cnt, (old_index, new_index) in iterator: -# old_Axis = fig.layout[axis_type + "axis" + str(old_index)] -# -# new_axis = copy.deepcopy(old_Axis) -# new_axis = new_axis.update( -# # dtick=0.1, -# showticklabels=False, -# title=dict( -# font=None, -# standoff=None, -# text="", -# ), -# ) -# -# new_axis = new_axis.update(**axis_data) -# -# axis_key = axis_type + "axis" + str(new_index) -# new_layout = go.Layout({ -# axis_key: new_axis, -# }) -# -# fig.update_layout(new_layout) -# -# fig.add_scatter( -# **go.Scatter({ -# axis_type + "axis": axis_type + str(new_index) -# }).to_plotly_json()) -# # __| -# __| - - def add_minor_ticks( fig, axis="x", # 'x', 'y', or 'both' @@ -394,8 +338,6 @@ def my_plotly_plot( # __| - - def reapply_colors(data): """Redefines the line colors of a plotly data series. @@ -437,88 +379,73 @@ def reapply_colors(data): return(dat_lst_master) # __| + def plot_layout( # xax_labels = ): """ - """ - # | - plot_layout - # | - Plot Settings - plot_title_size = 18 - tick_lab_size = 16 - axes_lab_size = 18 - legend_size = 18 - # __| - # | - Plot Layout - xax_labels = ["O2", "OOH", "O", "OH", "H2O"] - layout = { - - "title": "FED of ORR Mechanism For Iron-Supported-Graphene", - - "font": { - "family": "Courier New, monospace", - "size": plot_title_size, - "color": "black", - }, - - # | - Axes -------------------------------------------------------------- - "yaxis": { - "title": "Free Energy [eV]", - "zeroline": True, - "titlefont": dict(size=axes_lab_size), - "showgrid": False, - "tickfont": dict( - size=tick_lab_size, - ), - }, - - "xaxis": { - "title": "Reaction Coordinate", - "zeroline": True, - "titlefont": dict(size=axes_lab_size), - "showgrid": False, - - # "showticklabels": False, - - "ticktext": xax_labels, - "tickvals": [1.5 * i + 0.5 for i in range(len(xax_labels))], - - "tickfont": dict( - size=tick_lab_size, - ), - }, - # __| ------------------------------------------------------------------- - - # | - Legend ------------------------------------------------------------ - "legend": { - "traceorder": "normal", - "font": dict(size=legend_size) - }, - # __| ------------------------------------------------------------------- - - # | - Plot Size - "width": 200 * 4., - "height": 200 * 3., - # __| - - } - # __| - fig = Figure(data=dat_lst, layout=layout) - # plotly.plotly.image.save_as(fig, filename="pl_hab_opda_raman.png") - plotly.offline.plot( - { - "data": dat_lst, - "layout": layout, - }, - filename="plots/pl_fed_supp_graph_02.html" - ) - # tmp = plotly.plotly.image.plot(data, filename="pl_fed_180314.png") - return(layout) - # __| + + + +# | - OLD | add_duplicate_axes +# def add_duplicate_axes( +# fig, +# axis_type="x", # 'x' or 'y' +# axis_data=dict(), +# ): +# """ +# """ +# # | - add_duplicate_axes +# +# # This is necessary to make sure that the original traces are still visible after adding the new traces +# fig.update_layout( +# # paper_bgcolor="white", +# plot_bgcolor="rgba(255,255,255,0.)", +# ) +# +# # ######################################################################### +# axis_info_dict = get_xy_axis_info(fig)[axis_type] +# +# num_of_axis = axis_info_dict["num_of_axis"] +# axis_list = axis_info_dict["axis_list"] +# axis_num_list = axis_info_dict["axis_num_list"] +# +# axis_num_list_new = [i + num_of_axis for i in axis_num_list] +# iterator = enumerate(zip(axis_num_list, axis_num_list_new)) +# for i_cnt, (old_index, new_index) in iterator: +# old_Axis = fig.layout[axis_type + "axis" + str(old_index)] +# +# new_axis = copy.deepcopy(old_Axis) +# new_axis = new_axis.update( +# # dtick=0.1, +# showticklabels=False, +# title=dict( +# font=None, +# standoff=None, +# text="", +# ), +# ) +# +# new_axis = new_axis.update(**axis_data) +# +# axis_key = axis_type + "axis" + str(new_index) +# new_layout = go.Layout({ +# axis_key: new_axis, +# }) +# +# fig.update_layout(new_layout) +# +# fig.add_scatter( +# **go.Scatter({ +# axis_type + "axis": axis_type + str(new_index) +# }).to_plotly_json()) +# # __| +# __| + From c5fdcd02e2fcd7a555a33e5f7e56273bf7558718 Mon Sep 17 00:00:00 2001 From: raulf2012 Date: Sat, 1 Feb 2020 09:32:27 -0800 Subject: [PATCH 07/10] update --- dft_job_automat/.compute_env.py.swp | Bin 0 -> 20480 bytes dft_post_analysis/bands.py | 5 ++++- plotting/__init__.py | 1 + plotting/my_plotly.py | 21 +++++++++++++++------ 4 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 dft_job_automat/.compute_env.py.swp create mode 100644 plotting/__init__.py diff --git a/dft_job_automat/.compute_env.py.swp b/dft_job_automat/.compute_env.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..84a189320e3bd354faea106c4d824e5801fc610f GIT binary patch literal 20480 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 Date: Wed, 18 Mar 2020 18:24:37 -0700 Subject: [PATCH 08/10] Quick update/commit --- compute_envs/nersc.py | 4 + dft_post_analysis/bands.py | 3 + dft_post_analysis/rapiDOS/rapiDOS.py | 17 ++- .../rapiDOS/rapiDOS_analysis.ipynb | 105 +++++------------- .../oxr_plotting_classes/oxr_plot_volcano.py | 5 + plotting/my_plotly.py | 4 + 6 files changed, 58 insertions(+), 80 deletions(-) diff --git a/compute_envs/nersc.py b/compute_envs/nersc.py index 16c7864..38c7dbd 100644 --- a/compute_envs/nersc.py +++ b/compute_envs/nersc.py @@ -100,6 +100,10 @@ def submit_job_clust(self, **kwargs): self.arch_inst.__make_run_vasp_script__(params) + print("params:", params) + if "tasks-per-node" in list(params.keys()): + print("tasks-per-node is in params varialbe before merging with arch_inst") + params = merge_two_dicts(params, self.arch_inst.sbatch_params) # | - Write submission script diff --git a/dft_post_analysis/bands.py b/dft_post_analysis/bands.py index 1176560..4f96893 100644 --- a/dft_post_analysis/bands.py +++ b/dft_post_analysis/bands.py @@ -126,6 +126,9 @@ def plot_bands( s, k, x, X, e = bands_data + print("s:") + print(s) + # symbols = [t.replace('Gamma', '$\Gamma$') for t in s] symbols = [t.replace("Gamma", "G") for t in s] diff --git a/dft_post_analysis/rapiDOS/rapiDOS.py b/dft_post_analysis/rapiDOS/rapiDOS.py index 4adb355..caa99ba 100644 --- a/dft_post_analysis/rapiDOS/rapiDOS.py +++ b/dft_post_analysis/rapiDOS/rapiDOS.py @@ -11,6 +11,8 @@ import re import pandas as pd import os + +import shutil # __| @@ -178,7 +180,6 @@ def get_bandgap(total_dos): # __| -# if __name__ == '__main__': def rapiDOS( data_folder=None, @@ -214,6 +215,11 @@ def rapiDOS( ########################################################################### # Check spin: ########################################################################### + # Copy INCAR file into out_data folder, needed for further analysis + shutil.copyfile( + os.path.join(data_folder, "INCAR"), + os.path.join(out_folder, "INCAR"), + ) incar_file = open(os.path.join(data_folder, "INCAR"), "r") ispin = 1 # Non spin polarised calculations. @@ -374,3 +380,12 @@ def rapiDOS( #os.system('jupyter nbconvert --to notebook --execute rapiDOS_analysis.ipynb') # __| + + +if __name__ == '__main__': + print("KJKDFS") + rapiDOS( + data_folder=None, + out_folder="rapiDOS_out", + ) + diff --git a/dft_post_analysis/rapiDOS/rapiDOS_analysis.ipynb b/dft_post_analysis/rapiDOS/rapiDOS_analysis.ipynb index 9ab40d2..c60c3c3 100644 --- a/dft_post_analysis/rapiDOS/rapiDOS_analysis.ipynb +++ b/dft_post_analysis/rapiDOS/rapiDOS_analysis.ipynb @@ -47,15 +47,14 @@ }, { "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ - "total_dos_df = pd.read_csv('TotalDOS.csv')\n", - "pdos_df = pd.read_csv('PDOS.csv')\n", - "band_gap_df = pd.read_csv('BandGap.csv')" + "out_folder = \"rapiDOS_out\"\n", + "total_dos_df = pd.read_csv(out_folder + \"/\" + 'TotalDOS.csv')\n", + "pdos_df = pd.read_csv(out_folder + \"/\" + 'PDOS.csv')\n", + "band_gap_df = pd.read_csv(out_folder + \"/\" + 'BandGap.csv')" ] }, { @@ -94,20 +93,9 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAm0AAAF3CAYAAAD3rnzeAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMS4yLCBo\ndHRwOi8vbWF0cGxvdGxpYi5vcmcvNQv5yAAAIABJREFUeJzs3Xd4VFX+x/H3uXdaGi2E0JvSW+hC\nAJdlRcCuoKioC4LKWlBcUVfErthZUGy4Cvb6UxfRtawKiI0mVVB6EkhCSM8k087vjwSWnjYzd5J8\nX8/DI8y9c84nQPA7556itNYIIYQQQojIZlgdQAghhBBClE+KNiGEEEKIGkCKNiGEEEKIGkCKNiGE\nEEKIGkCKNiGEEEKIGkCKNiGEEEKIGkCKNiGEEEKIGkCKNiGEEEKIGkCKNiGEEEKIGkCKNiGEEEKI\nGsBmdYBQaNy4sW7btq3VMYQQolbwFhcTCATC3m8g4Gd/1gEaxzfCMMyw91/bGYaB3eWyOoYAVq1a\ntV9rnVDefbWyaGvbti0rV660OoYQQtQKO9asJLp+g7D3uy89naTBQ/jik49ompgY9v5ru6LcHNr1\n7md1DAEopXZV5D55PCqEECIiNY6P54evv6JxfLzVUYSICLVypE0IIUTNZ7PZaNe2jdUxhIgYMtIm\nhBAiIqVnZNChV2/SMzKsjiJERJCRNiGEEBFJa01+QQFaa6ujHJc/ECCvxIPPgkUawaANG5s3b7Y6\nRp3icrlo2bIldru9Su+Xok0IIYSogrwSDw0axdOoYQOUUlbHqbSA348zOsbqGHWG1pqsrCxSUlJo\n165dldqQx6NCCCFEFfgCgRpbsInwU0oRHx9PcXFxlduQok0IIUREio2J4dabbiQ2JnJHg6RgE5VR\n3b8vUrQJIYSISLGxsdw27SZiY2OtjhJxsrKy6DdgIP0GDKRVm7a0bd/+0K89Hs8x9x84cIAXX3qp\n3HZ9Ph8NGhy7J5/P58M0TZKSkujatStJSUnMmTPniE2Xly5dSv/+/encuTOdO3fm5ZdfPnRt8+bN\nnH766SQlJdGlSxemTp16TB9+v5/rr7+e7t2706NHDwYMGMCuXSffvuyuu+7im2++KffrOmjBggXc\nfPPNR7w2ZMgQ1q5dW+E2rCRz2oQQQkSk3Lw8Zj/1NHdMv4X69epZHSeixMfHs/LnnwC4/4EHiY2N\nYfott5zw/gPZ2bz40gKumTKlyn3GxcUdKm7S09MZP348+fn53H333aSlpTFhwgQ++eQTkpKSyMzM\nZOTIkbRo0YJRo0Zxww03MGPGDM466yy01mzYsOGY9t98802ysrJYt24dhmGwe/du6pXz5/7QQw9V\n+eupiWSkTQghRERyu9288trruN1uq6PUKE88+SRJffqS1Kcvz86fD8BdM2eydetW+g0YyD9mziQv\nL48zR4+hT58+9OzZk8WLF1eqj8TERF544QXmzZsHwLx585g8eTJJSUkAJCQkMHv2bB599FEA9u7d\nS8uWLYHSR4Q9evQ4ps29e/fSrFkzDKO0NGndujUNGjQ4NPp300030a1bN8444wyysrIAmDBhAh99\n9BEALVu25N5776V379707NmTrVu3VuprOlk/kUJG2oQQQohqynzscUq2bAlqm85OnUiYcVul3vPz\nzz/z1tvv8MP3y/H5fAweMpRhw4bx0IMPsm3b9kOjc16vl/feeZuEps3IyMggOTmZs88+u1J9dezY\nEbfbTVZWFhs3buTaa6894nq/fv3YuHEjANOnT2fYsGEkJyczcuRIJk6cSP369Y+4f/z48QwdOpRv\nv/2WESNGMGHChENFYG5uLsnJycydO5dZs2bxwAMPMGfOnGMyJSYmsmbNGubOnctTTz3F888/X6mv\nqaL9WEVG2oQ4Ca01xVu24C8otDqKEEKU6/sVK7jg/POJiooiLi6Oc885h++///6Y+7TWzJw1i549\nezJy5Ej27NnD/v37K91fRffQmzx5Mps2bWLs2LF8/fXXDBo06Ji5d61bt2bLli2HHnkOHz6cb7/9\nFig9HWPcuHFA6eja8uXLj9vPhRdeCEDfvn3ZuXPnMddPtBDg4OsV7ccqMtImxAn4Dhwgdfp0in78\nCSM2lqb33EP9cyr3SVQIUXWmadK2dWtM07Q6SrkqOyJmtddff4Pc3DxWr16NzWajZcuWld6KYuvW\nrURHRxMfH0/Xrl1ZtWoVZ5111qHrq1atolu3bod+3aJFCyZNmsSkSZPo3LkzmzdvplevXke06XK5\nGDNmDGPGjKFx48Z8/PHHDBky5Ji+T1R8OZ1OoPTvjs/nO+Z6fHw82dnZR7x24MABGjdufNz2Im11\nsIy0CXEC6Q89TNGq1biumYJq3Zq9d99NybZtVscSos5IaNyYH7/5moQT/A9VHGtIcjIff/IJbreb\n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\nQmCC\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "fig = plt.figure(figsize=(10.0,6.0)) # Create figure.\n", "plt.axvline(x=[0.0], color='k', linestyle='--',linewidth=1.2) # Plot vertical line in Fermi.\n", @@ -128,7 +116,7 @@ "plt.ylabel('DOS (states/eV)') # x axis label.\n", "plt.xlim([-8.0,4.0]) # Plot limits.\n", "\n", - "fig.savefig(\"Fig1.pdf\") # Save figure EPS." + "fig.savefig(out_folder + \"/\" + \"Fig1.pdf\") # Save figure EPS." ] }, { @@ -167,20 +155,9 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\nwEjGAABGAWycCMDMYZxTAAAAAElFTkSuQmCC\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "fig = plt.figure(figsize=(10.0,6.0)) # Create figure.\n", "pdos_energy_index_df = pdos_df.set_index(['Energy (E-Ef)']) # Set index.\n", @@ -203,7 +180,7 @@ "plt.ylabel('DOS (states/eV)') # x axis label.\n", "plt.xlim([-8.0,4.0]) # Plot limits.\n", "\n", - "fig.savefig(\"Fig2.pdf\") # Save figure EPS." + "fig.savefig(out_folder + \"/\" + \"Fig2.pdf\") # Save figure EPS." ] }, { @@ -215,20 +192,9 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\nWbHim5/jlZWVXH/99WRmZnLxxReza9cuAEaMGEF9fT0+n4/w8HDeeOMNAG677TY+++wzFi1axC23\n3MKkSZNISUnh8ccf7+7bdBqpMyYCWm1dHl5vQ6fWi31bXNwUDhyYR0PDAazWzo2o9aZvqu/7PxmD\nlqnKior/oKqqbIskRB9W+txzuPb6dxTblDac+CeeOOvzNm7cyNKlS9m8eTMej4ecnBwuvfRS5s+f\nT35+Pnl5eQA0NzezfPlyQkNDKS8vZ/z48UyZ0v6+wHv27OHVV19l3Lhx3HHHHfz1r3/lwQcfPK2d\nw+Hgvvvu4/PPP+f888/nlltuaT325JNPcuGFF/LBBx/wn//8hzvvvJMtW7Zw8cUX8+WXXxIXF0dK\nSgrr1q3jtttuY+PGjbz66qscOnSI7du3s3XrVoxGI6mpqcyaNYvExMSzfo/OREbGRECz2z9HUfRE\nRo4/q/NiY64BFMrL/9UzgXWT23Wi4Ksfq+9/m9U6nObmatzu8h7pXwgReNavX8/NN9+MxWLBZrMx\nbdo01q1bd1o7VVWZM2cOmZmZXH311RQUFFBZWdlu34MHD2bcuHEAzJgxg/Xr17fZbs+ePaSmpjJk\nyBAUReF//ud/Tonv9ttvB+Dqq6+muLiYxsZGcnNzWbt2LWvXruUnP/kJeXl5HD16lLi4uNaixFde\neSWhoaFYLBaGDx/O8ePHu/QenYmMjImAVlW1lrDQ0RgMtrM6z2SKITw8h7Lyjxk8+Kd9bnToZJLU\nEwv4oWWPSoD6hr2YTPLEm/Cv0NBQnn/+eUJD2y81IzrWlREsrS1ZsoTa2lq2bduGwWAgOTkZp9PZ\n7jnf/Rnsz5/JEydO5NVXXyUuLo7f/e53vPXWWyxfvpzc3NzWNiaTqfXver3e79PsMjImApbHU099\n/R4iIi7q0vlxsZNxOPL7ZIkH18mRsZ6apgyRJypFz7FarcyePRur1ap1KMKPcnNzWbZsGU1NTTQ0\nNLBixQpyc3Ox2WzU19e3tqutrSU2NhaDwcCqVasoKur4yfUjR46wefNmAN544w0mTJjQZrsRI0Zw\n8OBBjhw5gqqqvPnmm6fEt3TpUqDlKcukpCRCQkIYPHgwxcXFHDt2jIEDBzJhwgR+97vfMXFix0/g\n+4skYyJg1dRuBVTCw7O7dH5M7CRAR1kfLADrcpej11vR64N7pH+jMRSzKZEG2RZJ9IDq6mruuusu\nqqurtQ5F+FFOTg633norY8eOZdy4ccycOZOMjAzi4uIYM2YMGRkZzJkzh9tvv50vv/ySjIwM3nrr\nLVJSUjrsOy0tjRdffJG0tDQcDgf33ntvm+2Cg4NZuHAh1157LdnZ2SQkJLQee/bZZ/nqq6/IzMzk\nqaeeYvHixa3Hxo4dy/Dhw4GWpK24uJjx489ueUt3KKqq9trFuis7O1vdsmWL1mGIfiL/0AscP/43\nLpn4dZeTlm1fz8DlKmPchf/pU1OVO3fNoqFhLxeNW91j19i+4z4cjqNcNO7fPXYNcW4qLi4mKSmJ\noqIivy6CPlfs3buXtLQ0rcPoNfn5+dxyyy2tDwD0VW19XxRF2aqqaocjAjIyJgJWTc1mbLaR3Ro9\nio29DofjMA0NfavmlttVQVAPLd4/yWYbicNxCK/X0aPXEUKIc50kYyIgeb0u6up2EB7WtSnKk2Jj\nrkFRDH2u5pjLXU5QUHSPXsNmGwmo1MtUpRBCQ0OHDm1zVOz6669vrfx/8s/q1T03W9CT5GlKEZDq\n6negqm7Cw8d2q5+goEiioi6hrPQDhg55FEXpG9u39NQm4d/WkoxBff0ewsPG9Oi1xLnFYDCQkZGB\nwSC/gkTXffBB390l5WzJyJgISLU1LU/ddHXx/rfFx92Ay11GdfWGbvflDx5PI15vY49U3/82U1Ac\nRmMk9fW7e/Q64twTGxvLjh07iI3t2Q8UQvQXkoyJgFRTs5mQkBSMxohu9xUdfQV6vZXS0r4xVdnT\nNcZOUhQFm22kJGPC71wuF//9739xuVxahyJEnyDJmAg4quqlpnZbt9eLnaTXm4mNvZbyin/j9Z55\nP7Te4nK3VKruiU3Cv8tmS6ex8QA+n/zSFP5jt9u58sorsdvtWociRJ8gyZgIOA0N+/B6G7q9Xuzb\n4uNvwOttoKJS+8WhblfLyFhPT1NCy7oxVfXQ0Hiwx68lhBDnKknGRMCpaV0v5r9kLCL8QszmJIoK\nl/qtz65y9dI0JYDNOgJApiqFEK3sdnvr04vx8fEkJSW1fu12u09rX1VVxcKFCzvs1+PxEB4e3mG7\nuXPn8vvf/75Tsebn55OVldWptlqSZEwEnJqaLZhNiZjN/ismqSg6Bgy4i5razdTWalt40O2qQFGM\nflkP1xGLZSAGg02SMSFEq6ioKPLy8sjLy+P+++/noYceav06KCjotPadTcbOZZKMiYCiqio1tZv9\nOip2UmLC9zAYbBwveNXvfZ8Nt7uCoKDoXtkRQFEUrNYRkowJv4qJiWH79u3ExPT86K7oXQsWLCA9\nPZ309HReeuklAObMmcP+/fvJyspizpw51NXVcfnllzN69GgyMzNZuXJlh/0+++yzpKamMmHCBA4e\nbH/ZxObNm8nMzCQrK+uUJLC/aRQoAAAgAElEQVSpqYkf/vCHZGRkMHr0aNauXQvApEmT2LNnDwAZ\nGRk899xzADzxxBMsXryY1atXc8UVV3DTTTcxbNgw7rjjji69N+2RIi8ioDQ1HcXtruyRZMxgsJKU\neBvHjv+NpqbjWCwD/X6NznC5K3plvdhJobZ0CouW4vN50OnkR4boPqPRSGZmptZhBIQDB37p98LM\nNmsaqalPnvV5GzduZOnSpWzevBmPx0NOTg6XXnop8+fPJz8/v7Vwa3NzM8uXLyc0NJTy8nLGjx/P\nlClTztjvpk2beO+999i+fTtut5usrCwuuuiiM7a/8847eeWVVxg/fjwPPfRQ6+t//OMfMZlM7Ny5\nk927d3Pddddx8OBBcnNzWbduHfHx8ZjNZtavXw/AunXr+NGPfsSRI0fYtm0bu3fvJi4ujnHjxrFh\nwwbGjRt31u/RmcjImAgoNTUte5f2RDIGkDzgDhRFz/GCxR037iFuV3mvPEl5ks02Ep/PicNxqNeu\nKQJbaWkpiYmJlJaWah2K8KP169dz8803Y7FYsNlsTJs2jXXr1p3WTlVV5syZQ2ZmJldffTUFBQVU\nVlaesd+1a9e29hsWFsbUqVPP2LayspKmpqbWTb5vv/32U+KbMWMGACNHjiQxMZH8/Hxyc3NZu3Yt\n69ev54YbbqC6uhqHw0FRURFDhgwBYNy4cSQmJqLX68nKyuLo0aNdeYvOSD7mioBSU7MJozGS4OAh\nPdK/2RRPfNz1FBe/w3nn3YfZFN8j12mPy11BWNjoXruezZYBQF3dTqzWYb12XRG4fD4fJSUl+Hw+\nrUPp97oygqW1JUuWUFtby7Zt2zAYDCQnJ+N0OjWL58ILL+See+4hMTGRqVOnUlhYyN/+9jfGjv3m\nQ73JZGr9u16vx+Px+DUGGRkTAaWmZgvhYWM6XE/lqarCvmgRRQ//nOJf/IL6//4XtZO/GAYPnoWq\nejl8uHNP8/iTz9dMc3NVrzxJeVJw8CD0eit19Tt67ZpCiP4nNzeXZcuW0dTURENDAytWrCA3Nxeb\nzUZ9fX1ru9raWmJjYzEYDKxatYqioqJ2+504cSLLli3D6XRSV1fX7hqz6OhoLBYLX331FQBLl37z\nBHxubm7r13v37qWkpIShQ4diNpuJi4tj+fLlXHjhheTm5vLCCy8wceLE7rwdZ0VGxkTAcLnKaHIe\nJzl5Rrvt6j/7jOLZj+Grr8c4cCDe2lpq33sfy+jRJC54nqDk5HbPt1gGkJx8OwUFi0lK/H6vjlK5\nWwu+9l4ypig6Qm3p1NVJMiaEOLOcnBxuvfXW1hGlmTNnkpHRMrI+ZswYMjIymDx5Mg8//DBTp04l\nIyODnJwcUlJSOuz3xhtvJDMzk7i4OHJyctptv3jxYu655x50Oh1XXXVV6+uzZs3ivvvuIyMjA6PR\nyJIlS1qf/szNzeWLL77AZDKRm5tLYWEhubm53Xk7zoqiqmqvXay7srOz1S1btmgdhuijyspWsmv3\nzxibvYzQ0LYXB9evXk3hrJ9iShtO4vz5mFNTUT0eald8QNnzz6OYghi46FXMw1LbvZbHU8/Gjdeh\n05vIGfsher2lJ27pNHV1O9i85UYyM/5KTMyVvXJNgPz85zlesJhLL9mOTmfq+AQh2lFbW8uvf/1r\nfvGLXxAWFqZ1OP3O3r17SUtL0zoM8R1tfV8URdmqqmqH28HINKUIGDU1W9Drg7Fa2/4h1bR7N0U/\nfwRzZgaD/vEPzKktCZdiMBB+800MemMpik5PwT330NzBsLnBYCMtbT4OxxEOHf6d3+/lTFzuCgBM\nvbiAH8AWmoGqNtPQsL9XrysCU1hYGAsWLJBETIgTJBkTAaO6ZgNhYWPQ6YynHfM2NFL08MPoIyIY\n8Je/oAsOPq2NaehQBvztFXxOJ8d/fC/empp2rxcZOZ7kpJbpyurqDX67j/ac3AopKCi6V653Uljo\nKABqarf26nVFYKqtrWX27NnU1tZqHYrox+6///7Wyv8n/yxZskTrsLpEkjERENzuShobDxIR3nbd\nl/LfvUBzQSFJv3sBQ2TkGfsxp6aS/Oc/0VxQQMEDP8HXxtYe3zZ06GwslkHs3v0wbndVt+6hM1o3\nCe/lZMxsTsBsHkBNzaZeva4ITI2Njfz2t7+lsbFR61BEP7Zw4cLWyv8n//REQdbeIMmYCAjVJ/aj\njIi48LRjTbt3U/PW20TM+B+Cx4zpsK+QnBwSn59P09atlD7zDO2tq9Trg8lI/yPu5mr27J3dblt/\ncLvLMRoj0elO33Kkp0WEj6WmZkuP36MQomPy/7Bv6e73Q5IxERBqqjei1wdjs6Wf8rrq81H2y1+h\nj4wk5ic/6XR/odddR9TM+6l9732q/98/2m1rs40kZegc7PbPKOjhYrAuV3mvj4qdFB6eQ3NzlRR/\nFUJjZrMZu90uCVkfoaoqdrsds9nc5T6ktIUICNU1G9tcL1a74gOa8vJIeO459KGhZ9VnzKxZuA4c\npGz+fIwDkrFddtkZ2yYn30FV9ZccOvxboqMvIzh4cJfuoyNudwWmoN5dvH9SeHjLA0HVNZsICRmq\nSQwiMOh0OhISEtDpZDygK5KTkyksLKSiokLrUMQJZrOZ5A7KIrVHkjHR77nddhobDxAfd8Mpr/ua\nmqh48UUsF1xA2LQbznD2mSk6HUkLnufYD++k6MGHGPjqIoKz235CWVEUhg/7JRs2Xs2+/U8yKuv/\n9chG3i5XOcERPZPodcRiGURQUAy1NVtITrpNkxhEYIiPj6e4uFjrMPoto9HI4MHa/BwQPUM+loh+\nr+YM68Wq33wLT0UFsY8+gtLFT+C6kBAG/O0VjImJFNw/k6Zdu8/Y1mSKZcj5j1Jd/RUVFf/p0vXa\no6pe3O5yzKYEv/fdGYqiEB4+lmpZxC+6qbm5mR07dtDc3Kx1KEL0CZolY4qiDFAU5TNFUfYoirJb\nUZSfaRWL6N+qazactl7M29CI/W9/I2T8+DOOZnWWISKCga+9ii7UxrE77qBu1aoztk1MnE5ISCoH\n83+Dz+fq1nW/y+WuQFW9mMyJfu33bISHj8XlKqGpqf06bEK0p6KiggsuuECm2YQ4QcuRMQ/wc1VV\nRwDjgAcURRmhYTyin6quPn29WPU//oG3upqYn/3UL9cwJiQw6K23MA0ZQtGsn1Ly5FN4qqtPa6fT\nGUgZ+jhOZwElpcv9cu2TXM4SAM1GxqBlET8gJS6EEMKPNEvGVFUtUVV124m/1wN7gSSt4hH9k8tV\nQWPjgVPqi3nr6rC/9hrWyy7Dktn2tkhdYYyN5byl/yDy7rupee89Dk26hvI//AFP1an1xSIjc7FZ\nR3L8+Kuoauc2H+8Mp7NljY3JrF0yZg1JxWAIpbpmo2YxCCFEoOkTa8YURRkEjALkJ7w4K9XVXwIt\n1fBPqvr76/jq6oj56Sy/X08XFETc7EcZvGwZIRdeiH3hX8m//ApKf/krmktLgZa1VQMH3oPDcQi7\n/XO/XdvpOjkypt00paLoiIwYT5V9rTxWL4QQfqJ5MqYoihV4D3hQVdW6No7fqyjKFkVRtsj6AvFd\nVVXrMRjCsdlGAuCprqbq9dexTZqEuQc30jUPSyX5pT9y/soPCb3uOqrffpvDk6dQ8/4yAGJjryUo\nKJqi4rf8dk2XswS9PgSDwea3PrsiKvpSXO4yGhr2dqsfh+MIxwsWs2//k+ze8wgHDvySwsJ/UFX1\nJR5PvZ+iFX1RVFQUq1evJioqSutQhOgTNC1toSiKkZZEbKmqqu+31UZV1VeAVwCys7Plo7hopaoq\nVdVfEhl5MYrS8rmi6rXX8DkcxMzqfIHX7jANGULic78meub9lMx9kpInnqC5uJiYnzxAQvxNHC94\nFZerApMpptvXcrpKMJkSeqRkxtmIiroUgEr7Z9hsZ7/M0+kq5eCBX1Fe8S8AjMYI9Ppg3O4qfL6m\nE60UrNZhhIWNJiJ8HFFREzVPQoX/mEwmrrjiCq3DEKLP0CwZU1p+o7wK7FVV9UWt4hD9l8NxCJer\nlMiIlilKT2UlVf9YSuiUKZiG9m5R0qABAxj46iJK5j5J5Z/+hCE6moSp3+PY8VcoLX2f8867r9vX\ncDqLMWu4XuwkU1A0obZMKitWM3jQA2d1bnXNZnbu/F+83iYGD5pFQsL3sFhaloqqqorLXUZjw0Fq\n676mtnYbpaUfUFT0BjqdmYSEmxg8aBYmkzZFb4X/lJeXc+WVV7J69WpiY+X7KYSWI2PjgduBnYqi\n5J147QlVVT/WMCbRj1RVfQF8s17M/re/obrdxDzwv5rEoxgMJPzql3iq7JT+6lcMSn+L0NBRlJZ9\n6JdkzOUqwWbtuanXsxEXN4WD+c/R2Jjf6Wr8dvvn7Ng5E7M5kTGj3zztPEVRMJviMZviiYrKBVpq\nq9XWfk1JyXsUF79LaekKUlOeIiHhZs1HCEXXeTwedu7cicfj0ToUIfoELZ+mXK+qqqKqaqaqqlkn\n/kgiJjqtqvoLLJaBWCwDaC4tpfrNtwi7cRpBgwZpFpNiMJD0wgvow8MpfeYZ4mIn09Cwl4bGg93q\n1+dz4XZXalpj7Nvi4m9AUfSUlLS5uuA0dXU72bHzfwkOHsKY0W93OoFTFD3h4dmkpf2GcRf+i1Bb\nBnv3PcaBg8/49UlVIYTQkuYL+IXoCp+vmerqja1TlJULF6KqKjEzZ2ocGehDQ4l77DGcO3di2WYA\ndJSVfditPp0na4z1gWlKaJmqjIq6lJLSZfh87nbbOl2l7NhxH0HGSLKyFhMU1LVF28HBgxg1agkD\nBtxNYeH/Y/+BefJEpxAiIEgyJvql2ro8vN4GIiMn4D5+nJp33yPie7dgTOobpepCp0zGlJZG3cI3\niAi/kPLyf3erP6ezpeK9xTzAH+H5RVLSbbjd5ZSWfnDGNh5PAzu234vH28gFFyzCFBTdrWsqip6U\noU9w3sB7KSpaypGjf+pWf0IbFouFO++8E4vFonUoQvQJkoyJfqmyYhWKYiQycjwVf3wJxWgkug+M\nip2kKArR992L++hRrJWJOBz5NDUVdLm/k+eazcn+CrHboiIvwWodwbHjf0VVvacd9/lc7Ng5k4bG\nfaSn/wGrdZhfrqsoCkOGzCY+/kaOHPk9FRVn3p5K9E0REREsXryYiIgIrUMRok+QZEz0O6qqUlG5\nisiIi/DkF1H30UdE3n47hpjul4/wJ9tVV2E8byAsOwTQrQKwTmchimLAZIrzV3jdpigKgwc9gMNx\nmOPHF51yTFW97N7zCNXVX5I2fD7RJ8ph+PPaw4f9Gpstg917HsHhOOLX/kXPamhoYMGCBTQ0NGgd\nihB9giRjot9pbDxIU9NxomOuouL/fo/OZiPqR3drHdZpFL2eiB/ciuez3Zj1CVTaP+tyX03OQkym\nBHQ6TUsDniYmZhIxMddw6PCLrVOxbredHTtnUl7+MUOHziEh4aYeubZebyIz42V0OiM7ds7E42ns\nkesI/6urq+Oxxx6jru60Ot9CnJMkGRP9TkVly7SUtSSOhs8/J+qee9CHhWkcVdvCb5yGzmQm+FgY\n1dVf4fU6u9RPU1MhFkvfmaI8SVEU0ob/Gqs1jZ27/pcvv7qM9V+Mx25fS2rKk5w38Mc9en2zOZH0\nkX+gsfEQe/c9Lgv6hRD9kiRjot+pqFhFaOgF1Px+MYaYGCJvn6F1SGekDw/HNulqdP8pwedzUV39\nVZf6cToL+9R6sW8zGsPJHvMOQ4fOwWZLJzn5di7MWcmAAXf2yvUjI8cz5PyfU17+EQUFi3vlmkII\n4U99a85DiA44ncXU1+8kWb2Rpi0fEf/0U+j6+BNZYZMnU/vAB+gIwm7/nOjoy87qfK/XidtdgaWP\nJmMAOl1Qj4+Ctee88+6jrn47+YfmY7OlExGRo1ksonMMBvn1I8RJMjIm+pWKyv8C4Fu4EVNKCuG3\n3KJxRB0LuegiDCHhBJdFUmn/7Kyn0pzOQgAslr5T1qKvURSFEWkLsFjOY9fuWbhcZVqHJNqRmJhI\nc3MziYl9o4ixEFqTZEz0K2VlH2JyRsDOSuLnPY1iNGodUoeUoCBsV1+FYV0tTmchjY78szr/m7IW\nfaOGWl9lMNjIyHgZr9fB13l34nQWax2SOAOv10txcTFe7+klUYQ4F3WYjCmKYlYU5RZFUf6gKMo/\nFUVZoijKbEVRRvZGgEKc5HAcobZ2K0H/biBs2jSCx4zROqROs115JUFft+zDd7YlLhyOwwCEhAzx\ne1yBxhqSQmbGX3E6i9m0+QZKSz9oswaa0FZZWRlJSUmUlckIphDQwZoxRVGeAaYCnwEbgXLADKQC\n8xVFMQM/V1V1R08HKkRx4dvgA+suG7HvPKJ1OGcleNw4jK4QTI1gt6/hvIH3dPrcRschjMYIjEYp\nkNkZkZEXMzb7PfbseZTdex4i/9ACIsJzMBoj0OmC0OlM6HRmDAYrIdZhhIVmodP1/RFWIUTg6mgF\n5SZVVZ8+w7EXFUWJBQb6OSYhTqOqXooOL8W0XyH5kWcxRHVtf0Ot6EwmrBMmUPf1GmqsW/B46jEY\nbJ061+E4QnDw+T0cYWAJCRnKmDH/pKJyFWWlK6ip2Uyzpw5VdZ+2l6bRGEly8h0MHHA3BkOIRhEL\nIc5lHSVjwYqimFRVdbV1UFXVclpGy4ToUcdXLsAT4iBedwmh116rdThdYrvicoIWfYI6wUNV9ZfE\nxkzq1HmNjYeIib6ih6MLPDqdgbjYa4mLPfXfi6r68PncNDdXU1e3nZLS9zly5PeUlLzLiBEvEBE+\nVqOIhRDnqo7WjN0GFCiK8v8URblOURR9bwQlxLfVrlhB4YHX0DmNDL33Ja3D6TLrJZcQdNSAzhuE\nvXJNp85pbq6ludlOcPDgng3uHKIoOvR6M2ZzArGx13BB5iuMHv0WiqLj669ntLvxufCPiIgI3nrr\nLdmbUogT2k3GVFW9ERgKrAZmAYWKoixUFOWS3ghOnNuaS0oofvwJCp57DGemSuJ509Gb+u80kj48\nnJCsMZgPmbHbP+9UiYuTi/eDZfF+j4oIH0vO2A8ICxvD7j0PUVDwutYhBTSLxcL06dOx9PEagUL0\nlg6r7qmqWge8DryuKEoUcAvwR0VRIlVVlcJH4qypqoqnpITm4mI8lXa8NdX4Gh34HA58jY34HA7c\nR47g2LYNAO/cDNDnMfD8H2kcefdZr7icoDUbqEmto6FhHzZbWrvtGx0tm4yHyJqxHmcw2Mi6YDG7\nd/+MAwefxeNtYPCgB7QOKyBVVlYyffp03n77baKjo7UORwjNdboEsqIoEcBNwHQgEni3p4ISgcPb\n0IDrwEFcB/bjOnAA5/4DuA4cwFdf32Z7JTgYXXAwxrg4ou6+G+st17Hp6HRio67BYun/z4rYrrgC\n08vzAS92+5oOk7H6+j3odBbMZvnc0xv0ehPp6X9i797HOHz4RVRfM4MH/wxFUc66L4+nntLSD7Db\nP8dZW0iQL5SY6CtJSJuBXm/ugej7D7fbzaefforb7e64sRDngI5KW1iBG4FbgVHAB8AvgTWq7Mgr\n2uBtaMCxaRON67+g8csvcR892npMZ7ViSk0ldMpkzKmpGAcOxBAdjT48Ar01BMViQdGdOnN+8OBz\neL2NDDpvZi/fSc8IGjAAS/QQTPZCKsPWMGhQ+/dVV7eDUFs6Op1sHdNbdDoDI0YsQNEZOHL0JXxq\nM0POf6TTCZmqqhSXvEN+/gI8nhoMlXp0lT6aYlWqfJs5tO8Fzg/6MclTH+5SkieECDwd/YQ/CnwC\nvAz8W1XV5h6PSPQrqteLc9cuGr74gsYvvqRp+3bweFAsFoJzxhI2bRqmYamYU1MxJCae1S8fh+Mo\nBYVLSEi4GZttRA/eRe+yXnIJQVteozZqG83NtRiNYW228/maaWjYTXLS7b0coVAUPWnDf4NOMXLs\n2EJ8PjcpQ5/o8N+v213J3n1PUFn5X0zHzYS/aSBy2CTCrr0WfVQkFQUfc0z/DgcsL1P3y88Y9sDr\n/a5MixDC/zpKxgaoqtoEoCiKRVGU81VV3d8LcYk+QlVVVIcDb0MjPkfLei5vZSXOvXtx7t5N48ZN\n+OrqQFEwjxhB1N13EzJ+PJZRWeiCgrpxXR979z2BTmdiyPkP+/GOtGe99FJM816lfpKPqqp1xMVN\nabNdQ+N+fD43oaGZvRxh36K63TR8+SWNX3xJ8/HjqB4PhpgYzCNGEJI7AdPgnnnSVFF0DBv2SxSd\nkYKC1/D5nKSmPHXGArGVlZ+xZ+9jeJprCVtuJnRHDMm//S3BY78plRGScyFJ7ofZ9ulNlF68F9dv\nJjHy/rcwDR3aI/fQV5lMJqZMmYLJZNI6FCH6hHaTsW8lYlOBF4AgYLCiKFnAs6qqXt/zIYre5K2t\npe7jj6n/7DNce/fhqamB5rYHRI0DBmC76kqs48cTfNFFGPz4mHpB4evU1Gxk+PDnMJni/NZvXxA8\nehQmeyh6dwMVlf89YzJWV9eysUVo6AW9GV6f0VxSgn3Rq9SuXImvthYlOJig885DMRpxHTxI7fLl\nAJhHjiR06hRCr7sOY2xsu3363G6atm7FsXkzzgMH8NqrQFEwxMViGTkS6yWXYEpJaW2vKAqpKU+h\n15k5dvwV6uv3kJoyl7CwUa1tHI4jHDr8f5SXf4TZE0f4Ah228BEMePcvGNpYnG4MCiP7qo/YvuEu\nqq/bxO4/fp8RP3kLc2qqn965vi8qKooPP/xQ6zCE6DOUziz9UhRlK3A5LWvFRp14baeqqhk9HN8p\nsrOz1S1btvTmJc8Zqs9H9dI3qHjpJXx1dQQNHowlK+vEmq4wdFYbuuBgdCHB6MPCMKWmord1roL8\n2aqq+pK87XcSFXUZmRkLA3JdTeGDD1GYtBpnjkLuhE1tLujes+dRKu1ryJ2wKSDfgzPxORzYFy3C\n/uprqD4foZMmETr5Oqzjx6N8a7S1uaiI+tWrqf1wJc5du0CnI2TchYROmUrI+IsxREWhuly4jhzF\nsXkzjo0bady0CdXhAJ2OoMGDMcTEgKrSXFREc2EhAJbsMUTedhu2q69GMXzzebWsbCX7DzxDc3MV\nwcFDsFgG4nKV0dCwB53ORHRxJvrn8rCNv5Sk/3sRXXBw+/fp85C3cQbVjZuJXhLOsF+8gXnYsJ55\nU/sYh8PB22+/zfTp0wnu4H0Soj9TFGWrqqrZHbbrZDK2QVXVcYqifP2tZGyHqqq9On8iyVjP8DY0\nUPzIozSsWUPI+PHEPPgg5vSRmiQAVVVfsmPnfZjNSWSP+Wentwzqb2qWL+fI3x+j6qceMtJfJjb2\n1Gr8qqqy/ouLCA8fS0Z6/y10ezZUVaXuo48pf+EFPKWlhE6ZQuxDD2JMSurwXNfhI9St/JDaD1fS\nXFDQZpug884j+OKLsOZOJOTCHHQhp9asay4rp+6jj6h+802aCwowJiYSedddhN98U2ti5fE0UlL6\nHvbKz3C5KwkyRhIWnIXy1z04P15P+Pe+R/zTT52SxLXH42lgy4abcNQfJnZRFCkvvNVj0659SXFx\nMUlJSRQVFZGYmKh1OEL0GH8nY68C/wXmADcDPwWMqqre391Az0b2qAvULV9v781LBjxPVRXH7/4R\nrvx84ubMIeJ/btMkCTv5BNr+/U8THDyYUVlLMJliej2O3uKpquJA7njK/09PVOJlpyVc9fW72bT5\nekakLSAh4WaNouw97oICSp56CsdXGzCPGEHc3F8QPHr0WfejqirOHTtw7tmDp7oaxWgkKDkZy+jR\nGOM6N92t+nw0rPkc+6JFNG3bhj48nIgZM4j4n9tOmYpXm5up++QTyn/7Ap6qKuJmP0rE7bef9f8f\np6uUzRum4a2yE7c4gaEL38IY4AmKJGPiXOHvZCwY+AVw9YmX/g38SlVVZ7eiPEvZA8zqls2bIb5X\nZ0cDlq+xkWN33oXr4EGSX3oJa+4ETeJwOI6x/8DTVFWtIzIyl/SRfzjjE4aB5OgPbqUi5zD1o2oZ\nf/FaTKZv1jsdOfpnDh9+kQkTNmIKCtyimKrXS/XSpZT/3+9RdDpiH32E8O99D0XfN3Zec2zbhn3R\nqzR8+ino9VjS0zEmJeFraqIpLw9vdTWmYcNI+PWvsaSP7PJ16hv2sXXzLeiK3CS8M5jBf38TQ2Sk\nH++kb5FkTJwrOpuMtbsdkqIojyuKMkpVVYeqqr9QVXXsiT9zezsROxERLJ4MhVt7/9IBRm1upvCh\nh3Du3k3Si7/TJBHz+VwcOfInNm66ltrar0lNeYqsC149JxIxANtVV2JeVouqeigsWtr6uqqqVFT8\nG5stPaATMdfhwxybcTtlz/2G4JyxnL/yQyJ+8IM+k4gBBI8ezYCX/8z5Kz8k6p57UIxGmnbtormw\nEOvEXJJffpnBy97vViIGYLMOJyPzZZoTVcqvOsLx++/D19jop7sQQvR17Y6MKYoyHbgWuADYDvwL\n+I+qqtW9E96pskdlqlt+CDhr4c6PID5dizACQskzz1Dz5lvEP/MMEdO/3+vXr67ewL79T+FwHCI2\n9jpSU+YG3FOTHXEfO8ahSdfQ8LtBNIVWMO7CTwgKiqbSvobt23/EsGG/JDnptl6Lx1tTg/voUVSf\nD2NSEobY2B6ZslZVlZq336HsuefQWSzE/eIJQqdOPaceUjiTwsJ/sP/A04R8aiCxdCIDXv7zKQ8t\nBAqfz0d9fT02mw2drt0xASH6Nb9OU57ocBRwDS1TlXpaNg//RFXVTd0J9GxkZ2erW1a9C4uvA48L\n7voXxJw7j4P7S+3Kjyh+5BEif3Q3cY8+2qvXdrvtHMz/DaWlyzCbBzBs2Dyioy7t1Rj6ksPX30Dz\nQD3Ft+wlMmICaWm/4eu8u/B46rho3Cp0up79Raw2N1Pz/jJq3nkH5+7dpxwLGjSI8FtuJuLWW09b\n7N5VvqYmSuc9Q+2KFYRMmEDi/N+0Wf7hXLb/wLMUFr5O2FI9CVHTSJw//7SdKfo7VVXxeDwYDAZJ\nws9FPi+U5EH5XqjYD+3L5n0AACAASURBVA57y+sh0RAzHIZcDrZ4bWP0E78nY9/pPBS4Cpikquq9\nXYivS1qfpqw8CIuvBZ2hJSGLDPynj/zFdfgIR2+5BdPw4Zz3+t9RjG0XsPQ3n89NYdFSjhx5Ca/X\nwXkD72HQoAfQ6y29cv2+quKlP1H58stYlv0vh4r/D1BQFB3pI/9IbOw1PXrt+k8/pfy3L+A+cgTT\niDRCr56EaVgqisGA+8gR6letxrF5M/roaOIem03olCnd+sXpPnqUwp/+DNfBg0T/5AGiZ84MuCTD\nH1TVy/Yd92Kv/JyoP+pJuPhHxD02W+uw/ErWjJ2DVBUOfwY7/gkH//1NAqYPgpBYQIXGCvCe2K80\naQyMmgEX3AbG/ruXq78X8H+PllGwekVR5gKjaVnAv637oXbeKaUtynbD3yeDydaSkIUl92Yo/ZLP\n6eTo9B/gKStj8PJlGON7/pOHqqpUVv6Xg/m/+f/snXd8FGX+x98z20t6T0jooKAgCCggiL1y9nL2\n3tDzFBXLFc/eC3r2Xjj9eVbs2Avq0XuHkEB63WzfmXl+fzybBoEE2JAE9v16zWtnZ6c8s7O789lv\nJRAoJDVlPAMH/R23a2D7G+8FBFeuZMPJp5B9178QR+ZRUfk1OdkntyoqGmuMUIjy++6n7t13sfbv\nT+bUG3EfdlibQsu/YAHl9z9AcPFiEo4+mpy7/oUpOXmHj+n5+mtKb7sdxWIh9+GHuyxZpKegaV7m\nzjuTQP0G0u4T5J1/C2mXXtLVw4oZcTG2F6FrsPgdmP00VK4AezIMOgYGHg25IyC5N5iipWAMXVrL\nVn8Jyz+GssXgzobxf4FRl/ZIURZrMbZYCDFMUZRDgHuAh4F/CCEO2vWhdpyt6oxtng9vnCRNm+e8\nB+l7V0uRHaX07/+g7r33yH/hedwTJ3b68RoalrNmzb3U1v2O09mfgQNuIy1tUtwt0QIhBOuOPgZr\n3z4UvPBCpx8vUlrKpmuvI7hsGWmXX0bG9de3WxNLGAY1r75KxRNPYk5NJffBB3AdfHCHjmeEw1Q+\n9jg1r72Gfdgwej3x+B5ftiFWBIMlzJl7Kka9l7S7NfJve5Ckk07q6mHFhLgY20tYMwu+/rsUYdn7\nw8FTYL9TwdyBNlhCwIaf4KeHofBnSMyDQ6fBAec2i7ceQKzF2AIhxAhFUe4HlgghZrQsALu7aLPo\na/H/4D9nS/V92ksw6Oi2N+4IQkDJAvkBqFotzagmCyTlQ/4YGHAU2Ny7dhJdRP3MmZTcfAtpl19O\n5tTO7fUYClWwbv1jlJb+F4slmb59rycv9+xt9vTb2yl/+GFq3niTgT/+0KnlDIKrV1N8+RUYPh+5\nDz5AwhFH7ND2gaXLKLn5ZsKFhaRecjGZ11+/3eDywLJllN56G6E1a0g55xwyb522S/1K90YaGpYz\nb/6fMVUbpD4g6P3os7gnTOjqYe0ycTG2h+Orhs+nwrIPIbUfHPkv2HcytPFHXG9owD9nLqHVq4mU\nlqJVV6GYLag2K+asbKz9+mJzeLCtfw21fB6kDYTD74B9T4IeEOYQazH2KbAZGSc2EggA/xNC7Nam\neduswF9XBO+cA2VLYewUmHSrdF92BC0MG3+BlZ/Bqi/As1kuT8iRFjc9ArUbQQuAxQUjz4eJt4Ar\nLXYn1smE1q9nw+lnYN9nH3q/8XqHq4PvKLoeoKjoJTYWvYBhRMjvdQF9+lyLxZLYKcfbUwitWcP6\nyX8ic9o00i6+qFOO4Z8zh+JrpqA6HOS/+MJOt90x/H7KH3yoycWZcd11JBx5RKvPVHjTZmpeeYXa\nd9/FnJpK9t13kTBpUozOZO+jtvZ3Fiy8COtmC6nTzfR96XUcw3p283ifz8err77KxRdfjCtGySFx\nuglrvoGProZALUyaBuOuB3PrP2GNPZDrP5lJYNEiMAwATKmpso2ZYSACASIVFaBpciOTCVteBnZH\nNTZnFfb+BdhPvhnTiJO6taWsM4q+Hou0iq1RFCUH2F8I8fWuD7XjDM/KFr999imOAw/c2tUV9sNX\nt8O8V6VPeuQF0hyaPQzUFnWLDAOq10LRbGkBW/MNhOrB4pQZHPucIH3ZrhYZXoYORb/DgjdhyXtg\nS4Rj7oPhZ7ep9LsThs/HhrPOQq+uoe+HH3RKnJhhhCkp+T82FD5NOFxJRsaxDOh/C05n75gfa0+l\n8Kyz0b1e+n06M+ZuXN/s2RRfMwVLbi4FL70YEzdhw/ffU/HQw4Q3bMCUmopj2DBUp4Nw4UaCy5eD\n2UzyqaeSOfVGTEl7R924zqSi4kuWLL0WxxoHaW+46fPmDGz94olLcboRQsDPj8B390LmEDj1+a0K\ntAdXraLmtdfxfPYZIhzGNmgQ7iMOx3XwWBz777dVP1cRiRAuLia0eg3BlSsIrlhBaPkKtMrKpnXM\nLoE5NRVTVi9MOb3lfEoKpuRkOaWmYhs4sFX3jN1JZ5S2OAQYKIR4VVGUDMAthNiwi+PcIfZzucR7\n+QU4DjiA9GuvxTV+3NY3rs3z4JfHYeXnIHQw2aSwsidDqAEaSsGIyHVdmdKtuc+J0G8SWDqQ2Ve+\nHGZeD5v+B4OPh5OfAUfXXOT2EEJQMvUmPF9+ScHLL+EaOzbG+9cpK5/J+vVPEAwWk5w0mv79byI5\nud3PXZwtqPvvfyn929/p/eYbOEePjtl+vT//zKYp12Lt04eC116NqRtUaBreH3/E8+VXhFatQoRC\nmHNzcI0ZQ9JJJ8Vjw2LMps0zWLXq77jmOUj7IpM+/3kHS1Zm+xt2Q2pqarj66qt59tlnSd2DOw3s\nNYQapDVsxUzY/0yY/CRYm4VVYPFiKqc/he+XX1AcDpJOPomUM87Atu++O/XnU6uqIrh0CcFfPiG8\n+He0qjL0oIoeNqOHzRhhY6ttzFlZOA8cifvQQ3FNnLjbxFmsLWP/BEYBg4UQgxRFyQXeE0KM3/Wh\ndpxRI0eKWTffTNULL6KVlm5flPmqYN33MhvDXwOBGmnRSsiWPuze4yBtQJNlSwhBeN06/HPmEFi2\nDK20DK2iHGEITCnJ2AYOxD1+PM6DDsLkcsEfz8Ksf8igwjPfgNwDdudb0SFq3niT8vvuI+OGG0i/\nMnYVSHQ9RHn5TIqKX8LnW4PbPYT+/aeSlnpoPDh/JzECAdZOOgznmDH0emp6TPbp/fFHNl17HdYB\nAyh45eUu+2cYJ3as3zCdDRueJOFbG+lLBtL7rTcxJfa8MIB4zNgeRPU6GSZUtQaOvhsOvqbpvhpc\nuZLK6U/h/e47TMnJpF58MSlnnblTGdnbJVAL63+Atd/Axt8wKtehh1V0zYHmGkwokkPQ48C3aBV6\nVTWYTLjGjiXxxBNIOPJITO7OiwWPtRhbCIwA5jcG7TdmWO7ySHeAxpgxIxym/oMPqXr+ebTSUuz7\n70/SKSeTeNxxHb7hiHCY4KpVBBYsxD93Lv65c9FragDpt26qQG4yodVUE1q+AsPvR7FYSDzpT6Rd\ncik2SyW8d5EM9J/8pHRbdhO8v/xK8ZVX4p40iV5PTd/lek6GoVFfP5eKyq8pL59JJFKD2zWYPn2m\nkJl5HIrS/QMpuzsVjz1O9Usv0f/rr7D22rVSLQ3ffsumv96AfdAgCl5+KfY/fnG6BCEEq1b/g82b\nZ5DwqYWsitHkv/wSqq0D2WndiLgY20NY+Tl8eJUMBTrjNeh3KCAtVxWPPU79Bx+gJiSQduklpJx3\nPib3booP9FbI0KKi36HoNyhdBEJHJPcmmHI0DWXJeL7+gcjmzSg2G+5Jk0g84XjcEyei2mNbPiPW\nYux/QogxiqLMF0KMVBTFBfzWVWKskUZRVvv2W4TWrAWTCfvgwdj33x9b/36YkpNR3W5EOIIRDKCV\nVxApKSG0ejXBZcsQYVlczpKbi3P0aJxjRuMcPRpLfv5WFh4RDuNfuFAGHX74ESIcJulPk8m46iIs\nP94sU28PugqOvkdmYHYhoXXrKDzrbCx5efSZ8fZOV083jBA1NbOprPyayqpviERqUFUbaWmH0ivv\nfFJSxsYtYTEkUl7OuqOOJnHyieTee+9O76f+088omTYN+35DKXjxxR5pOYmzbYTQWb7iFsrKPsL9\nmUpu+Fh6PfFEt+rp2R5xMdbD0cLw7b/gt6chZzic+Sak9EZEItS8/TZVT/8bIxQi9YLzSb/iiq6P\nGw3UwcpPZcz3+h/AZEXsdwaB5GPw/LIEz5dfoldXg8UiNcSQIVhysjGlp2NKSkJ1uTC5XKguF6rb\nLR9drna/cyISQbVaYyrGbgIGIrMp7wcuAf4jhIiNP6WDbCubUghBaNUqPF9+SWDhIoJLl2J4vW3u\nw5SairVPHxzDh+MYPgzH8OFYcnJ2aBxadTU1r75KzetvgNlM+uWXkZq/EXXec9B7PJzxOrgzduoc\nd5VIeTkbzzsfIxCg7/+9u8NxO7oeorr6ByoqPqeq+gd03YvJ5CY9/TAyMo4hLXUiZnM8+6mzKH/g\nQWreeIN+Mz/B1r//Dm9f+3//R9k/78Q5ahS9nn129/0TjbNbEUJnxco7KC19D9c3KgWms8i58189\nRpBVVlYyefJkZs6cSUZG1/xWxtlJ6orgvYth81wYc4U0QJhteH/9lfL77ie8bh2uiRPIuu02bH27\nYZJJ1Rr443lY8BZoQdh3MmLc9fg2hvD//huBJUsJrlyJUV/f7q7UxETM6emYMzLkY3oaQtMx/H4i\npaUEly5ln3lzYx7AfxSyL6UCfCWEmNWhDWPINktbbIEwDAyPB622FsPrQ7FZUW02zBkZW2Vr7Arh\n4mIqHnqYhlmzsPQuIPucibiLp4MzDc56C/JGxuxYHSFSXk7RBReiVVVR8OorO5T+7vWuorj4Ncor\nPkfXvVgsqWSkH0lG5jGkpoxFVXuWG6SnotXWsu7oY7ANGiTLkHTw5ioMg8rHH6f6xZdwTZxAryef\nRHXs3a2m9nSEMFi95i42bXoT+zyVXkVHkv/QY/HrHqdz0DWY8xJ8f6/MnDzpKRh6CuHCQioefZSG\nWd9gKSgg67ZbcU/qAcW9fVXwx3PwxwuyokL/I2DCVOgjQ+GNUAi9qgq9oQHD55OT14veNO9Dr61F\nq6xEq6pCq6xEr65GsVhQnE7MGRnY992X3Lv+FVPL2INCiGntLetsOirGdjfeX3+l/J57CW/YgHv8\nKLJ6L8CqVMDxD8HIC3dL+YtIaSlFF12MVlVF/ksv4hzRfj1eIQyqq3+kqPgVamtno6p2MjOPIzvr\nJFJSxqKq3bd2y55M/ccfUzLtVtKvu5aMKVPaXV/3eCi94280zJpF8p/PJvuOOzqtllyc7oUQgqLi\nl1m75n4sGxSyZ+9H3/ue2y2tznaFYDDIrFmzOOqoo7DHOEYnTowRQlbS/+4uKFsiS0Cd8BgaSVQ9\n8yy177yDYrWSfuWVpF58Uc8r7Byshzkvw2//Bn8V9BoDB14IQ07qeL3S7RDrmLH5QoiRWyzrsgD+\n7ogIh6l54w0qn3kWdI20MQmkZS9BHXS4DO5PLui0Y/sXLGDTX/6CCATJf/GFdoWYpvkoLfuA4uLX\nCAQKsVky6JV4OHmWEVg0IT+ckQAgQBjyy4iQjy3nt3xdMYHVFZ3c8tGRLC2FznRZAqQbF+frLggh\nKLllGp6ZM8l54H6STz55m+v6fv+DkttuQ6uoIPPmm0i98MLu/480TsypqPiKZUtuRASDpHySQL9T\nHyTpmGO6eljbJB4z1gMI+2Spit/+LasSJOXD0fegZU+g9j/vUPPaaxiBAMmnn07GtVMw93R3c9gv\na4n+8TzUrJO1R/sdBgOPhD4TZRWGnUiEi4kYUxTlauAaoB+wrsVLCcCvQojzdnhku0B3FmONRMrK\nqHjoITyff4E51U1a/0qSB4RRD/0LjL02pu2UhK5T9fzzVP37GSw5OeQ/9yy2Advuz+lrWEXJuuco\nqfkKjRCJAQv5xT4yyz2oHfNWbwcF6MBOHClRcRYVaM5UWQeu6XmajLdzZ4ErY8eTIYSQNW8CtRCs\nk4+BuubnoQYwWWVvNLMd7EnyWAnZsiGtM7VbFPI1wmGKr7gS/++/k3rhBaRffXVTRqQQguCy5dS8\n+iqezz7D2qcPuQ8/hGP//dvZazelUeQrSrd473sqgUAxS+dfiye0FNsyhZzqwyi48q4djondHcTF\nWDfFVw3rv4fVX8muNBEfpA1AjPsrAX0Q9TM/pf6TmYhQCPcRR5B54w07FdvaHkIIdN2Lpvsw9BCG\n0XIKI2g0AhiIFsaBxnlFNWNS7agmBybVjsnkwGJJwWRyt/9nVQjZZnHxu9IiWF8kl9uSIHe4TFhI\nGwApfSClryxvtR0jQ6zEWBKQggzav7XFSw1CiJr2dt7uwRXlWOBJwAS8JIR4YHvrx1qMGUaIUKiS\ncLiCUKiCULiCcEjOhyPV8qILHVW1YjYnYrEkYTEn4XAU4HD0wensjdWa2ebF9f3xPyqnTycwbx4m\nt5nkglqS97NjPepqGHE+uHetWKN//gIqHnyQwKJFJJ5wAtn//MfWWXPeCiIbf6Cy9GNKwguptwdR\nhCCjKkxBjZPExOEoaQPkByq1rxRCtkSwJ8oCuIoKNN4gFfm8ab7l86Y3FCJ++Y8q7JVToE6W/mic\nfFXR+SpZ/63xeWMh3i1xpEqx5M6Q7ahMZmjscdl0LJ+05gXr5PGEvu03TlGlNW9bmKyyGHBCVvS4\nmVKkuTPkl9Hm3tr6Z3HKyWyLqZgwQiEqHnyI2hkzUOx27EOHotrthDdsIFJSguJ0knreeaRfdeXW\nsZCGAd4y2cqrrghCHtnay4jI828cu9W9hUBOjV02sK7J9mJ1RVAXHUfj5NkcvXZ+eR0bhbyiRieT\nFMsWR3RygsUefXTIcdsTpRvBltRiPnGL+SQ5v73GxCJq4d3y89wDEUKnaOMrbFjzOLoSwjHPQm7i\nyeSd8tdu5bqMi7FuQNAjLV4lC6F0oezLXL1WvuZIQSs4hgD741tTQ8MPP6CVlKLY7ST96U+kXnjB\nToswXQ8RDpfLe26oLHrvLScUalwm5w0jEMOTlSiKBaslFYs1FaslDastHas1A5s1A6s1A6s1Has1\nDZPJgao6MKk21NpNqJvmyvenZAGULwM93LxT1QwJudIL5EiRj6q56XdFOeuN2AbwyxNRMoEmB78Q\nomjH3opW+zIBq5EZmpuAOcCfhRDLt7XNiBFDxHffvYoQWnTSMUQEIXSEoTUtNxpfjy4zjDDhSA3h\ncBXhcBWRSA2hUCWaVtfGuEzRi5KGqtpQMGEYISKaB03zoGn1iBY3e5PJhdPZD5erPy7nAFyu/jid\nA3A4ClAUE/45c6h55VW8P/0IhsCRFsbdK4xrzIHYD/kTyj7HQFJeh94zva6Ohm+/o/addwguWYIp\nPZ2sabeQNHmy/GJVrEDbNBtP+Y94/CupdvipTzIjFAVn2EyuOoTsrJOw9T5cmpy7y41HCCkW/NXy\nn5mvErzlslaMt7x5XgvIG7yhAULemBvFkD2p+YvgSJEdF9p6bnHI42lBOQVq5f4bSqGhXAqYpuNW\nQEOZHFdHrH6K2izMGgWDKz1qdcuS/U4TsuU/qaQ8KfI64LYNrlpN3X//K8uxRCJY8vJwHXwQiYeN\nw2TUQm1hVHRtbDFfBHpo566HPam1pbJRpLlaPFct8gfJiEAkCL6K6PtWIYVW7Ub52EoYK/Lckwsg\nMVeKJKtLvleqWQoiQ4+6vnXQQlKoRYLRx4C8Zo1iP+iRlk6tAz/aJqsUeMKIinHRYj6Kam4WcPbG\nx2T5GWrr0WyT2ygm6b5QTLLeUpOgjC5TFLnc6pZTjEV7W0Qidaxb+iAlle8jzDqWjQpJnoFk9TuZ\ntINPxdLFLqW4GNuNGAbUboCK5VCxQoqJ8mWIqrXoYQXNrxIhi4ipFxEtmVCtQmhjGVpFBQCK04lr\nzBgSTziehMMP32apJCmyqgiHKwiHKwmFKqXYCjcLrFCoos37rqrasNmysFmzsNoysduysdoyMJsS\nUFUbqskmHxUrqmpFiX6vFJoNBXIeQEEIHd0IYOhBdCOIofuJROqiOqCaSPSxcbyGEd5qTFuMEEVR\no/U0o8cSoCBQjKjoEgIlauVXhGhaZ/zxa2MaMzYZeAzIBSqA3sAKIcTQdjfe9j7HAncKIY6JPr8N\nQAhx/7a2GTzYJp55dueKYZpMbqzWtKjybVbDNlsmVlsmNmsmNlsmFkvqdguYGoZGKFSK31+IP1CI\n378ev28dPv86QqGyFudnwensg8PRG6s1HXPIhrasGG3+avQ1ZSgRUCJgc2jYHGYs6WmY0jNQk9JR\nXCkgTAhDQfcECFdXEywqIlRRjjCDOTcRx8h0rP0saJEKgpFq/OYgfoeJkL05+86tZJCePI70grNJ\nTB0djyXaWfSItOA1WvsarXFhL4S8UiREGq08LebDPilSGkWevsUXXlGlQEvMlSIlMbe1pU1RoqIx\nLPfZKA695VBfLK2BLbEnSdN5cm9I6R2d7yPn7clS+JmsgBIdf4Mcf7CuhdWypoXlMiqOG59vOf4t\nMdulJTEhVx4zuSA6RecT87ZqGBwTtLAUZSGPnBpFWtN8vXwuRAuRpLQWTUTf65AnamX1yPclWN/s\n5t5ZcbslqiVquXNL8Wd1Ry2u7hbzUatlo2C1uuVnwmSR19BkbTEffVTNW1mxNc3HpnX/YfPmDwi6\nquWNwgu2KicOPRO7Iw+7PRurOwNrUiZmRxpmqwvVakW12KIWRSUahCBQkL+NiqJErYnKVv9ThJBD\naHrS8ncn+lTTBZtLNpGXk4fZ3JZFsvVOhdHOfUpRmppNNy8TW6yiwJb72dJKLgRb3xPbOMEt12nc\njxIN1xC0vY7Qmy2xQiAa/xCIFn9CoufRvHX0DVWgySsh5PVoGk/Eh+Gtx/DWIjzVGLXlGJ5KRH01\nem01WkCXFenDKrpmR4+Y0IM6QgiESYAJhAmwmTEX5GEpyMXcKwdLn15Y++RjKDqa3oAW8bR49BKJ\n1Ea9S5Vo2talIBqNG/L+moXVloXNltkkvGw2OZnNiV12fxJCoGkNhMPyPMKRGini9AC6EUDXAwgR\nabpWAj167XQEhlyOIY00LZYj5LL9958eUzG2CDgc+EYIMUJRlMOA84QQl+7sG6AoyunAsUKIy6LP\nzwcOEkJcu61tDjhgH/Hdd6+gqBZUxYSimFtNqtryuQlFsaAoJlTVgqp2foaHpjXg86/H511DQ8NS\nPA3LCAY3o2metk2uRnQC2rS8RD+bigGqAJWo4hbyuSIEQlEwFBMmawIORz4JqaNJzjyUxMThWCzx\nquvdBiHkDb2hFOo3Q/0mqFkLVeuksPJVQKAetvkPTZE3ZkeKtLIlF0DWUEjr3yzAHJ14vYWQ4rNR\noAmjWdyZ7TK+z5bQfaytnUEkIIVZoztcD0srbZNFr/GG2tLCF73xGpoUvoFoa7ZArbzejQKy0V2r\nBaVFMFbCbwsE0GA1U6Pa8FrMBG0mIk4FzQqGVcGwg7CAUAC1xRRn99BSG25PE4rWj9v71onGFZoN\nR1s/3yFMmEx2VNWBxezGYs3EYc/Bbu+Fw9FLepZsja6/dGnF2ovpaMxYR1PbIkKIakVRVEVRVCHE\n94qiPLGLY+wQiqJcAVwBMGCgkzVr7kFRtxBhjeJLtURFWMvlZhTVgsWS0mwVs6Rhs0kfsdm88wH1\nQhgEg5vw+dbh86/F51uH37cWn38dmuZpta7J5MZiSkT1KVDpQ1R5wWdIC1k4+t0wiegfdaUxNlH+\n1gPCDFhAcYM5VUCChmaBiFkhYgVowB9aTnXpcpxln5Dk3p/knKNJyzwKmzV9p88xTgcQImoRi1rF\nIoFm61mjZayhVFq0PCXSfecpkTfeVihRC4gDzA4pbPSwvDmHfc037rqNslH90velNW0ra1h03p29\nY9k/Ld3FLeP5WlrKGi1nhtZslWkUY+5MGW+XmNtsEevqhAghmq2YTXFhjbGQW1jItGCzJa2ldaxJ\ngNU2zwfrpUXO0KLWjEYxJloIsRYWD0NvHkNHsLhko2WLMxo7Z5eWL9W8hRu0RRxnIwqtnmuKTpmo\npcyopSEjgNEYaKKDqR7MDSomH1jrFZQIqEZro5LSmCzdljjY4tJ29EoLBJFwBIvF0oGA6m0sV9p5\nfZeTkjpAi2OIlqexrVNqzHNStti4rfW2eC5avtbGe48hLXJCF2AIhCHvI0JXMHSleZuWhkqTguKy\ngMsKTgs4rSguG4rLLp8r0nonRERaijQvmu5F133ouo9IpAoChTTWRzWZnNEwH+l1arSItZys1swe\nUzjcMDQMI4BhRFpZuyBqAWuyim2xvGndDn7f6bgYq1MUxQ38BLytKEoF4NvRE9uCzUB+i+e9osta\nIYR4AXgBYOjQTOFwFLSOFRMauhGIxoc1Lm8ZM6ZjiDCRSB1tffhNJlf0g5MVdVtmYbVlYLWko5ps\nKIoJQw+haR4iWj2RSC2BQBF+/0aCwaJWvmaLJQ2XawBZWSdG48cGYLcXoC9YR/17H9HwzTeg6Zhs\nOq7sEI6CVOwjxmAdNh7TPuNQUvq0GUOkezwEly3DP28+DbNmEVq1CtWVTMq5Z5N28qHgX0+gaj7+\nuqX4fGvxWMqoClVT2vATrP4biUYqOQkTye5/Mea0oT3DetEoDLyVzXFjWjAahN4YMxa9YVldMoi7\nMUas0SW3oxh6c7xaQ3nreDVvuXR1tXRRNs5HOvBVMNmiSQHZMhtn8PGQ1Cvqoow+ujNlbFEbhIuL\nCS6ci/BWY0k0Yc8wofo2N8eJrf9BCr6Wn3GTTYqilN7ShalapHhSom7KUBtJFttKpDBZW8SQpYAp\nQQpFPSKFSfky+T5tub3V3cJd2cJlmZQH1oRmwaFG47mMFiJGC0YFbmPcWKA5dqxRmIYaogJqy/kW\n7srtJXR0FNXSOmbMmR6NGTO1iBVrfFSisWJRsdT4mtUVdU0mtHBPRt2V1oTm1y2unUqh3xK/fyMb\nFjxChfdrDIuGU2oxcwAAIABJREFUWgeudakkJRxISr9JJA+diCUzp0vcQ/GYsU7G0GVIQ10RVK5A\nlC3DKF6KVrQKvd6HFlLRgioa6Wh6CpGQFc0TIVJZhwhXy32YTNgGD8I19hDcEybgHDMKxWxGCIFh\nBKRrL1Ir465C0TixcCXhkHz0eldQHfoRXd/699FkcrcQaJmtXJbSjZmN1ZoeU4+WYWhEIrXReLGq\n5jjyqJs1FK6MxpTXout+dD2IEO3FksWOjropXUAAabA+F0gC3tqVjEpFUczIAP4jkCJsDnCOEGLZ\ntrbZlWzKxgshL0J19AI0Z1GGQhVNzw1jS4tFM6rqwOHIb4oHczr64HJJ4dXSLSgMg4avZ1H13HOE\nVq7EZFdJKvCQuI8T+zEXoIw8V1oxdhAhBIGFC6l57XUavvoKS69e5D70EM6RLWqLNZQhiufg3fw1\nVZ7/UWGtwOtSUXVBZp1CnjqUpMzDUPJGyNopiXnbFAExQ0Trl7WZVblFbJKvMhqwv+3r0C7WqEvP\nkdQ6gN+WIMVcYzB4oE7GczWUyeO29U/GlhTNpkxsEdvjah3L0ygqmoL3o7Ff7kzpVnSk7JQIDq5a\nRcWDD+Kb/Vur5WpCAkmTJ5N22aXNLa8iQenyrN0IdYWtA/tDDdHkh8ZsyhYxSY4UacHaMmjfldY8\nb3W3P/5GV2xjAH/L7Mm6jXJZuGGH34Pt0hR0n9g6e3LLrEpbQrPga+k+bOlWtDhaB/DbEpsFfmMM\nXw8gEqll3dIH2Vz9XzAEzqV2sp3HkXv8ddgKenf18IC4GOsyhJC/deVLoxmU0cmzKfqyStg1nJBp\nX4KhdAIrNuJfuBAiEUxpaSQedxwpZ52JbeDADh9S07wtAvjLWwX0h5uWVSLE1n8EVdWBqlpl8H7T\nZI3GdStNj41xjfI7qsrEPT0QDd4PohuBrbxVjSiKBas1XbpVbRlYLKmYTA5MJmdTeQwVC4q/Guo3\no9RvlvO+KhRfFYoWigbtNxs8lWhwPwIy/lrd/SvwK4pyPPAEsrTFK0KI7XZH3h11xhrrm4TDVdI0\nKTRU1Y7ZkojZlIDJ1H5bIP/8BZTdfTehFSuwpjtI61dC4tBk1CNvgwPOiVnpAP+8eZTcMo1IWRkZ\n104h7corUdr4Ry20CA1Fn1Cy6T+URRajKzqJngj5m4NkVoXkv4/kAnkjtiXsWGmLlq8LvbXFqGVw\neKNrqy1MttaZeu7MZpdXU3mJzOYA5rZKW4Q8W9cUa/m8cVnQI4PIzfbmOmNN2Y7ZrWuONR23a9rL\n1H/6GaV33IHqcpF60UW4xo9rKm3h+fprPF98iQJk3HADqRdd2Oa171YIIa9BXZF00zZZFv2tXYiN\nU1NpC2eLEhdRF27j59Rs7zEiaXdQVvYpK5feik4A5+8WClIuIOei67eZAddVxMVYN8NbKcVZ8R+w\n7jvYPB8QkD0MY+if8XoL8Hz5Dd7vv0eEw7gOOYS0yy7DdfBBMTm8EEY0GaC1aNN1n6wtpsv6Yo21\nxgSNGdHNtcVoqjNmoKiWVnXGVJNdhio1lbVIxWJJxWbLxGxOats6XLVG1hlb+42sO9b4R9Jsl+Wg\nUvo0T0m95B/aRsu5amn6o6ck5cYr8O9utKoqKh55lPqPPsKclkTm/jUkZlejTLwBDrlRWk9ijN7Q\nQNm/7sLz6ackHHssuQ89uN12FJrmo6zsQ4qLXsYfLMKmJNBL709erQ2L39vs7glHaz9tVYG/+UvQ\nvKwxqKRlBf7o1GR1iYqtJtHVYpnVFb+hboH3xx8pvmYKzpEjyXviccxpaVutEykpoeze+/B++y3O\nMWPIffghLFlZXTDaOF2NpjWwctnfKK/+FMt6heyFw+lz63SsvTpWNmd309DQwKOPPsrUqVNJSNj1\nljNxYoy3UsakLpoBpYvkH+Nx16INOou69z+m5u230CurcE2cQOZNN2EfNKirRxwbgh5Z7HXh27Km\nGED6YOg7AXJHQu4ISB+0Q2Ewsa7A3x9Y2+KleAX+FghNo3bGf6h86imMQIC0wweRnvgtaq/94ORn\nIbtzK6MLIah55RUqHn4E96GHkvfU9Hb7gzX2pSwufpWa2l9RVTs52aeQl3cObve+8TIYXUikpIT1\nJ52MJb8Xvd94E5N721YNIQT1H3xA+b33obrd9Hr2GRxDd7riTJweSDBYwoJ5F+IPrCfhCzN9B/6V\n9Muv6HCT+Thxtkvhr/DTw7Iyf0pfOP4RjIIJ1L71FlXPPY/h85F06ilkXn99z22J5KuGP56F/70g\nw2my9pNerH1OlDG3u0CPqMC/o3RHMeafO5eyu+8htGoVrrEHkXVADbbaH2HEeXD8ozILajdR+867\nlN15Z4cFWSNe7yqKi1+jrPwjDCOM0ymTEDIzjsHlGhgXZrsRIQRFF15EcOlS+n78Edb8/PY3QhaG\nLb76KvTaOvIefZSEww/r5JHG6Q54GpaycN7FaP5a0l5z0v+6f+OeMKGrh9UudXV1/O1vf+Oee+4h\nOTlegqdHsP5H+GwqVK+BYWfJZuH+CNXPPU/NjBmoVivp11xD6vnnofSUZuGeEpj9NMx7VYZL7DsZ\nxt8AvQ7c7mZC1zH8fvTaWrTKSrTKKvlYU41iNqM6XZgzMrAPGYK9f7+Yuin7A5uEECFFUSYBw4A3\nhBBbl9LtRNoTY1p1NYHFiwkuWUp4UzF6bR2Gz4ditaLabJgzM7Dk5WHt0xfHAcN3qT1IZPNmyh95\nhIYvvsScm0PW1eeTUPIkSv0mOO5BGHVJl7jeGgVZ4vHHk/voIzskpMLhGioqv6S8fCZ1dXMAgdPZ\nl4yMY8jIOJrEhGFxYdbJeL7+ms1/uZ7sO+8k5eyzdmhbrbKS4mumEFy+nNwH7pedGeLssdTVzWXh\nwouhJkTGW2n0v/cV7Pvu29XD6hDxmLEeihaCnx+Dnx6C1P5w5uuQNZRwYSFl99+P78efsPbtS9bt\nt+OecEhXj3bbVK2F356ChTNk8s7+Z8AhNxAhleCSJQRXrUKrqECvrkav92D4fBg+H7rPi+HzI/z+\ntverKGxZ7HfIqpUxFWMLgVFAH+Bz4GNgqBDi+HY3jiFtiTHD58PzxRfUfzIT/5w58o1QVSw5OZhS\nUlDdbkQ4jBEMoJXLN7cRc2YmjuHDcY46EOfo0dgGD27XtB9av57ql1+m/pOZKCYTaZdfRtq4TNTP\nr5exT2e9CQUHd8r5d5SqF16k8rHHSL/uWjKmTNmpfYRCFVRWfUNlxVfU1v2OEBo2WzYZGUeTmXEc\nycnxiv6xRmga6/90EigK/T7+CMW84+U5DJ+P4mum4P/f/8j+152knHlmJ4w0TldTW/s7Cxddhlqp\nkfFCAv2fe6dTGjZ3FnEx1sPZ8DO8f6l06R3/sOy3rCg0/PAD5fffT2RjEe4jjiDr1mkdtu53OnoE\n1n0Pc16CNV/JxLER5xHufTqeXxZR/+lnhNeta1rdlJKCOT0NNSkJ1eXC5HKhulyoLnf00RVdJx1z\nZgbm9HRMKSlgGBiBAJGSEgILF5F69lmxD+BXFOUWICCEeEpRlAVCiBHtbhxDWooxw+ejZsYMal5+\nBb2uDmvv3iROnoxr7MHY991368bJUYxAgNDatQQWLiKwaBGBhQuJbJJpvWpCAs4DD8RxwAFY8vIw\nZ2WimExo1dUElyzB+8uvhFasQLHZSD7tVNIuuQTLypfgl8eh12g48w1ZL6qLEUJQeutt1H/8MXmP\nPUri8bummSOReqqqvqOy8iuqa37GMIK4XYPJz7+Y7Ow/oartZ5jGaR/Pl1+y+a83kPfEEyQee8xO\n78cIBtl0/fX4fvyJrDvuIPX83RraGaeTqauby4KFF2KqhLTpZvo99TqOAw7o6mHtEHExtgfgrYAP\nLpc1DkdeAMc/AmYbRjhMzWuvU/XccxCJkPzns0m/4grM6bu5+LgQstRP0e+w4SdY+anMqndlEBl0\nDg3lWdR/8xPBxYsBcI4ahfuII3AMH459331QHbHJoo9JzFiLnf2BLEFxBzBZCLFBUZSlQoj9dn2o\nHWfUqFHifz/+2EqEuSZMIP3qq3CMGLG1pUYIWQjTXy0vgi1Rli1wZbYqqhgpLcU/dy7+/83BP2cO\n4cLCrQ9uNuMcMQLXhAkkn3YqZqsm/xkU/gwHXgTHPRTt49Y9MMJhii6+hOCyZfR5913sg2OT7aLr\nfsrLP6d406t4vSux2/Po1/d6srNP3uvbXuwqhX8+B62qiv5ffrHLwdciHGbTjTfi/eZbMm+dRtpF\nF8VmkHG6FK93FfPmnY1SGyH1fkHvB/9NwqRJXT2sHaa8vJzx48fz66+/khXPAO65GDp8fx/8/Ajk\njZKeoahBIlJeTuX06dR/9DGK1UrqeeeSesklmFNSOmcsQshG6Bt/haLfpAjzyDryhjmRUMI4fN5e\neJeWEFi0GAwD+5AhJJ5wAonHH4clJ6dThhVrMTYEuAr4TQjxH0VR+gJnCiEe3PWhdpwD8vPF/+Xm\nodfX45o4gYwpU3AMH956JSHkhZj/Jqz5WhYR3RJbIuSPgd7jYNCxkDmkVXyX7vWhVZSjlZcjDANz\nSgrW3r1lrR4hYNkH8MU0WUfrxMfhgD938pnvHFplJetPPRXV6aTvf/+LKYYp5EIIamp+Zt36R2lo\nWIrTOYD+/W4kI+PouPtyJwgsWULhGWeSdfttpF5wQUz2KSIRNk+9iYavvybz5ptIu3SnW8lu+xiG\nQXD5CkKrViLCYczZ2ThHjMAUD8qOOYHAZubOOwPDU0/qPTr5N91P8qmndPWw4sSB5Z/Ah1fJothn\nvtEqVCe0YQNV/34Gz2efodjtJJ18EqkXXICtb99dPqzh9RL+7UO0hZ+jr1+AXudBC6nowo2upqEL\nF5rPILypDHTZicM+dCjuww4j8fjjsfXb9TG0R0zFWHdhP4dDfHnZZaRddtnWIgygbCl8fpMUY7ZE\nGHycdB+6M2UxtlCDtJRVLIeNv0HlCrldcm+ZwrrP8ZB/8LZriJQshO/ugbWzZL2Rk56BrCGdd8Ix\nwD93LhsvvAj3pEn0evqpmAslIQSVlV+xbv1j+P3rSEwczoABt5GSPDqmx9nTKf3736n/9DMG/vwT\nJvfO90vdEhGJUDJtGp7PvyDj+r+QdtVVMfkMiHCY2v97j5rXXmty8zdhsZB41JGkXXXVnlN/qIsJ\nh2uYN/8sgp5NpD1gkPvnm0i//PKuHtZOEw6HmT9/PiNHjsTaUzLv4myfihXwzjlQVwzH3AdjLm9l\n5AitWUP1q6/hmTkTEYngHDWKpFNOwX3YJMypqe3uXqupIbhiBaEVKwguXUpw4R+Ey9rIIVRVTElJ\nmJKT5ZSWim3AAOz77INj5EgsmZmxPOt2iVVpi5nIvpBfii16FSiK0g+4CCgUQryya8PtGAcOHy7m\nLVq09QtCwNyXpbXKngyTboUDzm2/yGpDGaz6AlZ9Lv3eehgcqTDgCFlnxJ0pg/5q1ssaK6WLZJud\nw++AMVd0fguhGFH92mtUPPAgmbfcQtolF3fKMQxDo6zsI9ZveJxQqIyMjGMZ0P8WnM7u0X6lO2P4\nfKyZMFEW7b1vu00odgqhaZTecQf1H39CyrnnknX7bbvkBg2uWMHmm28mvHYdjpEjST7zDJwjR6LY\n7USKimiYNYu6/74va+5ddhnp107pcJmVOFuj637mLzifhrolpD6ukD3+QrJuu61HW6DjMWN7KIFa\n+OAK6ZXqfwSc9PRWcdRaVRV1739A/YcfNoUE2fbZB8f++2PJy8WckYHQdERQBsGH1m8gtHo1Wnl5\n0z7MboE9KYi9by62MUdjHnY0pswczKkpqImJ3aobSazEWDZwI3AaUANUAnZkVuU64GkhxMexGHBH\naLO0hRaS1rD5b8DAo+GU52V19x0l1ABrv4WVn8lgP29Z82uqRVrChp4CI86VLXR6EEIINv/lLzR8\n/wO933wD54jOy7vQ9QBFRS9RuPF5hNDJz7+APr2nYLEkdtoxezp1779P6R1/o/eMGa17jMYQYRhU\nPPIoNa+8QsLRR5P7wP3bTHLZ3j5qXn+Dyscew5ScTPbdd+E+9NA2RYFeV0f5ww9T//4H2AYOJPeR\nh7EPHhyr09lrMIwIi5dcSXXVT6S8YCKr12RyH36oW91sdoa4GNuDMQxpHJn1D3nvnDQNRl+2VUy1\nEILg0qX4fp2N7/ffCa1ejV7Tunyp4nBg7dsHW99+2FM17PXfYTMVYR4wBo74J/QZvxtPbOeIuZtS\nUZQ+QA6yYfhqIcQ2Cm10HluJMS0M754n01QnTIXD7oidtSpYD/4a2YjYnSX7GfZgdI+HDaecijAM\n+n34QafH9IRC5axb/zilpf/FYkmmb9/rycs9G1WNTV/OPYmiSy8jvKmY/l9+2enWjurXXqPiwYew\nDRhA3vQnOxy3ESmvoPS2W/HN/g33kUeQc/fdHQrE9f74I6V/+zt6QwPZ//wnyaecvKunsNcghGD5\nipsoK/uIpBkWMo1x5D/3XM8pqLkd4mJsL6B6nTSUrPtO9m+ceAvsf/p2E92MYBCtqhrFYkG1WVET\nElDWfg3f3wtlSyBzKBzxDxh0TI9pobdHxoy1EmO6Bu9fAss/hhMeg9GxD07e0wgsWULhOefiHj+e\nXs8+s1vcHA0Ny1mz5l5q637H6ezPwAG3kZY2qUe7WGKJXl/P6vGHkHbxRWTceAOBQBEOR36nZqZ6\nf/2Vkqk3ISIRMqbeSMoZZ6BY2hbJQgg8M2dSfu99GOEwWbfdSvIZZ+zQ9dOqqtg89Sb8f/xB8pln\nknXH7ai27pN53F1Zv/5JNhROJ+FzGxkbhlDw+uvbbY3Vk4iLsb2Itd/ArH9C+VJwZcgyGPucKL1N\n2/odCdTJEKLZT0HFMinmDrsD9ju9VSWEnsCeLcYMAz66Gha/A0ffC+Ou7eqh9Rhq3nyL8nvv7dT4\nsS0RQlBV9S1r1t5PIFBIWtphDB50Jw5Hr91y/O5M/SefUHLLNLJmPMrqyFP4fKtJTh7DfkOnY7N1\nXp+3SGkpJbfehv+PP7D26UP61VfhPuLIppu9EQ7j++knql98icCiRdiHDSP3gQd2OvtIaBqVT06n\n+sUXsQ8ZQt70J7H2il//bVFW9gnLlt+Aa66dtG/y6Puf/7TZLL6nEg/g38sQQsZd//6sFGfCAHc2\n5AyDjMFyHgHecpkoV/QbGJps0j3hRtjvNDD1TK/KnivG5syBz26Eua9IpXzoLV09rB6FjB+7nobv\nv+/0+LEtMYwwmza9yfoNTyCEQd++f6Eg/5K92nW56brr8C9bTO0DCYRCZeTlnUNR0cukpx/O/vs9\n1anHFkLg/eEHKh59lPDadbJzRa9eKCYTkc2bZamK3BwyrrmGpFNPjUmcUsN331Ey7VZQVXIffKBH\n1sjqbOrq5zF//rlYN6hkvJ5K3zdnYC0o6OphxYkTG/w1sPorWPctVKyEqtWgh+RrZjukD4QBR8Lg\nEyDvwB5nCduSThFjiqJYgP2AzUKIil0Y304xatQoMffeY+C3p2H8X+HIO3uM37g7oXs8bDj1NEQ4\nTJ933+m0YnfbIhgsYfWau6ms/BqXayD7DL6H5OR2P6t7HEYgwOqx44hc35+K3gsYNuwFMtKPYN36\nxyksfJoxoz8lIaHzew0KwyCwcCG+X34hXFiI0A0seXm4DhqDa9y4bbowd5ZwURGbrv8roRUrSDnn\nz2TcOHWPcb/tKoFAMXPmnIKo9JHxhIN+z7+FfUj3Lp+zM8SLvsZpwtAh7JPzVnePF19bEqtsyueA\np4QQyxRFSQJ+A3QgFbhJCPGfWA24I4wamC3mnhuQZSWOeyguxHaB4KpVbDz3PMzZWfR5660uKdJZ\nWfUtq1fdSTBUQm7OmQwYcAsWSydVZ+6GeGbNYtMN11E53UFy2iiGD38RkO2nZv92KGmph7Lffk92\n8Sg7ByMYpOKxx6h98y3MOdnk3HU37kO6d2aUVlWFf8ECtPIKVLsN2777Yt9335hlNmpaA3PmnEaw\nrpD0h630u+8lXAd3bZ/bziIeMxZnb6GjYqy9X5EJQohl0fmLkVmU+wMHArvfP+irhLHXwrEPxoXY\nLmIfPJheTz9NZGMRxVOuxdhWF/pOJCP9CA4++Ct6F1xBadkH/Pb7UVRWfrPbx9FVNMyaRWiCHQ0P\n+fkXNS23WJLIzDiOquofMIzItnfQg1HtdrJvv53eb7+NarNTfNlllNx2O3p9fVcPrRVCCHy//07R\npZex5pAJbL7uL5Tfcw+lf/s7haedzrrjjqPu/fcRhrFLxzGMCIsXX4Pft56UZ1V63/zwHivE4sSJ\nszXtibFwi/mjgI8AhBBlba/eySTlwdH37HFmzK7CdfBB5D74AIEFC9h40cVo1dW7fQwmk5MBA6Yx\nZvQn2O15LF5yJRuLXtzt49jdCE3D++NPBI6y4nINJCVlXKvX09Inoete6uvnd9EIdw/OkSPo+9GH\npF15JfWffMK6E06k7oMPd1ncCMNA9/owwuH2V25re13H8+VXFJ5xJkUXXUxw1SrSp0yhz7vvMPCX\nn+k/62ty7rsPU0IipXf8jaJLLiVSsXORG0IIVq78O7V1s0l+S6Xg7H+QeNxxO7WvOHHi9Ey20fen\niTpFUU4ENgPjgUsBFEUxA7Fpab4juDLjFrEYk3j88Sg2G5tvnMqGk08h5957cE+cuNvH4XYP5sCR\n77J8xc2sXfsAhh6kb9/rdvs4dheBhQsJm+oIJEfon3XJVqUiUlPGoSgWqqt/ICXloC4a5e5BtdnI\nvOGvJB5zNGX/uovS22+n9p13yL7j9rbbnm0DraoKz+df4PnsM4IrViCiQsyckYFjxAic0Rg4a58+\n2yzNoXs81H/0MbVvv01440YsvQvI/te/SDr5pKZyHLoepEFZQXisIGHiFNzfFVJ9/3Q2nHYa+c88\ng2P//Xfo/AsLn6G07D3cn6v0HnsTqeecs0Pb90ScTidTpkzBuYOFh+PE2VNpL2ZsEDAdWez1cSHE\na9HlxwBHCyGm7o5BNtJmBf44MSG4ahWbb7iR8Pr1OA48kKTJJ+I44ADM6emYEhN3W6FJIXSWr5hG\nWdmHDB3yONnZf9otx93dVDzyCIXFL9FwUoRxY7/H4dg6W27+/HOJaHUcNOazLhhh1yAMA8/MmVQ8\n8ihaZSUJxx1L2iWXYN9vvzYFlOHz0fDtt9TP/BTf7Nmg69j22QfXuHGY09IQ4RDhwkJ8c+aglZQC\nYMnPxzV2LPahQzFnZACCyKbN+BfMx/vDj4hAAPvwYaRdfAkJRx3Z1DpK07wUbnyOTZveQNd9TWNQ\nVSvp9klYHlgBGz3kPf5Yh7NES0s/YvmKqTj+UOlvvYqsqbv1JzVOnDidzJ5b2iIuxjoNIxym7p13\nqH17BuGNG1u9pthsqE4nqtOJKTkZ2+DB2IcMwTl6NLZBA2NaxNUwwixYcAGehiUcNOZTnM6dq23V\nnVk/eTIl5xdh69uP0aM/bHudDdPZsGE6h05ciNkcu+bhPQHd66P6pRepfeNNDL8fS+8CEo48EtuA\ngSgWC1p5Gf4FC/D9OhsRCGDJzSXxxBNJmnwitoEDt9qfEIJIcTHeX37B99PP+OfOxfB6W61jzszE\nfehEks86G8d+Q1u95vWuYvGSawgECsnMPIHcnNNxOHoTCpVRXvE5paX/B0Il5Yd0rO9VkvPPO0k5\n68ztnmNl5XcsWXQllrWCgf4rybrhpr2mGHJDQwOPPvooU6dOJSEhoauHEydOpxEzMaYoynHArUDj\nr9My4EEhxOe7PModJC7Gdg9CCMKFhYRWrkSrrUWvq8Pw+TD8foTfj1ZZRXDlyqY+YqaMdNzjxuEa\nPx7X2LFRa8OuEQyV8ccfx+J27cPIkTNQlD0nTjC8aTOrTzuC8vsj9O93E336XN3melVV37No8WWM\nHDFjj3dVbgu9vp6GWbPwfP4Fvj/+AF1ves2Sl4drwiEkTZ6MY8SIHcpqFIaBVlqKVlMDioolKxNT\nenqbYsjTsJQFCy5EVa3sN/SJNq9FIFDMylV/p6bmZ5wl6SQ8WU/mWZeR8de/opi3jgYpWT+DFev/\ngWUTDPBeTs6UW/YaIQbxbMo4ew8dFWPbjRlTFOVy4Epk5mSjChoFPKAoSi8hxAu7PNI43Q5FUbD1\n7bvdvoVCCLTSUny//4Hv11/x/vQz9R9/AoBt8GApzMaNwzniAFTXjteQstuyGTjgDlasvJWyso/I\nyTl1p8+nu+H98QdCQ2SAelr6YdtcLzFxGAAez6K9VoyZkpJIPv10kk8/HSMcRistRWiadJ8nJe30\nfhVVxZKXhyUvb7vr1dcvZOGiizCbExk54m0cjvw213M48jlg+Cts2vQma5UHCN2tEn71Rbzn/I/s\nadNwjByJoijoQR+rv5tGif0LrOtU9k2+g7QLL9yrhFicOHG2pr2YseXAIUKImi2WpwG/CCE6vyJl\nC+KWse6LMAyCK1bgmz0b36+zCcybh4jIsgyW/HxsgwdhHzQI26DB2AYPwlpQ0BSLs819CoO5c08j\nFCpn7NhvMJn2jGDfoiuuYPOI39GHJTB+3C/bvRH/OvtQEhOHdXo1/jhbU1c3l4WLLsVqSWXEiLdw\nOLYv3BrxelezbPmNeL0rsBZZcMw2sBjJGAU2PAPL0LIMXKuTGTbhZZxDD+jks+iexC1jcfYWYmIZ\nQ4q1mi0XCiGq4//k4rREUVUcQ4fiGDqU9Msvx/D78c+dS3DZMoKrVhNatQrvd9/LvqKAYrdj6ZWH\nOS0dU2oKqsvVFJOmOl1YsrNwHnQwAwfewbz5Z7Fp05v07n1lF5/lrmMEAvjm/EHwjDDZaYe2axFJ\nTByGx7NoN40uTiO1tb+zaPHl2GxZjBjxFnZbdoe3dbsHMXrUh5SUvEuR/RXqCzYCsmyMvSGNvqZz\nyL/iLzErFtsTURSFxMTEuEUwTpwo7Ykxj6Iow4UQre4GiqIMBxo6b1hxejqq04l74sRWZTKMYJDQ\n2nWEVksI6n5JAAAgAElEQVRxFikpQauuJrRyFYbf3zQ1xQWpKglHHUXynw+kuPh18vMvRlV7dlNh\n32+/EcoPYpg00tMmtbt+YuIwKio+JxyuwWpN7fwBxqG6+kcWL7kGhyOfEQe8uVMN21XVQq9e55GX\ndy7B4GbC4Sps9mxs1qy4AAFycnKo72YFfuPE6UraE2NTgU8URXkVmBddNgq4EDivMwcWZ89Dtdtx\n7Dd0q0y1lgghEKEQ4Q0b8Hz+ObVvz8BUFiZ0pZ+y8k/IzTl9N4449jR8+y2hEWYURdmq0GtbuJwD\nAPD718fF2G6grOwTlq+4GZdrECMOeBWrNX2X9qcoCg5HLxyOXjEa4Z6BpmkUFhbSp08fzG0kOMSJ\ns7exXTu5EOIX4KDoehdFJxU4OPpanDgxRVEUVLsd+777kjl1Kv0++5Qk0/6YSxWKljzT1cPbJYSu\n4/3+B8IjLSQnj+5QuQqXqz8gxViczkMIncLCZ1i2/AaSkg7kwJEzdlmIxdk2FRUVDBw4kIqd7FoQ\nJ86eRrt/SaKtj/6hKEpG9Hllp48qTpwolpwcer/8MvVPT6Y6ZwNVsz8gfVzPzKwMLFpEiGrCCRF6\nd8BFCWC356GqVnz+dZ07uL0UIQS1tb+xbv0jeDyLyMw8gSH7PozJZOvqocWJE2cvor3SFgrwT2AK\nYIou04GnhBB3df7w4sQB1eFg8KUvMXv+Eaz/+k6SBhyCJTOzq4e1wzR8+y2hYTJeKK2DYkxRTDgc\nffD74paxnUEInbq6OTQ0LEfTPBhGCN0IYRhBIpF6GjyLCYZKsNmyGTrk8f9v786jI63q/I+/b62p\nJJXKvvXeNFs3dDfQLIrDzugwgB4XYEYdQQWVzW1E0PnNyJxxA0ZE8AiyKr8ZQBlZlQFptp8iIFtv\n0M3aNN1JupNKKqnKUuv9/ZHqpoF01qp6qiqf1zk5JlXPc59PnoOdb917n3tpaTlFc7pEpOAm6hn7\nOqN7Uh5mrX0TwBizGPiFMebr1tor8x1QBCBQv5D6qiOIrHyabRdfxIIbby65P5qx1Y+Q+scgFRWV\nVFYunvR5VZV7EY29nMdk5cdaS1fXXbzxxpWMxDt2ve5y+TDGh9tdgdtdTbBmOYsbv0Fz80dwuwu/\n3a6ICExcjH0WONFa27PzBWvtG8aYzwAPASrGpGDmLvkcvcNP0TfwF+ruu4/QqaWzb2X8jTeIb32T\noTkwp/GkKRWSlZWL6O55iEwmjsul4bOJpNMjvPTyRezY8XtqalawZO9LqKs9HK+3vuQK+HJVX1/P\nvffeS329HkoRgYmLMe/uhdhO1tpuY4w3T5lExtTQcCxebz0jH8mw/bLLqT7mGNw1NU7HmpTo6tXE\n97ZYk5r0EOVOlVV7YW2aoeEtVFe9f99FeUcmk2TN2i/S1/cUey3+FgsWnI0x4y8uLIVXUVHBKaec\n4nQMkaIx0aqDiWm+J5JzLpeX1pZTGV4cIzkYpvuqnzkdadKiDz5E6ug6XC4/dbVHTOncysBCAIaH\nt+QhWXl57fXL6Ov7C/vv/yMWLvyyCrEi1d3dzRFHHEF3t54HE4GJi7EVxpiBMb6iwIGFCCiyu6am\nD2NJ4fniB+i77TZGNm50OtKEEps3M7J+PSP7p6ir+wBud8WUzq/IrlE1Mvx2PuKVja7t9/H22zcx\nd+4/lfx6dOUumUzy9NNPk8xumSYy2020zpjbWlszxlfQWqthSim4UOhgvN56EkcGcAWD7Liy+Kct\nDjzwAKlmS8LbN+UhSgCftwGXK8DwyLbch8uhZHKASP9zDA8XPmcstomXX76EUOgQ9l5yScGvLyIy\nE7N3czQpSS6Xh8bG4+gd+BP1XzyTwcefYOi55yY+0SHWWvrvuZf0R0Y3mZ7MFkjvtXMV92LuGevo\nuJM/P3kkzz13Gk/+5SjWrD2HeLwwQ1CpVJS1687F46nmwAOuKfkts0Rk9lExJiWnqfFEUqko5uR9\n8DQ1seM/f4K11ulYYxp66ikSmzeTWOWjsnIJgcC8abVTUTGnaHvGurru4eWN3yYYPJAVy69n0aKv\n0tv7J57566kMDb2Z12tbm2HDS//MyMhWDjjgavz+0lt/bjaqqKjgtNNOo6JiakP2IuVKxZiUnPr6\nD+FyBQgPPEHjeecy/PzzxB5/3OlYY+r779swLSFi3jdobDxm2u0EKuYxMlJ8PWPDw9vYuOlfCYUO\n4aCVv6ax8TgWL7qQVat+h7UpXnjxLOLx/G15s/mtX9DT8zBLllxMXe2hebuO5FZ9fT133HGHlrYQ\nyVIxJiXH7a6gof5DdPf8kdDHP453/ny6f3oVNpNxOtq7JN5+m+gjj+D+7CFYO/UlLXZXEZhLKhUl\nmezPXcAc2PzWz7E2wbKlP8HlemelnGD1fqxccSPJZJgX13yeVCqa82uHw0/wxhtX0tJyKvPmnpnz\n9iV/BgcHueaaaxgcHHQ6ikhRUDEmJamp6UTi8S5iIxtpuuAC4hs3MvCHB5yO9S7h62/AuN0kDvHj\ndldTG1o17bYCFdknKke25irejI3Eu+js/B1tbZ8ikH3ic3c1Ncs58MBfMDj4GuvWnUcmk7sn56LR\nDaxbfwHVVfuw/37f12KuJaa/v58LLriA/v7i+nAh4hQVY1KSGhqOBqC390/U/P1J+Pfdl+6rf4ZN\npRxONiqxdRv9d91FzSc+Rjj2JxoajsLlmv4DyDuXtxgeLp5ibOvWW4EMC+afvcdjGuo/xH77/Qe9\nfX/mlVcuzcncvmj0ZV548Uw8niArVtyI21054zZFRJykYkxKks/XSHX1/vT2/hnjctH01a+SfGsL\n/ffc43Q0ALZ///vg9eL+x4NJJsO0tJw8o/be6Rkrjkn81qbp6ryLhoZjJnwoob3tkyxY8GW2ddzG\nli3Xz+i6O7of5PkX/hGXy8fBB91KRUXbjNoTESkGjhRjxpjLjTEbjTFrjTF3GWNqncghpa2+/kgi\n/c+TTg9TfewxVCxfTvfPf04m4ezmEP333EPs0UdpOu88wsk/43ZX01B/zIza9HhCuFwBRuKduQk5\nQ729fyKe2E5b6ycmdfxei79Jc/NJvPb6j3nzzaun3EMWi21i7bpzWbfuXAKB+Rxy8B1UVi6aTnQR\nkaLjVM/YH4EDrLXLgVcArdIoU1ZfdyTWJohE/ooxhqYLLyTV0UnkzjsdyzT07LN0/sv/ofKwwwh9\n+pPs6H6Q5qYP43bPbINvYwwVFW3ER4qjGOvsuguvt47GxmMndbwxLpYt/QmtrR/jjTd/yrr15zES\n79rj8clkPz3hx3j99f/kmb9+jKefOYlw+AkWL/oaqw757Zhz1KR0tLW1kUgkaGtTz6YITLxReF5Y\nax/a7cenAO1dIlNWW3soxvjo7fszDQ1HUXXkBwmsOoTwtddR+/GP4yrwGkYDDz1Ex0XfxjtnDnOu\n+indfatJp2O0tU2u92giFf52RuIdOWlrJjKZJOHwYzQ3/d2UFlh1ubws3f8Kqqv34/XX/5Nw+DEa\nGo4mWL0Mt6eKZCLM0NBmYoOvMjT0GgDGuAkGl7NkycW0t30Sr7cuX7+WFJC1lqGhIYLBoB6+EMGh\nYuw9Pg/c4XQIKT1ud4Da0MH09v4ZYFfv2JZ/+hx9t99Ow5lnFiTHyMaN9PziWqIPPkjFAQcw77pr\n8dTV0fH8bwgEFlBbe1hOruOvaCMWfiUnbc1Ef//zpFJRGqaxbpoxhgXzz6a56cO8teVGwuFH6e5+\nKPueh0BgHpWVi2ltPZVQ6GBqgsvxeKpy/BuI07q6upgzZw7btm2jvb3d6TgijstbMWaMeRhoHeOt\n71pr78ke810gBfzXOO2cA5wDMH/+/DwklVJWX/8hXn/jChKJHny+RqoOO4yqD36A8PU3UPepT+Gq\nys8fcmstw889R8/11zP4+BO4KitpvOB8Gs8+G+PzMTj4OpHIM+y1+J9z9sm/wt9GItFNJpNwdMuf\nnvCjGOOlvu7IabcRCMxnv30vBS4lnR4hk0ngdgdm9MSpiEipytucMWvtCdbaA8b42lmInQmcDHza\njjOb11r7S2vtKmvtqqampnzFlRJVXz9aEPT2PrnrtaYLLyQdDtP7X/+dl2vG33yTt88+h7c+81lG\n1q2n6WtfY8mjj9B03nkY32iRtGXLDbhcftrbT8vZdUefHLTE49tz1uZ0hMOPU1t7KB5PdU7ac7sr\n8HprVIiJyKzl1NOUHwEuAk611g45kUHKQzC4DI8nRG/fO8VYYOVKqo8+mvCNN5KO5nbl98hdd/Pm\nxz/B8Jo1NF/8bZY8sprGL38Jdyi065h4vJvOrrtpa/sEPl9Dzq7trxgdzhlxcBJ/IhFmcPCVGfWK\niYjIuzn1NOU1QBD4ozHmRWPMtQ7lkBJnjJu62sOIRJ5+1+uNF15Apr+f3l/9OifXsday44or6Lzk\nEgIrVrD4/vtpOPPMMR8SeHPz1UCa+fO+kJNr71ThH33yLO7g8haR/mcBqK3TPpAyfbW1tdx0003U\n1mpVIxFwqBiz1i6x1s6z1q7Mfn3ZiRxSHmrrDmd4eAsjI+88aRhYtozgiSfSe8stpCORGV+j+6qr\nCN9wI7VnnM78G67H29I85nGx2Cts23Ybc+Z8msrKhTO+7u52LnC6++9ZaJHIs7hcfmqCBzqWQUpf\nZWUlZ511FpWV2j1BBLQCv5SButojAOiLPPOu1xsvOJ/M4CDhG26YUfvhW24hfO11hD75CVr/7d8w\nnrGfe7E2w6ZX/g2PJ8jiRRfO6Jpjcbsr8XhqHV34NRJ5hpqalY4+QCClLxwOc8oppxAOh52OIlIU\nVIxJyauu3hePJ0Sk791DlRX77EPo1FPp/dWvSWzePK22Bx54gB0/+jHBE0+k7XvfG/fJyI6O3xCJ\nPMPeSy7O23pYfn8zifiOvLQ9kVQqSjT6ErW1GqKUmYnH49x///3E43Gno4gUBRVjUvKMcVFXexh9\nkafe917TN7+B8fno+sEPprwFz/CGDXRc8h0CBx1E+xWX77FHDGBw8A1efe371NUeQVvbp6b8O0yW\n39dEPNGdt/bH09//ApChLkfrpomIyCgVY1IWxpo3BuBtbqbx/PMZfOL/EXv0sUm3l+rpYet55+Ou\nq2Pu1T/D5d/zdkbpdJz1G76Ky+Vn6dIr8rqiuM/f5FjPWCTyDMZ4CIUOcuT6IiLlSsWYlIW62sOB\n988bA6j/zKfxLdmL7T/4AenY4IRtZYaGePu880hHIsz7+TV4GhvHPf61139ILPYSS/e/bNck+3zx\n+5qJJ3qm3MuXC5HIswSDy3C7NelaZsbn83Hcccfh82nuoQioGJMyUV2935jzxgCM10vb975HsqOD\nrn+/dNxCxqZSbPvGNxlZt545V1xOxdKl4163J/wYW7feyrx5Z9HYeNyMf4+J+HxNWJsglerP+7V2\nl07H6R9Yo/likhONjY2sXr2axgk+6IjMFirGpCwY46K29lD6+t4/bwygctUqGs89l4F776Pv1lvH\nPMZmMnRdeimxxx6j9V//D8ETThj3mslkPxtf/g5VVXuz1+Jvzfh3mAyff3QXiniBhyoHomuxNkFt\nSMWYzNzw8DB33HEHw8PDTkcRKQoqxqRs1NUezvDIlj1uF9T4lS8TPPEEtv/wR/Tdfvu73kvHBun4\n1kVEfnsnDV/+EnVnnDHh9V559d9JJHtYuv/luN17nlOWS37f6PpmiQJP4o8OrAWgJrSyoNeV8tTX\n18cZZ5xBX1+f01FEikLeNgoXKbTa2lUARCJ/paXl5Pe9b9xu2i+/nK0XXEjX9y4l+tAfqT7uONK9\nvUTuvJPUjh00feMbNJz9xQmv1dPzCF1dd7No4QXU1BRuAVSfL9szVuBibGBgLX5/G36fhpVERHJN\nxZiUjerqpbjdVUQiz45ZjAG4KiqYd+0v6L3lFnp/fSuDTz4JLheVq1Yx92dXEVg5cc9PJpPk1dd+\nQGXlXixceG6uf41x+bPDlIV+onIguo6amuUFvaaIyGyhYkzKhsvlIVRzEJH+v457nHG7afjCF6g/\n80zSfX0Yr/ddG31PpKPztwwNvcnyA68r+Er0bnc1LleARKKnYNdMJvsZHn6L9rbTCnZNEZHZRHPG\npKzU1q4iFttEMjnx04bG7cbT2DilQiyTSfHWW7+kpuYgGhuPn0nUaTHG4Pc3EU8UrmcsGl0PUNDh\nWClvLS0tbNu2jZaWFqejiBQFFWNSVkaXXrD09z+Xl/a7ex5iZORtFiw4O6+Lu47H52sq6NOUAwPr\nAAgGDyjYNaW8ud1u2tvbcbvdTkcRKQoqxqSs1NSsxBgvkcj4Q5XTYa1ly1vXEwgsoKlx/GUv8snv\nay7oMGU0toFAxXy83sn3IIqMp6OjA6/XS0dHx8QHi8wCKsakrLjdFdQED8hLMRbpf5aB6Frmz/sC\nxjj3id7nbyJR0GHKDVQHx1/8VmSqUqmU0xFEioaKMSk7tbWHMhBdTzo9ktN2t2y5Aa+3jra2j+e0\n3any+5pIpaI5//3GkkpFGR5+i5rgsrxfS0RktlIxJmWntvZQrE0yMPBiztociXfR07OaOe3/gNsd\nyFm70+Er4MKv0ehLAARVjImI5I2KMSk7odAhgMnpUOX2rnsB63ivGLyz1lghnqiMxkaLsWoVY5JD\nNTU1/PjHP6ampsbpKCJFQeuMSdnxekNUV+1DJPJsztrs2n4PNTUHUVm5KGdtTtfOVfgT8UL0jK3H\n72vRyvuSU9XV1Vx00UVOxxApGuoZk7IUqj2U/oEXyGRmPkk4GttILLaR1taP5iDZzPn8o8OUBekZ\ni27QEKXkXF9fH2eddZb2phTJUjEmZam2dhXp9CCx7DDbTHR13Y0xHlqaT8pBspnzeesxxp33nrF0\nepjBwddVjEnODQ8Pc8sttzA8POx0FJGioGJMytLo4q/MeKjS2jTbt99HQ/1R+HwNuYg2Y8a48Hkb\n875ZeCy2CcgQ1LIWIiJ5pWJMylKFv5WKinkT7lM5kb7IM8TjXbS0npqjZLnh8zfmfa2xaHQDoJX3\nRUTyTcWYlK3a2lVEIs9irZ12Gzu2/x6XK+Doivtj8fmaScTzuwp/NLoer7cOv78tr9eR2cfj8XDg\ngQfi8egZMhFQMSZlrLb2UJLJXoaG3pjW+ZlMih3dD9LYeJzja4u9l9+X/83Co7GXCFYvdWwPTilf\nzc3NrF27lubmZqejiBQFFWNStmpDO+eNTW+oMhJ5mmSyl5bmv89lrJwY3RIpjLXpvLSfyaQYHHyF\n6ur98tK+zG7xeJzVq1cTj8edjiJSFFSMSdmqrFyE19tAJPLMtM7fvuMPuN2VNDQcneNkM+f3NQMZ\nEonevLQ/PLyZTCZBdfX+eWlfZrdwOMwJJ5xAOBx2OopIUVAxJmXLGEN9/ZGEe/+EtZkpnZvJpOju\nfpDGxuNxuyvylHD6fNlV+PM1iT8W2wignjERkQJQMSZlraH+KJLJ8K4nAyerL/IUyWRf0awt9l7+\n7Cr88Xh+irFobCPGeKiqWpyX9kVE5B0qxqSs1Tf8DQDh3iemdN6O7b/H7a6ivv6ofMSasXc2C8/P\nE5Wx2EaqKvfC5fLnpX0REXmHijEpa35fI8HgAYTDj0/6nHQ6zo7uB2hqPLEohyjhnf0p8/VEZSy2\nUUOUkjdNTU2sWbOGpqYmp6OIFAUVY1L2GuqPor//BZLJ/kkdHw4/SioVpbX1Y3lONn1utx+PpyYv\nWyIlkxHi8U4VY5I3Xq+X5cuX4/V6nY4iUhRUjEnZG30aMkNv35OTOr6r6258vibq6z+Y32Az5PM1\n52VLpNFtkDR5X/Knq6uL9vZ2urq6nI4iUhRUjEnZq6lZiccTnNRQZTIZoSf8GK0tp2KMuwDpps/v\nb8rL05R6klLyLZPJ0NnZSSYztaecRcqVijEpey6Xh/q6D9EbfmLCrZG27/gD1iZpbf1ogdJNn9/X\nTDwPw5Sx2Ea83vpd89JERCS/VIzJrNDYeDzxxHb6+58b97iurnuoqtqb6uqlBUo2fT7f6GbhM9l7\ncyw7J+9rGyQRkcJQMSazQlPT3+J2V9LZ+T97PGZw8HX6+5+lteVjJVGI+PzNZDJx0ulYztq0Nk1M\n2yBJnlVVVfGtb32Lqqoqp6OIFAUVYzIreDxVNDV9mO07/kA6PTLmMW9v/TUul4/29k8VON30+LNr\njeVy4dehobfIZEYIqhiTPAqFQlx22WWEQiGno4gUBRVjMmu0t32SdDpGV9fd73svmeynq+t3tDSf\ngs/X4EC6qdu5JVIu1xqLDWryvuRff38/F110Ef39k1tuRqTcqRiTWaO29nCCwQN5a8t1ZDKpd733\n5uarSaeHmTf/8w6lm7qdPWOJHPaMxWIbMcZNZeWSnLUp8l6Dg4NcfvnlDA4OOh1FpCioGJNZwxjD\nwoVfYXh4C52dv931eiy2ia1bb6W9/fSSGp7z+7PDlLnsGYttpLJyMW63tkESESkUj9MBRAqpqfFE\n6mqP4JVX/4PKqiVU+FtYs/ZsPJ4Qey3+htPxpsTtrsblCuR0Ff5YbCOh0ME5a09ERCamnjGZVYxx\nseyAq/B6a3n++TN48i/Hkkz2sXLFDSUzV2wnYwx+f1POesaSyQFGRrZRXVU6vYNSmlwuF21tbbhc\n+hMkAuoZk1nI72vkiMP/l47OO0mnh2ht+SiBwFynY02L39eSs6cpY4PZbZCCKsYkv1pbW+no6HA6\nhkjRcPRjiTHmm8YYa4xpdDKHzD4eT5D5885i0cLzSrYQg9EnKnO1JZK2QZJCSSaTrF27lmQy6XQU\nkaLgWDFmjJkH/C2wxakMIqUul1sixWIv4/HU4ve15KQ9kT3p7u5mxYoVdHfnfjsvkVLkZM/YlcBF\nQG73chGZRXz+ZtLpGKnUzJcIiMU2EdQ2SCIiBedIMWaM+SiwzVq7xonri5SLXWuNzXCo0toMsdgm\nDVGKiDggbxP4jTEPA61jvPVd4DuMDlFOpp1zgHMA5s+fn7N8IuVg11pj8W4qKxdNu53h4bfIZIZV\njImIOCBvxZi19oSxXjfGHAgsAtZkh0PmAs8bYw6z1naN0c4vgV8CrFq1SkOaIrvx+Ua3RJppz1gs\nln2SUsWYFEBDQwMPP/wwDQ2ltZyMSL4UfGkLa+06oHnnz8aYzcAqa21PobOIlDq/f3Sy/UyXtxh9\nktJFVdXeOUglMj6/38/xxx/vdAyRoqEV90RKmMdTg8vlm/HCr9HYy1RWLsLtrshRMpE927FjB8uX\nL2fHjtxt5SVSyhxf9NVau9DpDCKlyhiDz9c84y2RYrFN1NQsz1EqkfGlUinWrVtHKpVyOopIUVDP\nmEiJ8/ubiSe2T/v8VCrKyMjbJbVJuohIOVExJlLifDNc+PWdyfv75yqSiIhMgYoxkRLnn+GWSO8U\nY/vmKpLIuAKBAGeeeSaBQMDpKCJFwfE5YyIyM35fM6nUAOn0yLQm4McGN+Lx1OD3t+Uhncj71dXV\ncfPNNzsdQ6RoqGdMpMT5/DNbhT8WfZnq6v21DZIUTCwW47LLLiMWizkdRaQoqBgTKXE7t0Sazlpj\n1maIDb6iIUopqIGBAb797W8zMDDgdBSRoqBiTKTE7ewZiyemPol/ZGQr6fSgVt4XEXGQijGRErdr\ns/D41Je3iMZeBiCoJylFRByjYkykxHm9dbhcfkbinVM+d/RJSqNtkKTgPB49Pyayk/7fIFLijDFU\nVLQzMtIx5XNjsY1UVi7E7dYSA1I47e3tJJNJp2OIFA31jImUgQr/HEZGtk35vFjsZaqrNF9MCiud\nTtPR0UE6nXY6ikhRUDEmUgZGe8amVowlkwMMD28hGFyWp1QiY9u+fTtz5sxh+/bpb+MlUk5UjImU\ngYqKOSQSPaTTI5M+JxZ7CUDFmIiIw1SMiZSBioo5AFOaNxaNbgAgGFyal0wiIjI5KsZEykBFYC7A\nlIYqo9EN+P2t+HyN+YolIiKToGJMpAwEdvWMTb4YG4huIBg8IF+RRPaorq6O22+/nbq6OqejiBQF\nFWMiZcDna8YY96SLsXR6iKGh1wlWa4hSCi8QCHD66acTCGhJFRFQMSZSFlwuD35/66TnjMViGwGr\nyfviiJ6eHo4//nh6enqcjiJSFFSMiZSJQMU8hoffmtSxA7sm76sYk8JLJBI88sgjJBIJp6OIFAUV\nYyJlIlC5kKFJFmPR6Aa83nr8/tY8pxIRkYmoGBMpE5WBBSSTvSSTAxMeG41uIBhchjGmAMlERGQ8\nKsZEykSgcgEAw8Obxz0uk4kzOPiKnqQUx/j9fk4++WT8fr/TUUSKgooxkTJRGVgIMOFQZSy2CWtT\nWuxVHNPQ0MB9991HQ0OD01FEioKKMZEyEQjMB2B4aPO4xw0MrAWgJrgi35FExjQ0NMTNN9/M0NCQ\n01FEioKKMZEy4XYH8PtbJ+wZ6x94AZ+viYqK9gIlE3m3SCTC5z//eSKRiNNRRIqCijGRMhIILJiw\nZ6y//0VCNSs1eV9EpEioGBMpI1VVexMbfBVrM2O+n0z2MTy8mZrQQQVOJiIie6JiTKSM1ASXkU7H\n9rj4a3//iwCEalYWMpaIiIxDxZhIGdm5XMVAdP2Y70ciz2CMl5qaAwsZS+RdWltbiUQitLZq0WER\nUDEmUlaqqpZgjI9odruj9+rt/TOh0MG43ZUFTibyDmMMlZWVmrcokqViTKSMuFw+qqv3JTpGz1gi\n0Us0toH6ug86kEzkHZ2dnfh8Pjo7O52OIlIUVIyJlJlgcBnR6Aaste96va/vSQDq6z/kRCwREdkD\nFWMiZSYUOohUaoBYbOO7Xg+Hn8DjCWobJBGRIqNiTKTMNNQfBUA4/Oiu1zKZON09D9HYeAIul8ep\naCIiMgYVYyJlxu9vJhhcRk/4sV2vhcNPkEpFaW05xblgIlmhUIirr76aUCjkdBSRoqBiTKQMNTQc\nS3//CyQSvQB0dt2F11tPnSbvSxGoqqri/PPPp6qqyukoIkVBxZhIGWppPgmAN9/8GX19T9Pd/SBz\n2ppqggYAAAgxSURBVM/A5fI6nEwEent7Of300+nt7XU6ikhRUDEmUoaqq/dl7tzPsnXb/2XN2nOo\nqJjLwoXnOh1LBICRkRF+85vfMDIy4nQUkaKgmbwiZWqvxd8glYxgXF7mzf0cbnfA6UgiIjIGFWMi\nZcrjqWbZsp84HUNERCagYUoRESkor9fL4YcfjterOYwioJ4xEREpsKamJp566imnY4gUDfWMiYhI\nQY2MjHDfffdpAr9IlooxEREpqN7eXk499VQtbSGS5VgxZoy5wBiz0RizwRhzmVM5RERERJzkyJwx\nY8yxwEeBFdbauDGm2YkcIiIiIk5zqmfsK8CPrLVxAGvtDodyiIiIiDjKqWJsH+BvjDFPG2MeN8Yc\n6lAOEREpsObmZl599VWamzUoIgJ5HKY0xjwMtI7x1nez160HjgAOBX5jjFlsrbVjtHMOcA7A/Pnz\n8xVXREQKxOPxsGTJEqdjiBSNvPWMWWtPsNYeMMbXPcBW4Hd21DNABmjcQzu/tNaustauampqyldc\nEREpkM7OTkKhEJ2dnU5HESkKTg1T3g0cC2CM2QfwAT0OZRERkQKy1jIwMMAYgyEis5JTK/DfBNxk\njFkPJIDPjTVEKSIiIlLuHCnGrLUJ4DNOXFtERESkmJhS6pAyxkSBTU7nmGUa0RByoemeF57ueeHp\nnhee7nnh7WutDU50UKltFL7JWrvK6RCziTHmWd3zwtI9Lzzd88LTPS883fPCM8Y8O5njtDeliIiI\niINUjImIiIg4qNSKsV86HWAW0j0vPN3zwtM9Lzzd88LTPS+8Sd3zkprALyIiIlJuSq1nTERERKSs\nlFwxZoxZaYx5yhjzojHmWWPMYU5nmg2MMRcYYzYaYzYYYy5zOs9sYYz5pjHGGmPG3C5McscYc3n2\nv/G1xpi7jDG1TmcqR8aYjxhjNhljXjPGXOx0ntnAGDPPGPOoMeal7L/hX3U602xgjHEbY14wxtw/\n0bElV4wBlwGXWmtXAv+a/VnyyBhzLPBRYIW1dhlwhcORZgVjzDzgb4EtTmeZJf4IHGCtXQ68Alzi\ncJ6yY4xxAz8H/g5YCvyDMWaps6lmhRTwTWvtUuAI4Dzd94L4KvDyZA4sxWLMAjXZ70NAh4NZZouv\nAD+y1sYBrLU7HM4zW1wJXMTof/OSZ9bah6y1qeyPTwFzncxTpg4DXrPWvpHdieV2Rj/oSR5Zazut\ntc9nv48yWiDMcTZVeTPGzAX+HrhhMseXYjH2NeByY8zbjPbQ6NNr/u0D/I0x5mljzOPGmEOdDlTu\njDEfBbZZa9c4nWWW+jzwgNMhytAc4O3dft6KioKCMsYsBA4CnnY2Sdn7KaMfpjOTObgoV+A3xjwM\ntI7x1neB44GvW2v/xxhzGnAjcEIh85WjCe65B6hntHv7UOA3xpjF2tx9Zia4599hdIhScmi8e26t\nvSd7zHcZHdb5r0JmE8k3Y0w18D/A16y1A07nKVfGmJOBHdba54wxx0zqnFL7e2qM6QdqrbXWGGOA\nfmttzUTnyfQZY/4X+LG19tHsz68DR1hru51NVp6MMQcCq4Gh7EtzGR2OP8xa2+VYsFnAGHMm8CXg\neGvt0ASHyxQZYz4AfM9a++Hsz5cAWGt/6GiwWcAY4wXuBx601v7E6TzlzBjzQ+CzjH6oq2B0atXv\nrLWf2dM5pThM2QEcnf3+OOBVB7PMFncDxwIYY/YBfGiz2byx1q6z1jZbaxdaaxcyOpRzsAqx/DLG\nfITRYYVTVYjlzV+BvY0xi4wxPuAM4F6HM5W9bMfFjcDLKsTyz1p7ibV2bvbf7zOAR8YrxKBIhykn\ncDZwlTHGA4wA5zicZza4CbjJGLMeSACf0xCllKFrAD/wx9G/XTxlrf2ys5HKi7U2ZYw5H3gQcAM3\nWWs3OBxrNjiS0Z6adcaYF7Ovfcda+wcHM8luSm6YUkRERKSclOIwpYiIiEjZUDEmIiIi4iAVYyIi\nIiIOUjEmIiIi4iAVYyIiIiIOUjEmIkXPGJM2xry429fFUzx/szFm3W7n/yz7+n7Zn18wxuz1nnOM\nMeYRY8weF5U2xtxsjPnSe177mDHmAWOMzxjzRHYZHhGRPdI/EiJSCoattStn2Max1tr3Llb8MeBO\na+1/jHH8ScCaCbaNuY3R/XGv2+21M4DbrLUJY8xq4HS0tZKIjEM9YyIyKxljTgK+BnzFGPPoGId8\nGrhnt+M/Y4x5JtuTdp0xxs3otlX7GWPassdUMbpX7t3Z0+7OtiMiskcqxkSkFATeM0x5+jTaeHS3\n87+eXX38WuBKa+2xYxx/JPAcgDFmf0Z7uI7M9tClgU9ba9OMbrx8WvacU4DHdutNWw8cOo2sIjKL\naJhSREpBvoYpx1NvrY1mvz8eOAT4a3arpACwI/vebcAVwFWMDlHeurMBa23aGJMwxgR3a0tE5F1U\njIlIyTPGzAPuy/54rbX22hw0mzLGuKy1GcAAv7LWXjLGcU8CbcaYFcAHGS3IdudndB9dEZExaZhS\nREqetfZta+3K7FcuCjGATcDi7PergU8aY5oBjDH1xpgF2Wtb4A7gV8AD1tpdhZcxpgHosdYmc5RJ\nRMqQijERKQXvnTP2o2m0sfucsV9P4vjfA8cAWGtfAv4FeMgYsxb4I9C227G3ASuy/7u7Y7PtiIjs\nkRn9UCciIrvLPiH5a2vtiTNo43fAxdbaV3KXTETKjXrGRETGYK3tBK4fb9HX8RhjfMDdKsREZCLq\nGRMRERFxkHrGRERERBykYkxERETEQSrGRERERBykYkxERETEQSrGRERERBykYkxERETEQf8fuDmS\n8TL4ln8AAAAASUVORK5CYII=\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "pdos_energy_index_df = pdos_df.set_index(['Energy (E-Ef)']) # Set index.\n", "# Sum same orbitals for all atoms:\n", @@ -262,7 +228,7 @@ "plt.xlabel('E - Ef (eV)') # x axis label.\n", "plt.ylabel('DOS (states/eV)') # x axis label.\n", "plt.xlim([-8.0,4.0]) # Plot limits.\n", - "fig.savefig(\"Fig3.pdf\") # Save figure EPS." + "fig.savefig(out_folder + \"/\" + \"Fig3.pdf\") # Save figure EPS." ] }, { @@ -299,20 +265,9 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAmMAAAF3CAYAAADpZ0xtAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMS4yLCBo\ndHRwOi8vbWF0cGxvdGxpYi5vcmcvNQv5yAAAIABJREFUeJzs3Xd4VFX6wPHvmZLeSIEkBAhNUEpA\nQOmiiA0b4k9Yyy7Ydm27upbddYviNru7K+xaVsUGurBrA7uAAiJKky41QCoppJGeOb8/TgIBQhKS\nmbkzk/fzPPPczJ07974hIfedU96jtNYIIYQQQghr2KwOQAghhBCiI5NkTAghhBDCQpKMCSGEEEJY\nSJIxIYQQQggLSTImhBBCCGEhScaEEEIIISwkyZgQQgghhIUkGRNCCCGEsJAkY0IIIYQQFrIsGVNK\nhSilvlVKfa+U2qKUmmVVLEIIIYQQVlFWLYeklFJAuNa6TCnlBFYAv9Baf3Oy98THx+vU1FRvhSiE\nEMIDXC4XOTk5JCYmYrNJB40IXGvXrs3XWie0dJzDG8E0RZsssKz+qbP+0WxmmJqaypo1azwdmhBC\nCA/Kysqia9eurFmzhuTkZKvDEcJjlFL7WnOcpR9JlFJ2pdQG4CDwmdZ6tZXxCCGE8LzOnTuzc+dO\nOnfubHUoQvgES5MxrXWd1noIkAKcpZQaePwxSqlblVJrlFJr8vLyvB+kEEIIt3I4HPTp0weHw7LO\nGSF8ik901muti4ClwEVNvPaC1nq41np4QkKL3a5CCCF8XHZ2NtHR0WRnZ1sdihA+wbKPJUqpBKBG\na12klAoFJgGPWRWPEEII79BaU1JSglUTyHxFTU0NGRkZVFZWWh2KaKeQkBBSUlJwOp1ter+VbcRJ\nwKtKKTumhe4/WutFFsYjhBBCeE1GRgaRkZGkpqZiCgwIf6S1pqCggIyMDHr27Nmmc1g5m3IjMNSq\n6wshhBBWqqyslEQsACiliIuLoz3j2n1izJgQQoiOIzIykoceeojIyEirQ7GcJGKBob0/R0nGhBBC\neFVkZCQPP/ywJGM+4t1330Upxfbt24/sS09PZ968eRZGddTo0aOtDsHjJBkTQgjhVUVFRdx5550U\nFRVZHYoA5s+fz9ixY5k/f/6Rfb6UjH399ddWh+BxkowJIYTwqvLycubMmUN5ebnVoXR4ZWVlrFix\ngpdeeom33nrryP5f//rXLF++nCFDhvDMM89QWVnJzJkzGTRoEEOHDmXp0qUAzJ07lyuvvJJJkyaR\nmprK7Nmzefrppxk6dCgjR46ksLDwhGsuWLCAgQMHkpaWxvjx44+c54orrmDChAn07duXWbOOLlcd\nEREBwLJly5gwYQJXX301/fv357rrrmtyRu6ECROOrNaTn59PwzKKzV3DalJxTwghhLDaR7+GnE3u\nPWfiILj40WYPee+997jooos47bTTiIuLY+3atQwbNoxHH32UJ598kkWLTJGDp556CqUUmzZtYvv2\n7VxwwQXs2LEDgM2bN7N+/XoqKyvp06cPjz32GOvXr+eee+7htdde4+677z7mmo888giffPIJXbt2\nPaZ19Ntvv2Xz5s2EhYUxYsQIJk+ezPDhw4957/r169myZQvJycmMGTOGlStXMnbs2Fb/k7TmGlaQ\nljEhAlnWBiiTlSuEEE2bP38+06dPB2D69OnHdFU2tmLFCq6//noA+vfvT48ePY4kY+eeey6RkZEk\nJCQQHR3NZZddBsCgQYNIT08/4VxjxoxhxowZvPjii9TV1R3ZP2nSJOLi4ggNDeWqq65ixYoVJ7z3\nrLPOIiUlBZvNxpAhQ5o8f3Nacw0rSMuYEIFq63vwnx+bryc+BON+aW08QtSz2+307t0bu91udSi+\no4UWLE8oLCxkyZIlbNq0CaUUdXV1KKV44oknTuk8wcHBR7622WxHnttsNmpra084/rnnnmP16tUs\nXryYYcOGsXbtWuDEGYlNzVBsfC273d7k+R0OBy6XC+CEgrqtuYYVpGVMiEBUdhA++AUkDYG+F8Ky\nv0LBbqujEgKALl26sGvXLrp06WJ1KB3awoULueGGG9i3bx/p6ekcOHCAnj17snz5ciIjIyktLT1y\n7Lhx43jzzTcB2LFjB/v376dfv35tuu7u3bs5++yzeeSRR0hISODAgQMAfPbZZxQWFlJRUcG7777L\nmDFj2nT+1NTUIwnewoULj3nNXddwN0nGhAhEG9+GikMw5Tm4/B9gD4IvHrE6KiEAqK6u5ptvvqG6\nutrqUDq0+fPnM2XKlGP2TZ06lfnz5zN48GDsdjtpaWk888wz3H777bhcLgYNGsS0adOYO3fuMa1U\np+L+++9n0KBBDBw4kNGjR5OWlgaYLsipU6cyePBgpk6d2uaxXPfddx//+te/GDp0KPn5+ce85q5r\n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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "\"\"\" Select one atom from the previous list. Remember list_of_atoms[0] corresponds to Atom #1, \n", "list_of_atoms[1] to #2 ...\"\"\"\n", @@ -340,7 +295,7 @@ "plt.xlabel('E - Ef (eV)') # x axis label.\n", "plt.ylabel('DOS (states/eV)') # x axis label.\n", "plt.xlim([-8.0,4.0]) # Plot limits.\n", - "fig.savefig(\"Fig4.pdf\") # Save figure EPS." + "fig.savefig(out_folder + \"/\" + \"Fig4.pdf\") # Save figure EPS." ] }, { @@ -372,20 +327,11 @@ }, { "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\nAAAAAElFTkSuQmCC\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "metadata": { + "lines_to_next_cell": 0 + }, + "outputs": [], "source": [ "fig = plt.figure(figsize=(10.0,6.0)) # Create figure.\n", "\n", @@ -423,14 +369,15 @@ "plt.ylabel('DOS (states/eV)') # x axis label.\n", "plt.xlim([-8.0, 4.0]) # Plot limits.\n", "plt.legend() # Add manually legend to the plot.\n", - "fig.savefig(\"Fig5.pdf\") # Save figure EPS." + "fig.savefig(out_folder + \"/\" + \"Fig5.pdf\") # Save figure EPS." ] }, { "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": true + "collapsed": true, + "lines_to_next_cell": 2 }, "outputs": [], "source": [] diff --git a/oxr_reaction/oxr_plotting_classes/oxr_plot_volcano.py b/oxr_reaction/oxr_plotting_classes/oxr_plot_volcano.py index aff4c0e..8077819 100644 --- a/oxr_reaction/oxr_plotting_classes/oxr_plot_volcano.py +++ b/oxr_reaction/oxr_plotting_classes/oxr_plot_volcano.py @@ -382,6 +382,10 @@ def __create_trace_i__(self, # print(mode_i) + # print("KSDJIKFKDSF") + # print("smart_format_i:", smart_format_i) + # print('smart_format_i.get("marker_size", 9)', smart_format_i.get("marker_size", 9)) + trace_i = go.Scatter( x=[x_energy], y=[y_energy], @@ -414,6 +418,7 @@ def __create_trace_i__(self, "color": "black", }, + marker=dict( size=smart_format_i.get("marker_size", 9), color=smart_format_i.get(self.marker_color_key, "red"), diff --git a/plotting/my_plotly.py b/plotting/my_plotly.py index 2c25d3c..7a1e28c 100644 --- a/plotting/my_plotly.py +++ b/plotting/my_plotly.py @@ -79,6 +79,7 @@ def add_duplicate_axes( tmp_define_both_axis_types=False, ): """ + Note: range must be set for this to work """ # | - add_duplicate_axes @@ -108,6 +109,9 @@ def add_duplicate_axes( for i_cnt, (old_index, new_index) in iterator: old_Axis = fig.layout[axis_type + "axis" + str(old_index)] + if old_Axis.range == None: + print("This doesn't work well if you don't set the range attribute! Do that!") + new_axis = copy.deepcopy(old_Axis) new_axis = new_axis.update( showticklabels=False, From 74bc4756563c529476a42cdf337d2663b834143e Mon Sep 17 00:00:00 2001 From: Raul Flores Date: Wed, 18 Mar 2020 18:24:58 -0700 Subject: [PATCH 09/10] Init commit for rclone command script --- rclone/bin/rclone_command.py | 90 ++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 rclone/bin/rclone_command.py diff --git a/rclone/bin/rclone_command.py b/rclone/bin/rclone_command.py new file mode 100644 index 0000000..358bba0 --- /dev/null +++ b/rclone/bin/rclone_command.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python + +"""TEMP. + +Author(s): Raul A. Flores +""" + +#| - IMPORT MODULES +import os + +import argparse +#__| + +#| - Argument Parsing Setup +parser = argparse.ArgumentParser("rclone_command") +parser.add_argument("item", help="folder/file to sync", type=str) +parser.add_argument("--rclone_mode", help="Either sync or copy") +parser.add_argument( + "--run_command", + help="Whether to actually run the rclone command or not", + default=False, + ) +args = parser.parse_args() +#__| + +#| - Input Variables +# selected_obj = "dos_calc" +selected_obj = args.item + + +# remote = "rclone_dropbox" +# remote = "rclone_gdrive" +remote = "rclone_gdrive_stanford" + +if args.rclone_mode: + command_type = args.rclone_mode +else: + command_type = "sync" +#__| + +#| - Find computer system +compenv = os.environ["COMPENV"] +if compenv == "nersc": + root_dir = "/global/cscratch1/sd/flores12" +elif compenv == "sher": + tmp = 42 +else: + print("Couldn't determine system") +#__| + +#| - Getting destintation path +root_dir_len = len(root_dir) + +cwd = os.getcwd() +# print("cwd:", cwd) + +cwd_rel = cwd[root_dir_len + 1:] +# print("cwd_rel:", cwd_rel) + +selected_obj_rel_path = os.path.join( + cwd_rel, + selected_obj) + +# print("selected_obj_rel_path:", selected_obj_rel_path) +#__| + + +remote_name = os.environ[remote] + +tmp = "" + \ + "rclone " + \ + command_type + \ + " " + \ + selected_obj + \ + " " + \ + remote_name + \ + ":" + \ + selected_obj_rel_path + +print("") +print(tmp) + +# print(tmp.split(" ") +print("") +[print(i) for i in tmp.split(" ")] + +if args.run_command: + os.system(tmp) +else: + print("COMMAND NOT RUN (set --run_command flag to True)") From 98a7aed88af09d36a21aea65c43c4be32737890e Mon Sep 17 00:00:00 2001 From: Raul Flores Date: Sun, 17 May 2020 19:18:37 -0700 Subject: [PATCH 10/10] Updating all PythonModules to sync with PROJ_irox --- dft_job_automat/job_analysis.py | 7 +- dft_job_automat/job_setup.py | 12 ++- oxr_reaction/oxr_methods.py | 1 + .../oxr_plot_2d_volcano.py | 97 +++++++++++++++++-- .../oxr_plotting_classes/oxr_plot_scaling.py | 21 ++-- .../oxr_plotting_classes/oxr_plot_volcano.py | 2 +- oxr_reaction/oxr_series.py | 3 + plotting/my_plotly.py | 60 +++++++++--- surface_energy/surface_energy.py | 51 ++++++---- 9 files changed, 206 insertions(+), 48 deletions(-) diff --git a/dft_job_automat/job_analysis.py b/dft_job_automat/job_analysis.py index fc2921c..f2e90a4 100644 --- a/dft_job_automat/job_analysis.py +++ b/dft_job_automat/job_analysis.py @@ -224,9 +224,14 @@ def __load_dataframe__(self): fle_name = self.root_dir + "/jobs_bin/job_dataframe.pickle" with open(fle_name, "rb") as fle: + + if sys.version_info.major > 2: - df = pickle.load(fle, encoding="latin1") + # print("IJDIFIDSj89yu892sdf") + # print("fle_name:", fle_name) + # NOTE Added encoding="latin1" for p36 support (180415 - RF) + df = pickle.load(fle, encoding="latin1") else: df = pickle.load(fle) diff --git a/dft_job_automat/job_setup.py b/dft_job_automat/job_setup.py index 307041b..faf2ae7 100644 --- a/dft_job_automat/job_setup.py +++ b/dft_job_automat/job_setup.py @@ -364,8 +364,18 @@ def __Job_list__(self): print(job_i_dir) if rev_dirs: + + print("rev_dirs:", rev_dirs) + if self.parse_all_revisions is False: - rev_dirs = [rev_dirs[-1]] + + last_rev_int = np.sort( + [int(i.split("_")[-1]) for i in rev_dirs])[-1] + rev_dirs = ["_" + str(last_rev_int), ] + # rev_dirs = [rev_dirs[-1]] + + print("rev_dirs:", rev_dirs) + print("IOPSDFJOKIDSIJFIJDSF") for rev_i in rev_dirs: path_i = os.path.join(job_i_dir, rev_i) diff --git a/oxr_reaction/oxr_methods.py b/oxr_reaction/oxr_methods.py index c362d7f..841bb40 100644 --- a/oxr_reaction/oxr_methods.py +++ b/oxr_reaction/oxr_methods.py @@ -214,6 +214,7 @@ def df_calc_adsorption_e( oxy_ref_e=oxy_ref, hyd_ref_e=hyd_ref, ) + # print("ads_e_i:", ads_e_i) ads_e_list.append(ads_e_i) df["ads_e"] = np.array(ads_e_list) diff --git a/oxr_reaction/oxr_plotting_classes/oxr_plot_2d_volcano.py b/oxr_reaction/oxr_plotting_classes/oxr_plot_2d_volcano.py index 7301688..45dc63b 100644 --- a/oxr_reaction/oxr_plotting_classes/oxr_plot_2d_volcano.py +++ b/oxr_reaction/oxr_plotting_classes/oxr_plot_2d_volcano.py @@ -39,11 +39,20 @@ class Volcano_Plot_2D(): def __init__(self, ORR_Free_E_Plot, plot_range=None, + smart_format_dict=None, + marker_color_key="color2", + marker_border_color_key="color1", + marker_shape_key="symbol", + ): """ """ # | - __init__ self.ORR_Free_E_Plot = ORR_Free_E_Plot + self.smart_format_dict = smart_format_dict + self.marker_color_key = marker_color_key + self.marker_border_color_key = marker_border_color_key + self.marker_shape_key = marker_shape_key if plot_range is None: self.plot_range = { @@ -167,9 +176,54 @@ def __create_data_point_traces__(self): """ """ # | - __create_data_point_traces__ + + + # | - Default Smart Format Dict + smart_format_dict = self.smart_format_dict + + if smart_format_dict is None: + print("No smart format given!") + smart_format_dict = [ + [{"bulk_system": "IrO3"}, {self.marker_color_key: "green"}], + [{"bulk_system": "IrO2"}, {self.marker_color_key: "yellow"}], + + [{"coverage_type": "o_covered"}, {"symbol": "s"}], + [{"coverage_type": "h_covered"}, {"symbol": "^"}], + + [{"facet": "110"}, {"color1": "red"}], + [{"facet": "211"}, {"color1": "green"}], + [{"facet": "100"}, {"color1": "black"}], + ] + # __| + + data_list = [] - for sys_i in self.ORR_Free_E_Plot.series_list: - trace_i = self.__create_scatter_trace_i__(sys_i) + for series_i in self.ORR_Free_E_Plot.series_list: + + + smart_format_i = self.ORR_Free_E_Plot.__create_smart_format_dict__( + series_i.properties, + smart_format_dict, + ) + + name_i = series_i.series_name + + if series_i.color is not None: + smart_format_i[self.marker_color_key] = series_i.color + + + format_i = smart_format_i + + if series_i.format_dict: + format_i = series_i.format_dict + + + print("format_i:", format_i) + + trace_i = self.__create_scatter_trace_i__( + series_i, + smart_format_i=format_i, + ) data_list.append(trace_i) return(data_list) @@ -182,6 +236,7 @@ def __create_scatter_trace_i__(self, """ """ # | - __create_trace_i__ + print("sys_i:", sys_i) trace_i = go.Scatter( x=[sys_i.energy_states_dict["o"] - sys_i.energy_states_dict["oh"]], y=[sys_i.energy_states_dict["oh"]], @@ -193,15 +248,41 @@ def __create_scatter_trace_i__(self, #
hoverinfo="name", + # marker=dict( + # size=20, + # # symbol=sys_i.format_dict["symbol_i"], + # symbol=sys_i.format_dict.get("symbol_i", "circle"), + # color=sys_i.format_dict.get("color_2", "blue"), + # line=dict( + # width=2, + # smart_format_i + # color=sys_i.format_dict.get("color_1", "black"), + # ), + # ), + marker=dict( - size=20, - symbol=sys_i.format_dict["symbol_i"], - color=sys_i.format_dict["color_2"], + size=smart_format_i.get("marker_size", 9), + color=smart_format_i.get(self.marker_color_key, "red"), + symbol=smart_format_i.get( + self.marker_shape_key, "circle"), line=dict( - width=2, - color=sys_i.format_dict["color_1"], + width=smart_format_i.get("marker_border_width", 1.), + color=smart_format_i.get( + self.marker_border_color_key, "black"), ), ), + + # marker=dict( + # size=20, + # # symbol=sys_i.format_dict["symbol_i"], + # symbol=sys_i.format_dict.get("symbol_i", "circle"), + # color=sys_i.format_dict.get("color_2", "blue"), + # line=dict( + # width=2, + # color=sys_i.format_dict.get("color_1", "black"), + # ), + # ), + ) return(trace_i) @@ -223,7 +304,7 @@ def ooh_oh_scaling(self, doh): """ooh_oh_scaling equation.""" # | - ooh_oh_scaling #like ambars - #dooh=0.5*doh + 3.0 #O + #dooh=0.5*doh + 3.0 #O #normal one # dooh = doh + 3.2 diff --git a/oxr_reaction/oxr_plotting_classes/oxr_plot_scaling.py b/oxr_reaction/oxr_plotting_classes/oxr_plot_scaling.py index 47cfd94..91d922e 100644 --- a/oxr_reaction/oxr_plotting_classes/oxr_plot_scaling.py +++ b/oxr_reaction/oxr_plotting_classes/oxr_plot_scaling.py @@ -269,7 +269,9 @@ def __create_trace_i__(self, # color=smart_format_i[marker_border_color_key], color=smart_format_i.get( self.marker_border_color_key, "black"), - width=1., + # width=1., + + width=smart_format_i.get("marker_border_width", 1.), ) ) ) @@ -381,10 +383,11 @@ def fit_scaling_lines(self, # | - Equation Annotations if dependent_species == "ooh": eqn_str_i = ("" + - "ΔGOOH=" + + "ΔGOOH = " + str(round(slope_i, num_round)) + - " ΔGOH+" + + " ΔGOH + " + str(round(intercept_i, num_round)) + + " (eV)" + "" ) @@ -394,8 +397,9 @@ def fit_scaling_lines(self, eqn_str_i = ("" + "ΔGO = " + str(round(slope_i, num_round)) + - " ΔGOH+" + + " ΔGOH + " + str(round(intercept_i, num_round)) + + " (eV)" + "" ) @@ -403,8 +407,9 @@ def fit_scaling_lines(self, eqn_str_i = ("" + "ΔGOH = " + str(round(slope_i, num_round)) + - " ΔGOH+" + + " ΔGOH + " + str(round(intercept_i, num_round)) + + " (eV)" + "" ) @@ -481,21 +486,21 @@ def add_ideal_lines(self): name="*OOH vs *OH Scaling", color="black", width=1, - dash="dash", + dash="dot", ) self.add_line({"slope": 2, "intercept": 0.}, name="*O vs *OH Scaling", color="black", width=1, - dash="dash", + dash="dot", ) self.add_line({"slope": 1, "intercept": 0.}, name="*OH vs *OH Scaling", color="black", width=1, - dash="dash", + dash="dot", ) # __| diff --git a/oxr_reaction/oxr_plotting_classes/oxr_plot_volcano.py b/oxr_reaction/oxr_plotting_classes/oxr_plot_volcano.py index 8077819..4aba0c2 100644 --- a/oxr_reaction/oxr_plotting_classes/oxr_plot_volcano.py +++ b/oxr_reaction/oxr_plotting_classes/oxr_plot_volcano.py @@ -400,7 +400,7 @@ def __create_trace_i__(self, legendgroup=group, - hoverinfo="text", + # hoverinfo="text", # hoverinfo=None, # hoverinfosrc=None, diff --git a/oxr_reaction/oxr_series.py b/oxr_reaction/oxr_series.py index 31bece2..9bfe72f 100644 --- a/oxr_reaction/oxr_series.py +++ b/oxr_reaction/oxr_series.py @@ -103,7 +103,10 @@ def __init__( self.hover_text_col = hover_text_col self.plot_mode = plot_mode self.smart_format = smart_format + self.format_dict = format_dict + if format_dict is None: + self.format_dict = dict() # self.overpotential_type = overpotential_type self.rxn_type = rxn_type diff --git a/plotting/my_plotly.py b/plotting/my_plotly.py index 7a1e28c..058a99b 100644 --- a/plotting/my_plotly.py +++ b/plotting/my_plotly.py @@ -80,6 +80,42 @@ def add_duplicate_axes( ): """ Note: range must be set for this to work + + Example usage: + + from plotting.my_plotly import add_duplicate_axes + + shared_axis_data = { + "tickcolor": "black", + "ticklen": 3, + } + + shared_xaxis_data = { + "dtick": 50, + **shared_axis_data, + } + + shared_yaxis_data = { + "dtick": 1, + **shared_axis_data, + } + + shared_meth_props = dict( + tmp_define_both_axis_types=True, + ) + + add_duplicate_axes( + fig, axis_type='x', + axis_data=shared_xaxis_data, + axis_num_list=[1, ], + **shared_meth_props) + add_duplicate_axes( + fig, axis_type='y', + axis_data=shared_yaxis_data, + axis_num_list=[1, ], + **shared_meth_props) + + """ # | - add_duplicate_axes @@ -309,6 +345,12 @@ def my_plotly_plot( ) # __| + if write_svg: + try: + fig.write_image(prepath + ".svg") + except: + print("Couldn't write svg") + # | - Write pdf and svg (if ORCA is installed and working) # Getting the hostname of computer import socket @@ -333,11 +375,6 @@ def my_plotly_plot( fig.write_image(prepath + ".pdf") except: print("Couldn't write pdf") - if write_svg: - try: - fig.write_image(prepath + ".svg") - except: - print("Couldn't write svg") if write_png: try: fig.write_image(prepath + ".png", scale=png_scale) @@ -393,11 +430,6 @@ def reapply_colors(data): # __| -def plot_layout( - # xax_labels = - ): - """ - @@ -408,6 +440,13 @@ def plot_layout( # | - OLD | add_duplicate_axes + +# def plot_layout( +# # xax_labels = +# ): +# """ + + # def add_duplicate_axes( # fig, # axis_type="x", # 'x' or 'y' @@ -461,4 +500,3 @@ def plot_layout( # }).to_plotly_json()) # # __| # __| - diff --git a/surface_energy/surface_energy.py b/surface_energy/surface_energy.py index 1dfd082..51a4022 100644 --- a/surface_energy/surface_energy.py +++ b/surface_energy/surface_energy.py @@ -193,9 +193,9 @@ def calc_std_surface_energy(self): sssc = special_surface_species_corrections - # TEMP - print(non_stoich_comp) - print(H_ref_electronic_energy) + # # TEMP + # print(non_stoich_comp) + # print(H_ref_electronic_energy) # TODO Make the referencing more robust, take arbitary dict of # referencec atoms @@ -206,14 +206,22 @@ def calc_std_surface_energy(self): -non_stoich_comp.get("H", 0.) * (H_ref_electronic_energy) + \ +0. + print("TEMP | ksjdfidsihjgisdfjgf8sgsd | TEMP") + print("electronic_energy:", electronic_energy) + print("bulk_formula_units_in_slab:", bulk_formula_units_in_slab) + print("bulk_energy_per_formula_unit", bulk_energy_per_formula_unit) + print("std_surface_e_per_side:", surf_e_0) + if apply_special_species_corrections: - special_surface_species for spec_i, num_spec_i in special_surface_species.items(): corr_i = num_spec_i * sssc[spec_i] - print(corr_i) - surf_e_0 += corr_i + print("Special spec corr:", spec_i, corr_i) + # surf_e_0 += corr_i + surf_e_0 -= corr_i + + print("std_surface_e_per_side__after_corr:", surf_e_0) + print("TEMP | ksjdfidsihjgisdfjgf8sgsd | TEMP") - print(surf_e_0) # Divide surface energy across two sides of slab surf_e_0 /= 2 @@ -235,11 +243,11 @@ def __count_special_surface_species__(self): non_stoich_comp_new = copy.copy(non_stoich_comp) - print(non_stoich_comp) + # print(non_stoich_comp) special_species_dict = dict() if "O" in non_stoich_comp.keys(): - + #| - If *O present num_Os = non_stoich_comp.get("O") if "H" in non_stoich_comp.keys(): @@ -254,6 +262,8 @@ def __count_special_surface_species__(self): special_species_dict["*OH"] = num_OHs special_species_dict["*O"] = left_over_Os + special_species_dict["*H"] = left_over_Hs + # All nonstoich Os will be *O species non_stoich_comp_new["O"] = 0 @@ -271,22 +281,26 @@ def __count_special_surface_species__(self): # All nonstoich Os will be *O species non_stoich_comp_new["O"] = 0 non_stoich_comp_new["H"] = left_over_Hs + #__| - else: - num_OHs = 0 - left_over_Os = num_Os - left_over_Hs = 0 - if "H" in non_stoich_comp.keys(): - if non_stoich_comp.get("H") > 0: - raise ValueError( - "NOT GOOD HERE, THERE IS AN *H WITHOUT and *OH") + + #| - __old__ + # else: + # num_OHs = 0 + # left_over_Os = num_Os + # left_over_Hs = 0 + # + # if "H" in non_stoich_comp.keys(): + # if non_stoich_comp.get("H") > 0: + # raise ValueError( + # "NOT GOOD HERE, THERE IS AN *H WITHOUT and *OH") # print("----") # print(non_stoich_comp_new) # print(special_species_dict) - + #__| self.non_stoich_comp_new = non_stoich_comp_new @@ -1009,6 +1023,7 @@ def surf_e_4( -nonstoich_Os * (G_H2O - G_H2) + \ -nonstoich_Hs * (G_H2 / 2) + \ +0. + # -nonstoich_Os * (G_H2O - G_H2 - 2.518583065) + \ # -nonstoich_Hs * (G_H2) + \