From d74f558e0e667399a9d419d53a3398c4f9edd755 Mon Sep 17 00:00:00 2001 From: EmmaSlackGraph Date: Tue, 4 Jun 2024 16:39:31 -0600 Subject: [PATCH 01/24] Add failure calculation files --- failureGraphs.py | 525 ++++++++++++++++++++++++++++ failureProbabilities.py | 741 ++++++++++++++++++++++++++++++++++++++++ graphBuilder.py | 488 ++++++++++++++++++++++++++ searchMethods.py | 429 +++++++++++++++++++++++ twoTurbineCaseStudy.py | 76 +++++ 5 files changed, 2259 insertions(+) create mode 100644 failureGraphs.py create mode 100644 failureProbabilities.py create mode 100644 graphBuilder.py create mode 100644 searchMethods.py create mode 100644 twoTurbineCaseStudy.py diff --git a/failureGraphs.py b/failureGraphs.py new file mode 100644 index 00000000..ef9c78da --- /dev/null +++ b/failureGraphs.py @@ -0,0 +1,525 @@ +import numpy as np +import networkx as nx +import matplotlib.pyplot as plt +import pandas as pd +from failureProbabilities import * +from twoTurbineCaseStudy import * +from famodel.project import Project + + +def addMoreEdges(nearby_platforms, G, Array, node, node_id, failures_c, failures_p, ids): + node_index = np.where(nodeNames == node)[0][0] + platforms = list(Array.platformList.keys()) + if len(nearby_platforms) > 1: + platforms = nearby_platforms + cables = list(Array.cableList.keys()) + all_ids = [] + combos = [] + for p in platforms: + for c in cables: + if c in p: + combos.append(str(p) + ',' + str(c)) + all_ids.append(c) + for m in Array.platformList[p]: + combos.append(str(m) + ',' + str(c)) + all_ids.append(str(m)) + for a in Array.platformList[p].anchorList: + all_ids.append(a) + all_ids = platforms+all_ids+list(combos) + for child in failures_c[node]: + child_index = np.where(nodeNames == child)[0][0] + original_ids = ids + if arr[node_index, child_index] > 1.5: + ids = all_ids + for id in ids: + if child + "\n" + str(id) in G.nodes: + G.add_edge(node + "\n" + str(node_id), child + "\n" + str(id), weight=0.001) + ids = original_ids + for parent in failures_p[node]: + parent_index = np.where(nodeNames == parent)[0][0] + original_ids = ids + if arr[parent_index, node_index] > 1.5: + ids = all_ids + for id in ids: + if parent + "\n" + str(id) in G.nodes: + G.add_edge(parent + "\n" + str(id), node + "\n" + str(node_id), weight=0.001) + ids = original_ids + return G + +def edgeReweight(G, edge): + new_weight = input("Enter weight for (" + str(edge[0].replace("\n", " ")) + ", " + str(edge[1].replace("\n", " ")) + ") (press \'Enter\' for default value)") + if new_weight=='': + new_weight=0.01 + G[edge[0]][edge[1]]['weight']=float(new_weight) + return G + +def get_y_Value(point1, point2, x): + return ((point2[1]-point1[1])/(point2[0]-point1[0]))*(x-point1[0])+point1[1] + +def get_min_max_vals(pnt1, pnt2, angle_radians): + vector = pnt2 - pnt1 + length = math.sqrt(vector[0]**2 + vector[1]**2) + if vector[0] == 0.0 and vector[1] > 0: angle = math.pi/2 + elif vector[0] == 0.0 and vector[1] < 0: angle = 3*math.pi/2 + elif vector[0] == 0.0 and vector[1] == 0: angle = 0.0 + else: angle = math.atan(vector[1]/vector[0]) + if angle == 0 and vector[0] < 0: + angle = math.pi + new_vector1 = np.array([length * math.cos(angle - angle_radians), length * math.sin(angle - angle_radians)]) + np.array(pnt1) + new_vector2 = np.array([length * math.cos(angle + angle_radians), length * math.sin(angle + angle_radians)]) + np.array(pnt1) + if angle == math.pi and angle_radians==0: + new_vector1 = np.array([length * math.cos(angle - angle_radians), 0]) + np.array(pnt1) + new_vector2 = np.array([length * math.cos(angle + angle_radians), 0]) + np.array(pnt1) + max_x = max([new_vector1[0], new_vector2[0], pnt1[0], pnt2[0]]) + min_x = min([new_vector1[0], new_vector2[0], pnt1[0], pnt2[0]]) + max_y = max([new_vector1[1], new_vector2[1], pnt1[1], pnt2[1]]) + min_y = min([new_vector1[1], new_vector2[1], pnt1[1], pnt2[1]]) + if max_x == min_x: + if max_x < 0: max_x = 0 + elif min_x > 0: min_x = 0 + return max_x, min_x, max_y, min_y + +# Create project class instance from yaml file +Array = Project(file='famodel/OntologySample600m.yaml') +print() + +# Create adjacency matrix from failure matrix +df = pd.read_excel("/Users/eslack/Documents/Code/ExcelFiles/failureData.xlsx", sheet_name="bMMatch") +arr = df.to_numpy()[:,1:] +nodeNames = df.to_numpy()[:, 0].flatten() + +# Get initial failure probabilities for each failure mode and effect +probabilities, array_of_probs = getProbabilities("failureProbabilities.xlsx", "Sheet3") + + + +# Determine if using the twoTurbine case study model +tT_input = input("Would you like to calculate for the twoTurbine case study? ") +if 'y' in tT_input: twoTurbine = True +else: twoTurbine = False + +# Determine angle of clashing we are interested in +angle_degree = float(input("What angle do you want to use? (in degrees) ")) +angle_radians = angle_degree/360 * math.pi * 2 + +# Determine what the nearby platforms are (True ==> all other platforms, False ==> physically close turbines) +nba_input = input("Would you like one turbine to directly affect all turbines? ") +nba = False +if 'y' in nba_input.lower(): + nba = True + + + +# Initialize and create the dictionaries of the children, parents, and probabilities for each failure +failures_c = {} +failures_p = {} +probability_dict = {} + +for i in range(arr.shape[0]): + node_children = [] + node_parents = [] + for j in range(arr.shape[1]): + if arr[i,j] > 0: + node_children.append(nodeNames[j]) + if arr[j,i] > 0: + node_parents.append(nodeNames[j]) + failures_c.update({nodeNames[i]: node_children}) + failures_p.update({nodeNames[i]: node_parents}) + probability_dict.update({nodeNames[i]: probabilities[i]}) + +# Create dictionary for each subsystem of failures +turbine = [0,1,2,3,4,5,26,27,28,29] +platform = [6,7,8,9,10,11,30,31,32] +clashing = [12,13,14] +mooring_materials = [33,34,35] +mooring = [15,16,17, 18] +connector = [36] +clump_weight = [37] +anchor = [19,20,38,39] +cable = [21,22,23,41,42, 45,46] +cable_mat = [43,44] +grid = [24,25] +buoyancy = [40] + +systems = {'turbine':nodeNames[turbine], 'platform':nodeNames[platform], 'clashing':nodeNames[clashing], 'moor_mat':nodeNames[mooring_materials], + 'mooring':nodeNames[mooring], 'connector':nodeNames[connector], 'weight':nodeNames[clump_weight], + 'anchor':nodeNames[anchor], 'cable':nodeNames[cable], 'grid':nodeNames[grid], 'cable_mat':nodeNames[cable_mat], + 'buoyancy':nodeNames[buoyancy]} + + +# Initialize graph, boolean for plotting, and list of probabilities +G = nx.DiGraph() +plot = False +probabilities = [] + +for platform in Array.platformList: + # Initialize list of mooring-mooring clashing nodea and list of mooring-cable and anchor-cable clashing nodes + mooring_clashes = [] + cable_clashes = [] + + # Determine the nearby platforms + nearby_platforms = [] + for platform2 in Array.platformList: + platform_xy = Array.platformList[platform].r + platform2_xy = Array.platformList[platform2].r + dist_btwn_turbines = math.sqrt((platform_xy[0] - platform2_xy[0])**2 + (platform_xy[1] - platform2_xy[1])**2) + if dist_btwn_turbines <= (1600 * math.sqrt(2)): + nearby_platforms.append(platform2) + if 'y' in nba_input: + nearby_platforms = [] + + # Create platform nodes + for platform_failure in systems['platform']: + if not(platform_failure + "\n" + str(platform) in list(G.nodes)): + probabilities.append(probability_dict[platform_failure]) + G.add_node(platform_failure + "\n" + str(platform)) + G = addMoreEdges(nearby_platforms, G, Array, platform_failure, platform, failures_c, failures_p, [platform]) + + # Create turbine nodes + for turbine_failure in systems['turbine']: + if not(turbine_failure + "\n" + str(platform) in list(G.nodes)): probabilities.append(probability_dict[turbine_failure]) + G.add_node(turbine_failure + "\n" + str(platform)) + G = addMoreEdges(nearby_platforms, G, Array, turbine_failure, platform, failures_c, failures_p, [platform]) + + for anchor in Array.platformList[platform].anchorList: + # Create anchor nodes + for anchor_failure in systems['anchor']: + if len(Array.platformList[platform].anchorList[anchor].mooringList) > 1 and "ingle" in anchor_failure: continue + elif len(Array.platformList[platform].anchorList[anchor].mooringList) <= 1 and "hared" in anchor_failure: continue + if not(anchor_failure + "\n" + str(anchor) in list(G.nodes)): probabilities.append(probability_dict[anchor_failure]) + G.add_node(anchor_failure + "\n" + str(anchor)) + G = addMoreEdges(nearby_platforms, G, Array, anchor_failure, anchor, failures_c, failures_p, [platform, anchor]) + + for mooring in Array.platformList[platform].anchorList[anchor].mooringList: + # Create mooring nodes based on specific materials + for section in Array.platformList[platform].anchorList[anchor].mooringList[mooring].dd["sections"]: + for material in systems['moor_mat']: + if ((("ope" in section["type"]["material"]) and ("ire" in material)) or (("oly" in section["type"]["material"]) and ("ynth" in material)) )or (("hain" in section["type"]["material"]) and ("hain" in material)): + if not(material + "\n" + str(mooring) in list(G.nodes)): probabilities.append(probability_dict[material]) + G.add_node(material + "\n" + str(mooring)) + G = addMoreEdges(nearby_platforms, G, Array, material, mooring, failures_c, failures_p, [platform, anchor, mooring]) + + # Create other mooring nodes + for mooring_failure in systems['mooring']: + if (Array.platformList[platform].anchorList[anchor].mooringList[mooring].shared) and ("ing line non" in mooring_failure.replace("\n", " ")): continue + elif (not Array.platformList[platform].anchorList[anchor].mooringList[mooring].shared) and ("ared line" in mooring_failure.replace("\n", " ")): continue + else: + if not(mooring_failure + "\n" + str(mooring) in list(G.nodes)): probabilities.append(probability_dict[mooring_failure]) + G.add_node(mooring_failure + "\n" + str(mooring)) + G = addMoreEdges(nearby_platforms, G, Array, mooring_failure, mooring, failures_c, failures_p, [platform, anchor, mooring]) + + # Create mooring-clashing nodes + for mooring2 in Array.platformList[platform].anchorList[anchor].mooringList: + if not(mooring == mooring2): + print('not shared', mooring, mooring2) + mooring_pnt1 = Array.platformList[platform].anchorList[anchor].mooringList[mooring].rA[:2] + mooring_pnt2 = Array.platformList[platform].anchorList[anchor].mooringList[mooring].rB[:2] + mooring2_pnt1 = Array.platformList[platform].anchorList[anchor].mooringList[mooring2].rA[:2] + mooring2_pnt2 = Array.platformList[platform].anchorList[anchor].mooringList[mooring2].rB[:2] + mMaxX, mMinX, mMaxY, mMinY = get_min_max_vals(mooring_pnt1, mooring_pnt2, angle_radians) + cMaxX, cMinX, cMaxY, cMinY = get_min_max_vals(mooring2_pnt1, mooring2_pnt2, angle_radians) + x_overlap = (cMaxX < mMaxX and cMaxX > mMinX) or (cMinX > mMinX and cMinX < mMaxX) + y_overlap = (cMaxY < mMaxY and cMaxY > mMinY) or (cMinY > mMinY and cMinY < mMaxY) + x_on_top = (cMaxX <= mMaxX and cMaxX >= mMinX) and (cMinX >= mMinX and cMinX <= mMaxX) + y_on_top = (cMaxY <= mMaxY and cMaxY >= mMinY) and (cMinY >= mMinY and cMinY <= mMaxY) + if (x_overlap and y_overlap) or (x_on_top and y_on_top): + if not(systems['clashing'][0] + "\n" + str(mooring) + "," + str(mooring2) in list(G.nodes)): + probabilities.append(probability_dict[systems['clashing'][0]]) + G.add_node(systems['clashing'][0] + "\n" + str(mooring) + "," + str(mooring2)) + G = addMoreEdges(nearby_platforms, G, Array, systems['clashing'][0], str(mooring) + str(mooring2), failures_c, failures_p, [platform, anchor, mooring, mooring2]) + mooring_clashes.append(str(mooring) + str(mooring2)) + + # Assign connector failures + for connector in Array.platformList[platform].anchorList[anchor].mooringList[mooring].dd['connectors']: + for connector_failure in systems['connector']: + if not(connector_failure + "\n" + str(mooring) in list(G.nodes)): probabilities.append(probability_dict[connector_failure]) + G.add_node(connector_failure + "\n" + str(mooring)) + G = addMoreEdges(nearby_platforms, G, Array, connector_failure, mooring, failures_c, failures_p, [platform, anchor, mooring, connector]) + + # Create cable-clashing nodes + for cable in Array.cableList: + if platform in Array.cableList[cable].dd['platforms']: + + # Cable failures + for cable_failure in systems['cable']: + if 'disconnect' in cable_failure.replace('\n',' ').lower(): + if not(cable_failure + "\n" + str(cable) + ' ' + str(platform) in list(G.nodes)): + probabilities.append(probability_dict[cable_failure]) + G.add_node(cable_failure + "\n" + str(platform)+','+str(cable)) + G = addMoreEdges(nearby_platforms, G, Array, cable_failure, str(platform)+','+str(cable), failures_c, failures_p, [platform, anchor, mooring, cable]) + else: + if not(cable_failure + "\n" + str(cable) in list(G.nodes)): + probabilities.append(probability_dict[cable_failure]) + G.add_node(cable_failure + "\n" + str(cable)) + G = addMoreEdges(nearby_platforms, G, Array, cable_failure, cable, failures_c, failures_p, [platform, anchor, mooring, cable]) + + # Cable buoyancy modules and materials + for cable_section in Array.cableList[cable].dd['cables']: + # Create buoyancy module nodes (one node per cable) + if 'buoyancy_sections' in cable_section.dd: + n_modules = cable_section.dd['buoyancy_sections'][0]['N_modules'] + for buoy_failure in systems['buoyancy']: + if not(buoy_failure + "\n" + str(cable) in list(G.nodes)): + probabilities.append(probability_dict[buoy_failure]) + G.add_node(buoy_failure + "\n" + str(cable)) + G = addMoreEdges(nearby_platforms, G, Array, buoy_failure, cable, failures_c, failures_p, [platform, anchor, mooring, cable]) + + # Create either dynamic or static cable failure node (one of each per cable at a maximum) + if cable_section.dd['cable_type']['dynamic']: + if not(systems['cable_mat'][0] + "\n" + str(cable) in list(G.nodes)): + probabilities.append(probability_dict[systems['cable_mat'][0]]) + G.add_node(systems['cable_mat'][0] + "\n" + str(cable)) + G = addMoreEdges(nearby_platforms, G, Array, systems['cable_mat'][0], cable, failures_c, failures_p, [platform, anchor, mooring, cable]) + else: + if not(systems['cable_mat'][1] + "\n" + str(cable) in list(G.nodes)): + probabilities.append(probability_dict[systems['cable_mat'][1]]) + G.add_node(systems['cable_mat'][1] + "\n" + str(cable)) + G = addMoreEdges(nearby_platforms, G, Array, systems['cable_mat'][1], cable, failures_c, failures_p, [platform, anchor, mooring, cable]) + + # Cable clashing failures + cable_pnt1 = np.array(Array.platformList[Array.cableList[cable].dd['platforms'][0]].r) + if not ('substation' in Array.cableList[cable].dd['platforms'][1].lower()): + cable_pnt2 = np.array(Array.platformList[Array.cableList[cable].dd['platforms'][1]].r) + else: cable_pnt2 = np.array(Array.substationList[Array.cableList[cable].dd['platforms'][1]].r) + mooring_pnt1 = Array.platformList[platform].anchorList[anchor].mooringList[mooring].rA[:2] + mooring_pnt2 = Array.platformList[platform].anchorList[anchor].mooringList[mooring].rB[:2] + cMaxX, cMinX, cMaxY, cMinY = get_min_max_vals(cable_pnt1, cable_pnt2, angle_radians) + mMaxX, mMinX, mMaxY, mMinY = get_min_max_vals(mooring_pnt1, mooring_pnt2, angle_radians) + x_overlap = (cMaxX < mMaxX and cMaxX > mMinX) or (cMinX > mMinX and cMinX < mMaxX) + y_overlap = (cMaxY < mMaxY and cMaxY > mMinY) or (cMinY > mMinY and cMinY < mMaxY) + x_on_top = (cMaxX <= mMaxX and cMaxX >= mMinX) and (cMinX >= mMinX and cMinX <= mMaxX) + y_on_top = (cMaxY <= mMaxY and cMaxY >= mMinY) and (cMinY >= mMinY and cMinY <= mMaxY) + if (x_overlap and y_overlap) or ((cMaxX == mMaxX and cMinX == mMinX)and(cMaxY == mMaxY and cMinY == mMinY)): + for cable_clashing_failure in systems['clashing'][1:]: + if not(cable_clashing_failure + "\n" + str(cable)+str(mooring) in list(G.nodes)): + probabilities.append(probability_dict[cable_clashing_failure]) + G.add_node(cable_clashing_failure + "\n" + str(cable)+str(mooring)) + G = addMoreEdges(nearby_platforms, G, Array, cable_clashing_failure, str(cable)+str(mooring), failures_c, failures_p, [platform, cable, anchor, mooring, str(cable)+str(mooring)]) + cable_clashes.append(str(cable)+str(mooring)) + if len(cable_clashes) < 1: + if not(systems['clashing'][1] + "\n" + str(platform) + "," + str(cable) in list(G.nodes)): + probabilities.append(probability_dict[systems['clashing'][1]]) + G.add_node(systems['clashing'][1] + "\n" + str(platform) + "," + str(cable)) + G = addMoreEdges(nearby_platforms, G, Array, systems['clashing'][1], str(platform) + ',' + str(cable), failures_c, failures_p, [platform, anchor, cable, mooring, str(cable) + ' ' + str(platform)]) + if not(systems['clashing'][2] + "\n" + str(platform) + ',' + str(cable) in list(G.nodes)): + probabilities.append(probability_dict[systems['clashing'][2]]) + G.add_node(systems['clashing'][2] + "\n" + str(platform) + ',' + str(cable)) + G = addMoreEdges(nearby_platforms, G, Array, systems['clashing'][2], str(platform) + ',' + str(cable), failures_c, failures_p, [platform, anchor, cable, mooring, str(cable) + ' ' + str(platform)]) + + # If there are no specific mooring-mooring clashing, create a generic mooring-mooring clashing node for the platform + if len(mooring_clashes) < 1: + fail_list = [platform, anchor] + for a_variable in Array.platformList[platform].mooringList.keys(): + fail_list.append(a_variable) + if not(systems['clashing'][0] + "\n" + str(platform) in list(G.nodes)): + probabilities.append(probability_dict[systems['clashing'][0]]) + G.add_node(systems['clashing'][0] + "\n" + str(platform)) + G = addMoreEdges(nearby_platforms, G, Array, systems['clashing'][0], platform, failures_c, failures_p, fail_list) + + # Shared mooring line failures + for mooring in Array.platformList[platform].mooringList: + if Array.platformList[platform].mooringList[mooring].shared: + for mooring_failure in systems['mooring']: + if not twoTurbine or 'shared line' in mooring_failure.replace('\n', ' ').lower(): + if not(mooring_failure + "\n" + str(mooring) in list(G.nodes)): + probabilities.append(probability_dict[mooring_failure]) + G.add_node(mooring_failure + "\n" + str(mooring)) + G = addMoreEdges(nearby_platforms, G, Array, mooring_failure, mooring, failures_c, failures_p, [platform, mooring]) + + # Create mooring nodes based on specific materials + for section in Array.platformList[platform].mooringList[mooring].dd["sections"]: + for material in systems['moor_mat']: + if ((("ope" in section["type"]["material"]) and ("ire" in material)) or (("oly" in section["type"]["material"]) and ("ynth" in material)) )or (("hain" in section["type"]["material"]) and ("hain" in material)): + if not(material + "\n" + str(mooring) in list(G.nodes)): probabilities.append(probability_dict[material]) + G.add_node(material + "\n" + str(mooring)) + G = addMoreEdges(nearby_platforms, G, Array, material, mooring, failures_c, failures_p, [platform, mooring]) + + # Create mooring clashing nodes + for mooring2 in Array.platformList[platform].anchorList[anchor].mooringList: + if not(mooring == mooring2): + mooring_pnt1 = np.array(Array.platformList[platform].mooringList[mooring].rA[:2]) + mooring_pnt2 = np.array(Array.platformList[platform].mooringList[mooring].rB[:2]) + mooring2_pnt1 = np.array(Array.platformList[platform].mooringList[mooring2].rA[:2]) + mooring2_pnt2 = np.array(Array.platformList[platform].mooringList[mooring2].rB[:2]) + mMaxX, mMinX, mMaxY, mMinY = get_min_max_vals(mooring_pnt1, mooring_pnt2, angle_radians) + cMaxX, cMinX, cMaxY, cMinY = get_min_max_vals(mooring2_pnt1, mooring2_pnt2, angle_radians) + x_overlap = (cMaxX < mMaxX and cMaxX > mMinX) or (cMinX > mMinX and cMinX < mMaxX) + y_overlap = (cMaxY < mMaxY and cMaxY > mMinY) or (cMinY > mMinY and cMinY < mMaxY) + x_on_top = (cMaxX <= mMaxX and cMaxX >= mMinX) and (cMinX >= mMinX and cMinX <= mMaxX) + y_on_top = (cMaxY <= mMaxY and cMaxY >= mMinY) and (cMinY >= mMinY and cMinY <= mMaxY) + if (x_overlap and y_overlap) or (x_on_top or y_on_top): + if not(systems['clashing'][0] in list(G.nodes)): probabilities.append(probability_dict[systems['clashing'][0]]) + G.add_node(systems['clashing'][0] + "\n" + str(mooring) + "," + str(mooring2)) + G = addMoreEdges(nearby_platforms, G, Array, systems['clashing'][0], str(mooring) + "," + str(mooring2), failures_c, failures_p, [platform, mooring, mooring2]) + mooring_clashes.append(str(mooring) + str(mooring2)) + + # Cable clashing failures + for cable in Array.cableList: + if platform in Array.cableList[cable].dd['platforms']: + cable_pnt1 = np.array(Array.platformList[Array.cableList[cable].dd['platforms'][0]].r) + if not ('substation' in Array.cableList[cable].dd['platforms'][1].lower()): + cable_pnt2 = np.array(Array.platformList[Array.cableList[cable].dd['platforms'][1]].r) + else: cable_pnt2 = np.array(Array.substationList[Array.cableList[cable].dd['platforms'][1]].r) + + if not ('substation' in mooring[0].lower()):mooring_pnt1 = np.array(Array.platformList[mooring[0]].r) + else: mooring_pnt1 = np.array(Array.substationList[mooring[0]].r) + if not ('substation' in mooring[1].lower()): mooring_pnt2 = np.array(Array.platformList[mooring[1]].r) + else: mooring_pnt2 = np.array(Array.substationList[mooring[1]].r) + + x_overlap = (cMaxX < mMaxX and cMaxX > mMinX) or (cMinX > mMinX and cMinX < mMaxX) + y_overlap = (cMaxY < mMaxY and cMaxY > mMinY) or (cMinY > mMinY and cMinY < mMaxY) + cMaxX, cMinX, cMaxY, cMinY = get_min_max_vals(cable_pnt1, cable_pnt2, angle_radians) + mMaxX, mMinX, mMaxY, mMinY = get_min_max_vals(mooring_pnt1, mooring_pnt2, angle_radians) + if (x_overlap and y_overlap) or ((cMaxX == mMaxX and cMinX == mMinX)and(cMaxY == mMaxY and cMinY == mMinY)): + for cable_clashing_failure in systems['clashing'][1:]: + if not (systems['clashing'][1] in list(G.nodes)): probabilities.append(probability_dict[systems['clashing'][1]]) + G.add_node(systems['clashing'][1] + "\n" + str(mooring) + "," + str(cable)) + G = addMoreEdges(nearby_platforms, G, Array, systems['clashing'][1], str(mooring) + "," + str(cable), failures_c, failures_p, [platform, cable, mooring, str(mooring) + "," + str(cable)]) + G = addMoreEdges(nearby_platforms, G, Array, systems['clashing'][1], str(platform) + "," + str(cable), failures_c, failures_p, [platform, cable, mooring, str(platform) + "," + str(cable)]) + cable_clashes.append(str(cable)+str(mooring)) + + # Grid/substation failures + for grid_failure in systems['grid']: + if not(grid_failure in list(G.nodes)): + probabilities.append(probability_dict[grid_failure]) + G.add_node(grid_failure + "\n" + "") + G = addMoreEdges(nearby_platforms, G, Array, grid_failure, "", failures_c, failures_p, [platform]) + + + +# If interested in the twoTurbine case study, alter a few of the failure nodes +if twoTurbine: + list_of_failures_to_rid = ['Tether & anchor systems array_cable10', 'Cable protection system array_cable10', + 'Terminations array_cable10', 'Offshore joints array_cable10'] + for failure in list(G.nodes): + if 'connector' in failure.replace('\n', ' ').lower(): + G.remove_node(failure) + if 'buoyancy modules' in failure.replace('\n', ' ').lower(): + G.remove_node(failure) + if failure.replace('\n', ' ') in list_of_failures_to_rid: + G.remove_node(failure) + + + +# Print the number of nodes and (if the user wants) the list of nodes +print('\nNumber of nodes -',len(G.nodes),'\n') +user_input3 = input("Would you like to see the list of failures? ") +if 'y' in user_input3.lower() or 'rue' in user_input3.lower(): + for edge in list(G.nodes): + print(edge.replace("\n", " ")) + +# Print the number of nodes and (if the user wants) the list of edges +print('\nNumber of edges -',len(G.edges),'\n') +user_input4 = input("Would you like to see the list of edges? ") +if 'y' in user_input4.lower() or 'rue' in user_input4.lower(): + itervar = 0 + for edge in list(G.edges): + print(edge) + itervar += 1 + if (itervar + 1) % 1000 == 0: user_input45 = input("Continue? ") + + + +# If the user wants to input probabilities for specific edges, reweight edges based on the user's inputs +user_inputs = input("Would you like to input probabilities into adjacency matrix? ") +twoTurbine_calculationType = False +if (user_inputs == 'y' or user_inputs == 'yes') or user_inputs == 'True': + twoTurbine_calculationType = True + for i in range(len(G.edges)): + edge = list(G.edges)[i] + if ('rift off' in edge[0].replace("\n", " ") or 'ncreased' in edge[0].replace("\n", " ")) or 'ynamics' in edge[0].replace("\n", " "): + G = edgeReweight(G, edge) + elif ('apsize' in edge[0].replace("\n", " ") or '-cable' in edge[0].replace("\n", " ")) or 'ing line non' in edge[0].replace("\n", " "): + G = edgeReweight(G, edge) + elif ('ragging' in edge[0].replace("\n", " ") or 'hain' in edge[0].replace("\n", " ")) or 'ire rope' in edge[0].replace("\n", " "): + G = edgeReweight(G, edge) + elif ('ynthetic' in edge[0].replace("\n", " ") or 'able profile' in edge[0].replace("\n", " ")) or 'ared line' in edge[0].replace("\n", " "): + G = edgeReweight(G, edge) + elif ('load on cable' in edge[0].replace("\n", " ") or 'eight' in edge[0].replace("\n", " ")): + G = edgeReweight(G, edge) + + + +# Ask user if they are ready to continue to Bayesian network calculations (if not, quit) +continue_input = input("Ready to continue? ") +if 'n' in continue_input.lower(): + quit() + + +# Bayesian network calculation +arr = nx.to_numpy_array(G) +nodeNames = np.reshape(np.array(list(G.nodes)), (len(list(G.nodes)), )) + +poc = "child" +filename = "turbineInference_" + poc + "FAModelTwoTurbine" + "_" + str(nba_input) + ".xlsx" +for start_component in range(1,arr.shape[0]+1): # Iterate through each failure mode/effect in turbine + print(start_component) + +with pd.ExcelWriter(filename) as writer: + all_probabilities = np.zeros(arr.shape) # Initialize a large array to put all the pairwise probabilities in + for start_component in range(1,arr.shape[0]+1): # Iterate through each failure mode/effect in turbine + a = arr.copy() + non = nodeNames + K, a, g, e, m, non = breadth_first_multi(a, nodeNames, [start_component], poc) # Generate tree for Bayesian network + prblts = [] # Initialize array of node probabilities (in order of appearance in graph) + for node in non: + node_index = np.where(nodeNames == node)[0][0] + prblts.append(probabilities[node_index]) # Add nodes to array of node probabilities + prblts = np.array(prblts) + + probabilitiy_table = np.zeros((2, a.shape[0])) # Initialize table of inference probabilities + nodes = diagonal_nodes(a) # Diagonal matrix of node names (numerical +1) + a = make_binary(a, 0.5) # Binarize adjacency table + + nodeNamesArray = np.array(list(K.nodes)) # Initialize array of names of nodes + nodeNamesArray = np.reshape(nodeNamesArray, (len(nodeNamesArray),)) # Make numpy array + + # Interence----------------------------------------------------------------------- + for node in range(a.shape[0]): + pts_bool = nodes @ a[:, node] # vector of zeros and child names (numerical names) + pts = pts_bool[np.nonzero(pts_bool)] #list of just the child names (numerical names) + + if len(pts) < 1: # If no parents, add probability of failure happening to the probability table + probabilitiy_table[0][node] = probabilities[np.where(nodeNames == nodeNamesArray[int(node)])[0][0]] + probabilitiy_table[1][node] = 1 - probabilities[np.where(nodeNames == nodeNamesArray[int(node)])[0][0]] + continue + + if twoTurbine_calculationType: + parents, our_table = twoTurbine_bayesian_table(a, arr, node + 1, nodeNames, non) # Calculate the probability distribution table + else: + parents, our_table = bayesian_table(a, node+1, True, nodeNames, True, prblts) + mlt_table = np.ones((our_table.shape[0],2)) # Initialize table for multiplying across rows of probability distribution table + + # Calculate Probability Table ------------------------------------------------------------ + for i in range(our_table.shape[0]): + for j in range(our_table.shape[1] - 2): + parent = int(parents[j]) + if our_table[i,j] == 0: + our_table[i,j] = probabilitiy_table[0][parent - 1] + if probabilitiy_table[0][parent - 1] == 0: + break + else: + our_table[i,j] = probabilitiy_table[1][parent - 1] + if (parent-1 == 0): # If the node's parent is the evidence, zero out the non-failing possibility + our_table[i,j] = 0 + mlt_table[i,0] *= our_table[i,j] # Multiply the probabilities across the probability distribution table + mlt_table[i,1] = mlt_table[i,0] * our_table[i, -1] # Multiple by the probability of event not failing given combination of parent failure + mlt_table[i,0] *= our_table[i, -2] # Multiple by the probability of event failing given combination of parent failure + sm_table = np.sum(mlt_table, axis = 0) #/np.sum(mlt_table) # Sum the products of probabilities across the columns + probabilitiy_table[0][node] = sm_table[0] # Update the inference probability table with the probabilites just calculated + probabilitiy_table[1][node] = sm_table[1] + + # Print and add probability of node to table + print(start_component, node, " --> Probability of ", nodeNamesArray[node].replace("\n", " "), "=", sm_table) + index2 = np.where(nodeNames == nodeNamesArray[node])[0][0] + all_probabilities[0 * arr.shape[0] + start_component - 1][0 * arr.shape[0] + index2] = sm_table[0]/np.sum(sm_table) + + # Write array to dataframe + df3 = pd.DataFrame(all_probabilities) + df3.to_excel(writer, sheet_name="allProbs") + + +# If we want to plot the network, plot it now +if plot: + nx.draw_networkx(G) + plt.show() \ No newline at end of file diff --git a/failureProbabilities.py b/failureProbabilities.py new file mode 100644 index 00000000..5bece555 --- /dev/null +++ b/failureProbabilities.py @@ -0,0 +1,741 @@ +import numpy as np +import networkx as nx +import matplotlib.pyplot as plt +import scipy.stats +import copy +import math +import random + +from graphBuilder import * +from searchMethods import * +from twoTurbineCaseStudy import * + +''' failureProbability ------------------------------------------------------------------------------------------------------------- + + ****************************** Last Updated: 23 April 2024 ****************************** + + Methods: + 1) transition_natrix: inputs adjacency matrix, probabilities --> output conditional probabilities, scaled probabilities + + 2) conditional_probabilities_update: input start node, list of probabilities --> output conditional probabilities + + 3) single_simulation: input start node, adjacency martrix, list of node nanes, update boolean --> output graph generated + + 4) single_backward_simulation: input start node, adjacency martrix, list of node nanes, update boolean --> output graph generated + + 5) monte_carlo_sim: number of iterations, plotting boolean, starting nodes, adjacency matrix, list of node names, random seed + booleanm, midpoint boolean --> output average probabilities, similarity between average and conditional probabilities + + 6) single_conditional_prob: inputs first probability, second probability, midpoint boolean --> output conditional probability + + 7) multi_cond_prob: input parent nodes, current nodes, probabilities, midpoint boolean --> output conditional probability + + 8) bayesian_table: input adjacency matrix, current node, midpoint boolean, node names, alternate probabilties boolean, + alternate probabilities, multiple turbines boolean --> output parents list, probability distribution table + + 9) write_bayesian_probabilities: input adjacency matrix, node names, probabilties, multiple turbines boolean, midpoint boolean calculation, + number of iterations --> writes probability distribution tables to Excel file (nothing returned) + +10) probability_over_time: input failure rate, time --> output probability of a failure at time t + +11) bayesian_inference: input adjacency matrix, node names, indicator nodes, evidence nodes, hypothesis nodes, probabilties, hypothesis +boolean, printing boolean, multiple turbine boolean, parent or child indicator, twoTurbine boolean --> outputs inference probabilities + +12) backward_bayesian_inference: input adjacency matrix, list of node names, array of nodes to start with, array of evidence nodes, + array of hypothesis nodes, probabilties, boolean for indexing into the right values of the array, boolean for multiple turbines, + parent or child indicator, boolean for twoTurbine method --> output two arrays of inference probabilities (one normalized and one not) + +------------------------------------------------------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------------------------------------------------------ + + + transition_natrix Documentation + ---------------------- + This method inputs an adjacency matrix and vector of probabilities (corresponding to the probabilitiy that each + failure will happen). Since the data we have does not tell us the probability that any two event will happen, we + find the lower and upper bounds of what the conditional probability of every pair of related events (related indicated + by the adjacency matrix). We then take the median of these values as the probability that the second failure will + occur given that the first already occured. This method returns the array of these conditional probabilities and + a scaled array of these probabilities (scaled so that the sum of the columns is 1).''' + +def transition_matrix(arr, probabilities, mid_point = True): + arr = make_binary(arr) # Array to show which failures lead to other failures + nodes = diagonal_nodes(arr) # Matrix with numbers on the diagonal + + bounds = [] # Initialize list of upper and lower bounds + transitions = np.zeros(arr.shape) # Initialize matrix of conditional probabilities + + for node in range(arr.shape[0]): # For each node... + children_bool = arr[node] @ nodes # Find the children of the node + children = children_bool[np.nonzero(children_bool)] + + parent_probability = probabilities[node][0] # Find the probabilitiy of the node occuring + lower_bound = [] # Initialize lower bounds of all children of the node + upper_bound = [] # Initialize upper bounds of all children of the node + + for child in children: # For each of the node's children... + child_probability = probabilities[child - 1][0] # Find the probability of the child occuring + + # The lower bound is the probability of the child occuring (i.e. child and parent are completely indepedent evens) + lb = child_probability + # The upper bound is the overlap of the child and parent probabilities (i.e. child and parent are completely dependent events) + ub = min(parent_probability, child_probability)/parent_probability + + if mid_point: + tm_val = (lb + ub) / 2 # Calculate the midpoint of the two bounds + # print("Midpoint = True") --> for debugging + else: + rand_val = np.random.rand() + tm_val = rand_val * (ub - lb) + lb + # print(rand_val, tm_val, tm_val == ((lb + ub) / 2), lb, ub) --> for debugging + # print("Midpoint = False") --> for debugging + + transitions[node][child - 1] = tm_val + lower_bound.append(lb) # Append the lower bound to the list + upper_bound.append(ub) # Append the upper bound to the list + + lower_bound = np.array(lower_bound) # Convert lower and upper bounds to numpy arrays + upper_bound = np.array(upper_bound) + + midpoint = (upper_bound + lower_bound) / 2 # Calculate the midpoints for all the children of the parent node + + bounds.append([lower_bound, upper_bound, midpoint]) # Append the lower, upper, and midpoints to the list + + transition_matrix = transitions / np.sum(transitions, axis = 0) # Scale the conditional probabilities + transition_matrix[np.isnan(transition_matrix)] = 0 # Replace any NaN values with 0s + return transitions, transition_matrix # Return conditional probabilities and scaled conditional probabilities + +''' conditional_probabilities_update Documentation + -------------------------------------------------- + This method inputs a start value (indicating the starting node) and a list of probabilities that each node will + occur. We then compute conditional probabilities of all the nodes given the starting node. This method returns + the list of calculated conditional probabilities.''' + +def conditional_probabilities_update(start_arr, probabilities): + # Create vector of the parent probabilities + for start in start_arr: + parent_probability = np.reshape(np.repeat(probabilities[start][0], probabilities.shape[0]), probabilities.shape) + + # Calculate the conditional probabilities (using the midpoint method described in transition_matrix() method) + conditional_probabilities = (probabilities + np.reshape(np.min(np.hstack((probabilities, parent_probability)), axis = 1), probabilities.shape)/parent_probability[0])/2 + probabilities = conditional_probabilities + + return conditional_probabilities # Return the calculated probabilities + +''' bayesian_probabilities Documentation + -------------------------------------------------- + This method inputs a start value (indicating the starting node) and a list of probabilities that each node will + occur. We then compute conditional probabilities of all the nodes given the starting node. This method returns + the list of calculated conditional probabilities.''' + +def bayesian_probabilities(parents, child, probabilities): + for i in range(len(parents)): + rand_val = np.random.rand() + + + +''' single_simulation Documentation + --------------------------------------------- + This method inputs a starting node, adjacency martrix, list of node nanes, and a boolean that either uses the method of + updating conditional probabilities based on already visited nodes. We then compute the failure propagation and graph the + propagation via Networkx. This method returns the graph generated.''' + +def single_simulation(start_arr, arr, nodeNames, update = False, plot = False, rand_seed = True, mid_point = True): + monte_carlo_array = np.zeros(arr.shape) + probs = np.zeros((arr.shape[0], 1)) + + # Use the probabilities from the COREWIND failure rates + probabilities = np.array([0.0195, 0.0195, 0.013625, 0.0055, 0.0175, 0.2075, 0.001, 0.001, 0.001, 0.093185, 0.001, 0.001, + 0.027310938, 0.033968125, 0.033968125, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, + 0.0205, 0.0205, 0.02, 0.01, 0.01, 0.233, 0.288, 0.543374, 0.1285, 0.01, 0.01, 0.01, 0.015, 0.0155, + 0.015, 0.0155, 0.015, 0.0155, 0.015, 0.33, 0.025, 0.025, 0.025, 0.025, 0.025, 0.105]) #0.01375, + probabilities = np.reshape(probabilities, (arr.shape[0], 1)) # Reshape these probabilities into a vector + + G = nx.DiGraph() # Initialize a graph and a list of modes and effects for plotting nodes in different colors + effects = [] + modes = [] + + if update: # If you want to update the probabilities, update the probabilities given the indicated starting node + probabilities = conditional_probabilities_update(start, probabilities) + transitions, tm = transition_matrix(arr, probabilities, mid_point) # Compute the probabilities for all linked nodes + + if rand_seed: random.seed(16) # Use a random seed to get the same results (yet random results) each time + + adj = make_binary(arr).astype(int) # Make a binary adjacency matrix to determine the children of each node + nodes = diagonal_nodes(arr) # Diagonal array of node indices + 1 + + nodeList = np.reshape(np.repeat(False, arr.shape[0]), (arr.shape[0], 1)) # Create a list ot determine which nodes have been visited + gens = {} + queue = [] + + for start in start_arr: + G.add_node(str(0) + ": " + str(nodeNames[start-1])) + nodeList[start-1][0] = False + queue.append([start, 0, 0]) + gens.update({str(0) + ": " + str(nodeNames[start-1]): {"layer": 0}}) + # Determine if the source node is an effect or a mode, and add it to the correct array + '''if source < 49: modes.append(nodeNames[source - 1]) + else: effects.append(nodeNames[source - 1])''' + if start < 27: effects.append(nodeNames[start - 1]) # Add the start node to either the effects or mode list + else: modes.append(nodeNames[start - 1]) + + while len(queue) > 0: # For each node in the queue... + current = queue[0] # Get the node from the front of the queue + children_bool = adj[current[0]-1] @ nodes # vector of zeros and child names (numerical names) + children = children_bool[np.nonzero(children_bool)] #list of just the child names (numerical names) + + for child in children: # For each child of the current node... + if nodeList[child - 1] == False or gens[nodeNames[child - 1]]['layer'] > current[1]: # If the node has not been visited... + if np.random.rand() < transitions[current[0] - 1][child - 1]: # If the random value is less than the probability... + monte_carlo_array[current[0] - 1][child - 1] = 1 + probs[child - 1] = 1 + G.add_node(nodeNames[child-1]) # Add the child to the graph + if update: # If updateing is desired, update the probabilities with the addition of the child node + probabilities = conditional_probabilities_update(current[0]-1, probabilities) + transitions, tm = transition_matrix(arr, probabilities, mid_point) + + # Determine if the child is an effect or mode and add it to the correct array + if child < 27: effects.append(nodeNames[child - 1]) + else: modes.append(nodeNames[child - 1]) + + # Add an edge between the current node and child in the tree we are building + G.add_edge(nodeNames[current[0]-1], nodeNames[child-1]) + + queue.append([child, current[1]+1]) # Append the child to the queue + nodeList[child - 1] = True # Change the status of the child to say we have visited it + gens.update({nodeNames[child-1]: {"layer": current[1]+1}}) # Add the child to the layer dictionary + queue = queue[1:] # Remove the current node from the queue + + nx.set_node_attributes(G, gens) # Set the layers of the graph + + # Plot the graph + if plot: + pos = nx.multipartite_layout(G, subset_key='layer') + nx.draw_networkx_nodes(G, pos, nodelist=effects, node_color="#98c5ed", node_size=2700, edgecolors="#799dbd", node_shape="s") + nx.draw_networkx_nodes(G, pos, nodelist=modes, node_color="#fabc98", node_size=2700, edgecolors="#c89679", node_shape="s") + nx.draw_networkx_labels(G, pos, font_size=10, verticalalignment='center_baseline') + nx.draw_networkx_edges(G, pos, arrowsize=60) + plt.box(False) + plt.show() + + # draw_bfs_multipartite(arr, nodeNames, start, "child") # Plot the graph when all probabilities are one for comparison + return G, monte_carlo_array, probs # Return the graph + + + + +''' single_backward_simulation Documentation + --------------------------------------------- + This method inputs a starting node, adjacency martrix, list of node nanes, and a boolean that either uses the method of + updating conditional probabilities based on already visited nodes. We then compute the backward failure propagation and graph the + propagation via Networkx. This method returns the graph generated.''' + +def single_backward_simulation(start_arr, arr, nodeNames, update = False): + # Use the probabilities from the COREWIND failure rates + probabilities = np.array([0.0195, 0.0195, 0.013625, 0.0055, 0.0175, 0.2075, 0.001, 0.001, 0.001, 0.093185, 0.001, 0.001, + 0.027310938, 0.033968125, 0.033968125, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, + 0.0205, 0.0205, 0.02, 0.01, 0.01, 0.233, 0.288, 0.543374, 0.1285, 0.01, 0.01, 0.01, 0.015, 0.0155, + 0.015, 0.0155, 0.015, 0.0155, 0.015, 0.33, 0.025, 0.025, 0.025, 0.025, 0.025, 0.105]) #0.01375, + probabilities = np.reshape(probabilities, (arr.shape[0], 1)) # Reshape these probabilities into a vector + + G = nx.DiGraph() # Initialize a graph and a list of modes and effects for plotting nodes in different colors + effects = [] + modes = [] + queue = [] + gens = {} + + if update: # If you want to update the probabilities, update the probabilities given the indicated starting node + probs = conditional_probabilities_update(start, probabilities) + else: probs = probabilities + transitions, tm = transition_matrix(arr.T, probs) # Compute the probabilities for all linked nodes + + # random.seed(16) # Use a random seed to get the same results (yet random results) each time --> feel free to comment out + + adj = make_binary(arr).astype(int) # Make a binary adjacency matrix to determine the children of each node + nodes = diagonal_nodes(adj) # Create a matrix with node names to determine the children of each node + nodeList = np.reshape(np.repeat(False, arr.shape[0]), (arr.shape[0], 1)) # Create a list ot determine which nodes have been visited + + for start in start_arr: + G.add_node(str(0) + ": " + str(nodeNames[start-1])) + nodeList[start-1][0] = False + queue.append([start, 0, 0]) + gens.update({str(0) + ": " + str(nodeNames[start-1]): {"layer": 0}}) + # Determine if the source node is an effect or a mode, and add it to the correct array + '''if source < 49: modes.append(nodeNames[source - 1]) + else: effects.append(nodeNames[source - 1])''' + if start < 27: effects.append(nodeNames[start - 1]) # Add the start node to either the effects or mode list + else: modes.append(nodeNames[start - 1]) + + while len(queue) > 0: # For each node in the queue... + current = queue[0] # Get the node from the front of the queue + children_bool = adj[current[0]-1] @ nodes # vector of zeros and child names (numerical names) + children = children_bool[np.nonzero(children_bool)] #list of just the child names (numerical names) + + for child in children: # For each child of the current node... + if nodeList[child - 1] == False or gens[nodeNames[child - 1]]['layer'] < current[1]: # If the node has not been visited... + if np.random.rand() < transitions[current[0] - 1][child - 1]: # If the random value is less than the probability... + G.add_node(nodeNames[child-1]) # Add the child to the graph + if update: # If updateing is desired, update the probabilities with the addition of the child node + probs = conditional_probabilities_update(current[0]-1, probs) + transitions, tm = transition_matrix(arr.T, probs) + + # Determine if the child is an effect or mode and add it to the correct array + if child < 27: effects.append(nodeNames[child - 1]) + else: modes.append(nodeNames[child - 1]) + + # Add an edge between the current node and child in the tree we are building + G.add_edge(nodeNames[child-1], nodeNames[current[0]-1]) + + queue.append([child, current[1]-1]) # Append the child to the queue + nodeList[child - 1] = True # Change the status of the child to say we have visited it + gens.update({nodeNames[child-1]: {"layer": current[1]-1}}) # Add the child to the layer dictionary + queue = queue[1:] # Remove the current node from the queue + + nx.set_node_attributes(G, gens) # Set the layers of the graph + + # Plot the graph + # pos = nx.multipartite_layout(G, subset_key='layer') + # nx.draw_networkx_nodes(G, pos, nodelist=effects, node_color="#98c5ed", node_size=2700, edgecolors="#799dbd", node_shape="s") + # nx.draw_networkx_nodes(G, pos, nodelist=modes, node_color="#fabc98", node_size=2700, edgecolors="#c89679", node_shape="s") + # nx.draw_networkx_labels(G, pos, font_size=10, verticalalignment='center_baseline') + # nx.draw_networkx_edges(G, pos, arrowsize=60) + # plt.box(False) + # plt.show() + return G, nx.to_numpy_array(G), probs # Return the graph + + + + +''' monte_carlo_sim Documentation + --------------------------------------------- + This method inputs the number of iterations, a boolean for plotting the average graph or not, an array of the nodes to start with, + an adjacency matrix, list of nodeNames, boolean for a random seed, and a boolean for using a midpoint calculation. We then generate + a graph with failure probabilities for the number of iterations and find the average graph and the probability that each node + is in the graph (the number of times the node shows up divided by the number of iterations). Lastly, we calculate the similarity + between the probability of the nodes in the graph (that we just calculated) compared to the estimated probability calculated via + conditional probabilities. We return the first list of probabilities and cosine similarity between the two lists of probabilities.''' + +def monte_carlo_sim(num_iterations, plot, start_arr, adjacency_matrix, nodeNames, rand_seed, mid_point): + # Initialize the adjacency matrix to trrack which nodes are in each graph (which we will then calculate an average from) + adj_matrices = np.zeros(adjacency_matrix.shape) + + # Initialize array for probabilities to track average probabilities for each node + probs = np.zeros((adjacency_matrix.shape[0], 1)) + + # List of probabilities for each failure happening + probabilities = np.array([0.0195, 0.0195, 0.013625, 0.0055, 0.0175, 0.2075, 0.001, 0.001, 0.001, 0.093185, 0.001, 0.001, + 0.027310938, 0.033968125, 0.033968125, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, + 0.0205, 0.0205, 0.02, 0.01, 0.01, 0.233, 0.288, 0.543374, 0.1285, 0.01, 0.01, 0.01, 0.015, 0.0155, + 0.015, 0.0155, 0.015, 0.0155, 0.015, 0.33, 0.025, 0.025, 0.025, 0.025, 0.025, 0.105]) #0.01375, + probabilities = np.reshape(probabilities, (adjacency_matrix.shape[0], 1)) + + # Run each simulaiton + for i in range(num_iterations): + arr = copy.deepcopy(adjacency_matrix) + G, adj_mat, prob = single_simulation(start_arr, arr, nodeNames, rand_seed = rand_seed, mid_point = mid_point) + adj_matrices += adj_mat.astype(float) + probs += prob + # print(i+1) # Debugging --> feel free to uncomment + + # Calculate average graph and average probabilities + adj_matrices = adj_matrices/num_iterations + probs = probs/num_iterations + K, abc = matrix2Graph(adj_matrices, nodeNames, True) + + # Calculate similarity between average and conditional probabilities + v1 = conditional_probabilities_update(start_arr, probabilities) + v2 = probs + + # Plot average graph + if plot: + draw_bfs_multipartite(adj_matrices, nodeNames, start_arr, "child") + return v2, cosine_similarity(v1, v2) # Return average probabilities and similarity of average and conditional probabilities + + + + +''' single_conditional_prob Documentation + --------------------------------------------- + This method inputs the probability of first failure, probability of second failure, and boolean for using the midpoint calculation + or not. We use these probabilities to estimate the conditional probability between the two failures. We return this conditional probability.''' + +def single_conditional_prob(pa, pb, mid_point = True): + lb = pb * pa # Lower bound for conditional probability + ub = min(pa, pb) # Upper bound for conditional probability + if mid_point: + overlap = (lb + ub) / 2 # Find midpoint between the lower and upper bounds + else: + rand_val = np.random.rand() + overlap = rand_val * (ub - lb) + lb # Find a random value between the lower and upper bounds + return overlap/pa # Return the conditional probability + + + + +''' multi_cond_prob Documentation + --------------------------------------------- + This method inputs array of parent nodes, current nodes, array of probabilities, and boolean for using a midpoint calculation or not. We + calculate the conditional probability when the node has multiple parents (i.e. probability of current event given the parent events). This + method outputs the conditional probability.''' + +def mult_cond_prob(parents, current, probabilities, midpoint = True): + if len(parents) < 1: + return probabilities[current - 1] # If no parents, return the probability of the current node + + # Create list of parent node probabilities, with the probability of the current node appended to the end + parents_sub = [int(parent - 1) for parent in parents] + parents_sub.append((current - 1)) + parents_sub = np.array(parents_sub) + parent_probs = probabilities[parents_sub] + # Initialize denomenator + denomenator = 1 + + # Calculate the single conditional probability for pairs of events until there are no events left + while len(parent_probs) > 1: + if (not isinstance(parent_probs[1][0], float)) and len(parent_probs[1][0]) > 1: + parent_probs[1][0] = parent_probs[1][0][1] + lb = parent_probs[0][0] * parent_probs[1][0] # Lower bound for conditional probability + ub = min(parent_probs[0][0], parent_probs[1][0]) # Upper bound for conditional probability + if midpoint: + val = (lb + ub) / 2 # Find midpoint between the lower and upper bounds + else: + rand_val = np.random.rand() + val = rand_val * (ub - lb) + lb + parent_probs = np.vstack(([val], parent_probs[2:])) + if len(parent_probs) == 2: + denomenator = parent_probs[0][0] + # print(parent_probs[0][0], denomenator, parent_probs[0][0]/denomenator) + return parent_probs[0][0]/denomenator # Return the conditional probability + + + + +''' bayesian_table Documentation + --------------------------------------------- + This method inputs adjacency matrix, current node, boolean for midpoint calculation, list of node names, boolean for alternate probabilties, + array of new probabilities, and boolean for calculations among multiple turbines. We calculate the probability distribution and format it + into a table. This method outputs the list of parents and the probability distribution table.''' + +def bayesian_table(adj, current, midpoint, nodeNames, pvs = False, prob_vals=None, mult_turbine = False): + nodes = diagonal_nodes(adj) # Diagonal matrix of node's numerical names +1 + adj = make_binary(adj, 0.5) # Binarize adjacency matrix + + parents_bool = nodes @ adj[:, current-1] # vector of zeros and parent names (numerical names) + parents = list(parents_bool[np.nonzero(parents_bool)]) # list of just the parent names (numerical names) + prob_table = np.zeros((2 ** len(parents), len(parents) + 2)) # Initialize the array for the node's conditional probability distribution + + # *** Code in development for multiple turbine bayesian tables *** + arr_nonbinary = adj.copy() + num_parents = len(parents) + + '''if mult_turbine: + for p in range(num_parents): + if arr_nonbinary[parents[p] - 1][current - 1] > 1: + print("Unlocked!", parents[p]) + parents.append(parents[p])''' + # *** End of code in development ********************************* + + for iteration in range(2 ** len(parents)): # For each combination of parents having either failed or not + if pvs: # Determine the probabilities being used + probabilities = prob_vals.copy() + else: + probabilities = np.array([0.0195, 0.0195, 0.013625, 0.0055, 0.0175, 0.2075, 0.001, 0.001, 0.001, 0.093185, 0.001, 0.001, + 0.027310938, 0.033968125, 0.033968125, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, + 0.0205, 0.0205, 0.02, 0.01, 0.01, 0.233, 0.288, 0.543374, 0.1285, 0.01, 0.01, 0.01, 0.015, 0.0155, + 0.015, 0.0155, 0.015, 0.0155, 0.015, 0.33, 0.025, 0.025, 0.025, 0.025, 0.025, 0.105]) #0.01375, + probabilities = np.reshape(probabilities, (len(probabilities), 1)) + # print(probabilities) + + true_false_array = [] # Iniitalize to record which parents have failed + + for p in range(len(parents)): + probabilities[int(parents[p]-1)] = abs((int(iteration / (2 ** p)) % 2) - probabilities[int(parents[p]-1)]) # Determine parent probability (given if the parent has failed or not) + true_false_array.append((int(iteration / (2 ** p)) % 2)) # Append whether or not the parent node has failed + prob_table[iteration][p] = int(iteration / (2 ** p)) % 2 # Append whether or not the parent node has failed + + # if mult_turbine and nodeNames[int(current - 1)][:3] != nodeNames[int(parents[p] - 1)][:3]: #and p > 0) and parents[p] <= parents[p-1]: + # probabilities[int(parents[p]-1)] *= 0.7 # If there is more than one turbine, then decrease the probability of failure by 30% + prob = mult_cond_prob(parents, current, probabilities, midpoint) # Calculate the conditional probability of the node given if the parents have failed or not + # print(true_false_array, prob) + # print(probabilities) + prob_table[iteration][-2] = prob # Add calculated probability to the array + prob_table[iteration][-1] = 1 - prob # Add the probability of the node not failing to the array + #print(prob_table) + return parents, prob_table # Return a list of the parents and the probability distribution array + + + + +''' write_bayesian_probabilities Documentation + ---------------------------------------------- + This method inputs adjacency matrix, list of node names, probabilties, boolean for calculations among multiple turbines, + boolean for midpoint calculation, and number of iterations. We calculate the probability distribution for each node in the graph and format it + into a table. This method writes the probability distribution tables to an Excel file (nothing returned).''' + +def write_bayesian_probabilities(adjacency_matrix, nodeNames, probabilities, mult_turbine, midpoint=True, num_iterations=1, twoTurbines = False, name=""): + ps = probabilities.copy() + with pd.ExcelWriter("bayesianProbabilities"+name+".xlsx") as writer: # Write to file + for current in range(1, adjacency_matrix.shape[0]+1): # Repeat for each node in the graph + if any(probabilities != ps): + print("Unequal probabilities", current-1) + break + adjacency_matrix2 = adjacency_matrix.copy() + parents, our_table = bayesian_table(adjacency_matrix2, current, midpoint, nodeNames, True, probabilities, mult_turbine) # Probability distrubution table + if twoTurbines: + parents, our_table = twoTurbine_bayesian_table(adjacency_matrix, adjacency_matrix, current, nodeNames, nodeNames) # Probability distrubution table + parents = [int(parent) for parent in parents] # Format parent list + + # Find average probability distribution table for the number of iterations + for i in range(num_iterations - 1): + parents, our_table2 = bayesian_table(adjacency_matrix, current, midpoint, nodeNames = nodeNames, pvs=True, prob_vals=probabilities, mult_turbine=mult_turbine) + our_table += our_table2 + + # Update column titles for the probability distribution table + parents = [nodeNames[parent - 1].replace("\n", " ") for parent in parents] + parents = np.append(parents, "P(" + nodeNames[current - 1].replace("\n", " ") + " = True)") + parents = np.append(parents, "P(" + nodeNames[current - 1].replace("\n", " ") + " = False)") + df = pd.DataFrame(our_table, columns = parents) + + df.to_excel(writer, sheet_name=nodeNames[current - 1].replace("\n", " ").replace("/", " or ").replace(":", "")[:31]) # Write to file + print(current, nodeNames[current - 1].replace("\n", " ")[:31]) # Print current node to keep track of which nodes have a probability distribution table + + + + +''' probability_over_time Documentation + ---------------------------------------------- + This method inputs a failure rate, lamda, and a time, t. We return the probability of a failure at time t.''' + +def probability_over_time(lamda, t): + return 1 - math.exp((np.log(1-lamda)) * t) # Return the updated probability of failure given a certain time + + + + +''' bayesian_inference Documentation + ---------------------------------------------- + This method inputs adjacency matrix, list of node names, array of nodes to start with for generating the graph of the Bayesian network, array of + evidence nodes, array of hypothesis nodes, probabilties, boolean for finding hte probability of failure of the hypothesis, boolean for printing + information about the calculations, boolean for multiple turbines, parent or child calculation (string), and boolean for using the twoTurbine case + study method. We calculate Bayesian inference given the evidence and hypothesis variables. This method outputs an array of inference probabilities.''' + +def bayesian_inference(arr, nodeNames, indicator, num_evidence, num_hypothesis, probabilities, tf = True, printing = False, multi= False, poc="parent", twoTurbine = False): + # print("E", num_evidence) + # print("H", num_hypothesis) + a = arr.copy() + non = nodeNames + prblts = probabilities + evidence = num_evidence + hypothesis = num_hypothesis + if not multi: + K, a, g, e, m, non = breadth_first_multi(a, nodeNames, indicator, poc) # Generate tree for Bayesian network + # draw_bfs_multipartite(arr, nodeNames, indicator, type = "multi-"+poc, multi_turbine = False) + prblts = [] # Initialize array of node probabilities (in order of appearance in graph) + for node in non: + node_index = np.where(nodeNames == node)[0][0] + prblts.append(probabilities[node_index]) # Add nodes to array of node probabilities + prblts = np.array(prblts) + + evidence = [] + hypothesis = [] + for hypothesis_node in num_hypothesis: # Adjust hypothesis node so that index in tree matches up with index in original adjacency matrix + if twoTurbine: + non = np.array(non) + if nodeNames[hypothesis_node - 1] not in non: # If node is not in tree, then there is a 0% probability of failure + print("Probability table of", nodeNames[hypothesis_node - 1], "...", np.reshape(np.array([0,1]), (1,2))) + return np.reshape(np.array([0,1]), (1,2)), np.reshape(np.array([0,1]), (1,2)) + hypothesis.append(np.where(non == nodeNames[hypothesis_node - 1])[0][0]) + + for evidence_node in num_evidence: # Adjust evidence node so that index in tree matches up with index in original adjacency matrix + if twoTurbine: + non = np.array(non) + if nodeNames[evidence_node - 1] not in non: # If node is not in tree, then this is an error! + print("ERROR - evidence node, " + nodeNames[evidence_node - 1] + ", not in graph!") + if evidence_node in num_hypothesis:# If node is in hypothesis, then probability of failure is 100% + print("Probability table of", nodeNames[evidence_node - 1], "...", np.reshape(np.array([1,0]), (1,2))) + return np.reshape(np.array([1,0]), (1,2)), np.reshape(np.array([1,0]), (1,2)) + evidence.append(np.where(non == nodeNames[evidence_node - 1])[0][0]) + + probabilitiy_table = np.zeros((2, a.shape[0])) # Initialize table of inference probabilities + nodes = diagonal_nodes(a) # Diagonal matrix of node names (numerical +1) + a = make_binary(a, 0.5) # Binarize adjacency table + hypothesis_nodes_array = np.zeros((len(hypothesis), 2)) + + # Depending on tree (forward or backward propagation), either iterate through nodes forward or backwards + if poc == "parent": + this_range = reversed(range(a.shape[0])) + elif poc == "child": + this_range = range(a.shape[0]) + + for node in this_range: + pts_bool = nodes @ a[:, node] # vector of zeros and child names (numerical names) + pts = pts_bool[np.nonzero(pts_bool)] #list of just the child names (numerical names) + + if len(pts) < 1: # If no parents, the probability is the initial probability + probabilitiy_table[0][node] = prblts[node] + probabilitiy_table[1][node] = 1 - prblts[node] + continue + + parents, our_table = bayesian_table(a, node+1, True, nodeNames, True, prblts) # Calculate the probability distribution table + + # If only using two turbines (specific to case study), calculate using twoTurbine method + if twoTurbine: + parents, our_table = twoTurbine_bayesian_table(a, arr, node + 1, nodeNames, non) # Calculate the probability distribution table + mlt_table = np.ones((our_table.shape[0],2)) # Initialize table for multiplying across rows of probability distribution table + + for i in range(our_table.shape[0]): + for j in range(our_table.shape[1] - 2): + parent = int(parents[j]) + + # Replace boolean in probability distribution table with probability of this event + if our_table[i,j] == 0: + # print(a.shape, probabilitiy_table.shape) + our_table[i,j] = probabilitiy_table[0][parent - 1] + + # If the node is in the hypothesis or evidence array, do not include this + if probabilitiy_table[0][parent - 1] == 0: + # print("indexing_error!!", parent, node) + # print(non[parent], non[node]) + break + if (parent-1 in hypothesis and tf): # or (parent in evidence): + our_table[i,j] = 0 + # print("in hypto and tf or in evidence", parent) + else: + our_table[i,j] = probabilitiy_table[1][parent - 1] + + # If the node is in the hypothesis or evidence array, do not include this + if (parent-1 in hypothesis and not tf) or (parent-1 in evidence): + our_table[i,j] = 0 + # print("in hypto and not tf or in evidence", parent) + + mlt_table[i,0] *= our_table[i,j] # Multiply the probabilities across the probability distribution table + mlt_table[i,1] = mlt_table[i,0] * our_table[i, -1] # Multiple by the probability of event not failing given combination of parent failure + mlt_table[i,0] *= our_table[i, -2] # Multiple by the probability of event failing given combination of parent failure + + sm_table = np.sum(mlt_table, axis = 0) #/np.sum(mlt_table) # Sum the products of probabilities across the columns + + if node in hypothesis: + print("Probability table of", non[node].replace("\n", " "), "...", sm_table) + hypothesis_nodes_array[np.where(np.array(hypothesis) == node)[0][0]][0] = sm_table[0] + hypothesis_nodes_array[np.where(np.array(hypothesis) == node)[0][0]][1] = sm_table[1] + if all(hypothesis_nodes_array != 0): + return probabilitiy_table, hypothesis_nodes_array + + probabilitiy_table[0][node] = sm_table[0] # Update the inference probability table with the probabilites just calculated + probabilitiy_table[1][node] = sm_table[1] + + if printing: # Print the inference probability, along with the indicators, evidence, and hypothesis conditionals + print("Indicator:", nodeNames[np.array(indicator)]) + print("Evidence:", nodeNames[np.array(evidence)]) + print("Hypothesis:", nodeNames[np.array(hypothesis)]) + print("Probability of Failure:", probabilitiy_table[:, 0][0], probabilitiy_table[:, 0][0]/np.sum(probabilitiy_table[:, 0])) + print("Probability of No Failure:", probabilitiy_table[:, 0][1], probabilitiy_table[:, 0][1]/np.sum(probabilitiy_table[:, 0])) + return probabilitiy_table, hypothesis_nodes_array # Return array or inference probabilities + + + + +''' backward_bayesian_inference Documentation + ---------------------------------------------- + This method inputs adjacency matrix, list of node names, array of nodes to start with for generating the graph of the Bayesian network, array of + evidence nodes, array of hypothesis nodes, probabilties, boolean for indexing into the right values of the array, boolean for multiple turbines, parent or child + calculation type (string), and boolean for using twoTurbine case study method. We calculate Bayesian inference given the evidence and hypothesis variables. + This method outputs an array of inference probabilities (normalized and not).''' + +def backward_bayesian_inference(adjacency_matrix, nodeNames, indicator, evidence, hypothesis, probabilities, start_bool = True, multi = False, poc="parent", twoTurbine = False): + # Calculate Bayesian inference for hypothesis = True and hypothesis = False + #print("--- First Run of Bayesian Inference ----------------") + # print("Before bn", probabilities.shape) + printing = False + if len(evidence) > 0 and len(hypothesis)>0: + printing = False + + pt1, hnd1 = bayesian_inference(adjacency_matrix, nodeNames, indicator, evidence, hypothesis, probabilities, True, printing=printing, multi = multi, poc=poc, twoTurbine=twoTurbine) + #print() + #print("--- Second Run of Bayesian Inference ---------------") + if poc == "parent": + pt2, hnd2 = bayesian_inference(adjacency_matrix, nodeNames, indicator, evidence, hypothesis, probabilities, False, printing=printing, multi = multi, poc=poc, twoTurbine=twoTurbine) + # print("pt1", pt1[:, 0]) + # print("pt2", pt2[:, 0]) + # Choose correct indexing value + index = 1 + if start_bool: + index = 0 + + if multi: + return [hnd1[0][0]/(hnd1[0][0]+hnd1[0][1]), hnd1[0][1]/(hnd1[0][0]+hnd1[0][1])], [0,0] + + # Depending on what our evidence and hypothesis nodes are, index into the correct values + if poc == "parent" and 0 in evidence: + p_true = pt1[:, 0][index] + p_false = pt2[:, 0][index] + + elif poc == "parent" and 0 in hypothesis: + p_true = pt1[:, 0][0] + pt2[:, 0][0] + p_false = pt1[:, 0][1] + pt2[:, 0][1] + + elif poc == "child": + if hnd1.shape[0] == 1: + p_true = hnd1[0][0] + p_false = hnd1[0][1] + + else: # If multiple nodes in hypothesis, multiply all possibilities together to get normailized value + print("Starting hypothesis for-loops...") + p_false = 0 + for iteration in range(2 ** hnd1.shape[0]): + p_false_mult = 1 + for node in range(hnd1.shape[0]): + p_false_mult *= hnd1[node][int(iteration / (2 ** node)) % 2] + if iteration == 0: + p_true = p_false_mult + else: + p_false += p_false_mult + if (iteration) % 1000000000000 == 0: + print("Percent done:", iteration/(2 ** hnd1.shape[0]) * 100) + print("Percent done:", 100) + else: + p_true = np.sum(pt1[:, 0]) + p_false = np.sum(pt2[:, 0]) + + probability_distribution = [p_true/(p_true + p_false), p_false/(p_true + p_false)] # Normalize the probability distribution of the event happening + + return probability_distribution, [p_true, p_false] # Return the normalized probability distribution and unnormalized distribution + + + +''' +# ----------- For running the code: feel free to uncomment ------------------- +arr, nodeNames = excel2Matrix("failureData.xlsx", "bigMatrix") +#random.seed(18) +start = 30 #np.random.randint(0, arr.shape[0]) +probabilities = np.array([0.0195, 0.0195, 0.013625, 0.0055, 0.0175, 0.2075, 0.001, 0.001, 0.001, 0.093185, 0.001, 0.001, + 0.027310938, 0.033968125, 0.033968125, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, + 0.0205, 0.0205, 0.02, 0.01, 0.01, 0.233, 0.288, 0.543374, 0.1285, 0.01, 0.01, 0.01, 0.015, 0.0155, + 0.015, 0.0155, 0.015, 0.0155, 0.015, 0.33, 0.025, 0.025, 0.025, 0.025, 0.025, 0.105]) #0.01375, +probabilities = np.reshape(probabilities, (arr.shape[0], 1)) +start = 44 #np.random.randint(1, arr.shape[0]+1) +single_simulation(start, arr, nodeNames) +single_simulation(start, arr, nodeNames, update=True) + +num_iterations = 100 +plot = True +start = 11 #np.random.randint(1, arr.shape[0]+1) +monte_carlo_sim(num_iterations, plot, start, adjacency_matrix, nodeNames, rand_seed=False, mid_point=False)''' + + + + +'''# ----------- For running the code: feel free to uncomment ------------------- +adjacency_matrix, nodeNames = excel2Matrix("ExcelFiles/failureData.xlsx", "bigMatrix") +probabilities = np.array([0.0195, 0.0195, 0.013625, 0.0055, 0.0175, 0.2075, 0.001, 0.001, 0.001, 0.093185, 0.001, 0.001, + 0.027310938, 0.033968125, 0.033968125, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, + 0.0205, 0.0205, 0.02, 0.01, 0.01, 0.233, 0.288, 0.543374, 0.1285, 0.01, 0.01, 0.01, 0.015, 0.0155, + 0.015, 0.0155, 0.015, 0.0155, 0.015, 0.33, 0.025, 0.025, 0.025, 0.025, 0.025, 0.105]) #0.01375, +probabilities = np.reshape(probabilities, (adjacency_matrix.shape[0], 1)) +targets = [44] +starts = [16, 33, 35, 36] + +# C, D = bayesian_table(adjacency_matrix, 16, True, pvs = True, prob_vals=probabilities, mult_turbine = False) +# A, B = backward_bayesian_inference(adjacency_matrix, nodeNames, [0], [0], [44], probabilities, start_bool = True) +# print(B) +write_bayesian_probabilities(adjacency_matrix, nodeNames, probabilities, mult_turbine = True, midpoint = True, num_iterations=1)''' \ No newline at end of file diff --git a/graphBuilder.py b/graphBuilder.py new file mode 100644 index 00000000..451bb184 --- /dev/null +++ b/graphBuilder.py @@ -0,0 +1,488 @@ +import pandas as pd +import numpy as np +import networkx as nx +import matplotlib.pyplot as plt + +''' graphBuilder--------------------------------------------------------------------------------------------------- + + ****************************** Last Updated: 19 February 2024 ****************************** + + Methods: + 1) excel2Matrix: inputs fileName, sheetName, boolean of nodeLabels --> output adjacency matrix and name array + + 2) matrix2Graph: inputs adjacency matrix, name of nodes, boolean of nodeLabels --> output graph and adjacency + matrix + + 3) export2graphml: inputs G, string (file name) --> writes graphML file with inputted file name (no return) + + 4) get_node_dict: inputs adjacency matrix and name of nodes --> outputs array containing dictionaries with each + entry containing the node's name, number, and whether it is an effect or mode. + + 5) get_node_array: inputs adjacency matrix and name of nodes --> outputs array with dictionary entries of the + names of the nodes + + 6) get_graph_edges: inputs adjacency matrix, string indicating type of output desired, and threshold --> outputs + an array of edges + + 7) make_binary: inputs adjacency matrix and float type --> outputs binarized adjacency matrix + + 8) plot_graph: inputs graph and type (str) --> no output (prints graph) + + 9) max_degrees: inputs adjacency matrix, names of nodes, threshold for binarization of adjacency matrix, boolean + for showing names --> outputs tuples of max out degree, max in degree, and max overall degree with the nodes that + have these max degrees + +10) getProbabilities: inputs fileName, sheetName, boolean of nodeLabels --> output probabilities and name array + +11) diagonal_nodes: inputs adjacency matrix --> outputs nodes diagonal matrix + +12) cosine_similarity: inputs vector one and vector two --> outputs cosine similarity of vector one and two + +13) make_undirected_unweighted: inputs adjacency matrix and (optional) threshold --> outputs undirected, unweighted + version of the adjacency matrix + +------------------------------------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------------------------------------ + + + excel2Matrix Documentation + ------------------------------- + This code is meant to take in an excel file (already properly formatted by theh user) and output the corresponding + graph. This assumes that the contents of the excel file is an adjacency matrix. The sheetName input identifies which + sheet in the excel file the adjacency matrix is in. The nodeLabels input lets the user choose if they would like + words (input TRUE) or numeric (input FALSE) labels on the graph. The plot input lets the user toggle on/off plotting + of the graph. Finally, the graph is returned.''' + +def excel2Matrix(fileName, sheetName = None): + # Read in excel file as a dataframe + df = pd.read_excel(fileName, sheet_name=sheetName) + + # Convert the data frame to a numpy array + arr = df.to_numpy() + + # Create an array of the names of the nodes for the graph. + nodeNames = arr[:, 0].flatten() + + # return the adjacency matrix without labels and an array of the names of the nodes + return arr[:, 1:], nodeNames + + + +''' matrix2Graph Documentation + ------------------------------- + This method takes in an adjacency matrix, the names of the nodes, whether or not to display the node names, and + whether or not to display the plot of the graph once constructed. The output is the graph and adjacency matrix.''' + +def matrix2Graph(arr, nodeNames, nodeLabels = False): + # Create the graph (DiGraph because the graph is directed and weighted) and add the nodes selected above + G = nx.DiGraph() + if not nodeLabels: + nodeNames = range(arr.shape[0]) + G.add_nodes_from(nodeNames) + + # The following nested for loops find the edges of the graph and add them to the graph using the 'add_weighted_edges_from()' + # command. 'i' indexes through the rows and 'j' indexes through the columns. If there is no edge (i.e. 0 in the adjacency + # matrix), then move on to the next set of nodes. Otherwise, add the edge to the graph. + for i in range(arr.shape[0]): + for j in range(arr.shape[1]): + if arr[i,j] == 0: + continue + else: + if nodeLabels: G.add_weighted_edges_from([(nodeNames[i], nodeNames[j], arr[i,j])]) + else: G.add_weighted_edges_from([(i, j, arr[i,j])]) + + # Return the graph and adjacency matrix + return G, arr + + + +''' export2graphML Documentation + ------------------------------- + This method takes in a networkx graph and ouputs a graphML file.''' + +def export2graphML(G, name): + + # create a name for the graphML file that you are going to write + filename = name + "_FOWT_graphML.graphml" + + # wrtie the graphML file using networkx methods + nx.write_graphml(G, filename, encoding='utf-8', prettyprint=True, infer_numeric_types=True, named_key_ids=True, edge_id_from_attribute=None) + return + + + +''' get_node_dict Documentation + ------------------------------- + This method takes in the adjacency matrix and the names of all the nodes. It outputs an array of node dictionaries. + Each key/value pair contains the node's number, name, and 'class_id' (which refers to if the node represents a + failure effect or failure mode).''' + +def get_node_dict(arr, nodeNames): + # Initialize array for the nodes + nodes = [] + + # Iterate through each node number + for index in range(arr.shape[0]): + + # If the number is less than 26 (value chosen based on excel sheets), then the node is a failure effect. + if index < 26: + class_id = 'effect' + + # Otherwise, the node must be a failure mode. + else: + class_id = 'mode' + + # Add a dictionary with the node's number, name, selection criteria (in this case they can all be selected), + # and class_id (whether they are an effect or mode). + nodes.append({ + 'data': {'id': str(index), 'label': nodeNames[index]}, + 'selectable': True, + 'classes': class_id + }) + + # Return the array of dictionaries for the nodes + return nodes + + + +''' get_node_array Documentation + ------------------------------- + This method takes in an adjacency matrix and list of node names. It outputs an array with each entry a + key/value pair. Each key is the string 'label', but the values are the string names of the nodes.''' + +def get_node_array(arr, nodeNames): + # Initialize an array for the nodes + nodes = [] + + # Iterate through each node number + for index in range(arr.shape[0]): + + # For every node, locate its name and append a dictionary with 'label' as the only key and + # the name just acquired as the only value. + nodes.append({'label': nodeNames[index]}) + + # Return the array of nodes + return nodes + + +''' get_grpah_edges Documentation + ------------------------------- + This method takes in an adjacency matrix (array type) and a string indicating the type of edge list + desired. This method outputs an array of all the edges. If the 'dictionary' type is indicated, then each + entry in the array is formatted as + {'data': {'id': name_of_source_and_target, 'source' name_of_source, 'target', name_of_target}}. + If the 'array' type is indicated, then each entry in the array is formatted as + [source, target]). + Lastly, if the 'tuple' type is indicated, then each entry in the array is formatted as + (source, target).''' + +def get_graph_edges(arr, type, threshold): + # Initialize an array for the edges + edges = [] + + # Iterate through each row of the adjacency matrix + for i in range(arr.shape[0]): + + # For each row, iterate through each column of the matrix + for j in range(arr.shape[1]): + + # If the entry in the adjacency matrix is greater than 0,5 (can change this threshold), then + # add an edge to the array of edges. Each edge is added by appending an array containing the + # source and target. + if arr[i, j] > threshold: + if type == 'dict': + edges.append({'data': {'id': str(i)+str(j), 'source': str(i), 'target': str(j)}, + 'classes': 'red'}) + elif type == 'array': + edges.append([i,j]) + elif type == 'tuple': + edges.append((i,j)) + + # Otherwise, if the input does not indicate an edge, move on to the nect entry in the + # adjacency matrix + else: + continue + + # Return the array of edges + return edges + + + +''' make_binary Documentation + ------------------------------- + This method takes in an adjacency matrix (array typle) and a threshold value (float type). It outputs a + new adjacency matrix such that all the entries are either 0 or 1.''' + +def make_binary(arr, threshold = 0): + # Initialize the new adjacency matrix by setting it equal to the original adjacency matrix + arr_altered = arr + + # Iterate through each row of the matrix + for i in range(0, arr_altered.shape[0]): + + # In each row, iterate through each column of the matrix + for j in range(arr_altered.shape[0]): + + # If the entry is greater than the indicated threshold, set the entry of the new adjacency + # matrix equal to 1 + if arr[i,j] > threshold: + arr_altered[i,j] = 1 + + # Otherwise, set the entry equal to 0 + else: + arr_altered[i,j] = 0 + + # Return the new adjacency matrix + return arr_altered + + + +''' plot_graph Documentation + ------------------------------- + This method takes in a plot and type of plot desired, then prints a plot of the graph.''' + +def plot_graph(G, type, effects_mark, nodeNames): + + # This type places the failure effects on one side and the failure modes on the other in vertical columns + if type == "bipartite": + pos = nx.bipartite_layout(G, nodeNames[:effects_mark]) # positions for all nodes + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98", edgecolors="#c89679", node_shape="s") + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed", edgecolors="#799dbd", node_shape="s") + + # This for loop places labels outside of the nodes for easier visualization + for i in pos: + x, y = pos[i] + if x <= 0: + plt.text(x-0.075,y,s=i, horizontalalignment='right') + + else: + plt.text(x+0.075,y,s=i, horizontalalignment='left') + + #nx.draw_networkx_labels(G, pos, horizontalalignment='center') + nx.draw_networkx_edges(G, pos) + plt.box(False) + plt.show() + return + + # This type draws all the nodes in a circle + elif type == "circular": + pos = nx.circular_layout(G) # positions for all nodes + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") + nx.draw_networkx_labels(G, pos) + nx.draw_networkx_edges(G, pos) + plt.box(False) + plt.show() + return + + # This type attempts to have all the edges the same length + elif type == "kamada_kawai": + pos = nx.kamada_kawai_layout(G) # positions for all nodes + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") + nx.draw_networkx_labels(G, pos) + nx.draw_networkx_edges(G, pos) + plt.box(False) + plt.show() + return + + # This type attempts to have non-intersecting edges. This only works for planar graphs. + elif type == "planar": + pos = nx.planar_layout(G) # positions for all nodes + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") + nx.draw_networkx_labels(G, pos) + nx.draw_networkx_edges(G, pos) + plt.box(False) + plt.show() + return + + # This type plots the graph in a random configuration + elif type == "random": + pos = nx.random_layout(G) # positions for all nodes + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") + nx.draw_networkx_labels(G, pos) + nx.draw_networkx_edges(G, pos) + plt.box(False) + plt.show() + return + + # This type uses the eigenvectrs of the graph Laplacian in order to show possible clusters of nodes + elif type == "spectral": + pos = nx.spectral_layout(G) # positions for all nodes + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") + nx.draw_networkx_labels(G, pos) + nx.draw_networkx_edges(G, pos) + plt.box(False) + plt.show() + return + + # This type treats nodes as repelling objects and edges as springs (causing them to moving in simulation) + elif type == "spring": + pos = nx.spring_layout(G) # positions for all nodes + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") + nx.draw_networkx_labels(G, pos) + nx.draw_networkx_edges(G, pos) + plt.box(False) + plt.show() + return + + # This type places nodes in concentric circles + elif type == "shell": + pos = nx.shell_layout(G) # positions for all nodes + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") + nx.draw_networkx_labels(G, pos) + nx.draw_networkx_edges(G, pos) + plt.box(False) + plt.show() + return + + # This type places nodes in spiral layout + elif type == "spiral": + pos = nx.spiral_layout(G) # positions for all nodes + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") + nx.draw_networkx_labels(G, pos) + nx.draw_networkx_edges(G, pos) + plt.box(False) + plt.show() + return + + # This type places nodes in spiral layout + elif type == "multipartite": + + pos = nx.multipartite_layout(G) # positions for all nodes + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") + nx.draw_networkx_labels(G, pos) + nx.draw_networkx_edges(G, pos) + plt.box(False) + plt.show() + return + + # Plot the grpah with networkx and matplotlib using regular algorithm + nx.draw(G, with_labels=True, font_weight='bold') + plt.show() + return + + + +''' max_degrees Documentation + -------------------------------- + This method takes in an adjacency matrix, the names of the nodes, a threshold for binarization + of the adjacency matrix, and a boolean for labeling the nodes with names (True) or numbers (False). + The output consists of three tuples: + 1. (the nodes with the highest out degree, value of the highest out degree) + 1. (the nodes with the highest in degree, value of the highest in degree) + 1. (the nodes with the highest overall degree, value of the highest overall degree)''' + +def max_degrees(arr, nodeNames, threshold = 0, name = False): + + # Create copy of the adjacency matrix so that we can alter it without losing information about + # our original adjacency matrix. + arr_altered = arr + + # Binarization of adjacency matrix. The threshold determines the cutoff for what will be labeled + # as a 1 versus a 0. Anything above the threshold will be a 1, and anything below the threshold + # will be set to 0. + for i in range(0, arr_altered.shape[0]): + for j in range(arr_altered.shape[0]): + if arr[i,j] > threshold: + arr_altered[i,j] = 1 + else: + arr_altered[i,j] = 0 + + # Calculating out degrees and the maximum of the out degrees + out_degrees = np.sum(arr_altered, axis=1) + max_out = np.where(out_degrees == max(out_degrees)) + + # Calculating in degrees and the maximum of the in degrees + in_degrees = np.sum(arr_altered, axis=0) + max_in = np.where(in_degrees == max(in_degrees)) + + # Calculating overall degrees and the maximum of the overall degrees + degrees = out_degrees + in_degrees + max_deg = np.where(degrees == max(degrees)) + + # If the user chooses to list the nodes by their proper name (rather than with numbers), then + # we index into the array of node names, nodeNames, to find the names of these nodes. We then + # return the three tuples about maximum out degree, in degree, and overall degree. + if name: + out_name = nodeNames[max_out[0].tolist()] + in_name = nodeNames[max_in[0].tolist()] + deg_name = nodeNames[max_deg[0].tolist()] + return (out_name, max(out_degrees)), (in_name, max(in_degrees)), (deg_name, max(degrees)) + + # Otherwise, if the user chooses not to label nodes with their proper names, then we return the + # three tuples about maximum out degree, in degree, and overall degree. Rather than listing the + # names of the nodes, their corresponding numbers are listed. + return (max_out, max(out_degrees)), (max_in, max(in_degrees)), (max_deg, max(degrees)) + + + +''' getProbabilities Documentation + ------------------------------- + This method inputs an Excel file and reads the probabilities from the file. We return an array of these + probabilities and an array of the node names (indexed the same as the probability array).''' + +def getProbabilities(fileName, sheetName = None): + df = pd.read_excel(fileName, sheet_name=sheetName) + + # Convert the data frame to a numpy array + arr = df.to_numpy() + + # Create an array of the names of the nodes for the graph. + nodeNames = arr[:, 0].flatten() + + # return the adjacency matrix without labels and an array of the names of the nodes + return arr[:, 1], nodeNames + + +'''diagonal_nodes Documentation + -------------------------------- + This method takes in an adjacency matrix and outputs a diagonal matrix. For an adjacency matrix of length k, the + outputted diagonal matrix places values 1-k on the diagonal.''' + +def diagonal_nodes(arr): + # Return the diagonal matrix with i+1 in the i,i spot and zero elsewhere + return np.diag([i+1 for i in range(arr.shape[0])]) + +'''cosine_similarity Documentation + ----------------------------------- + This method takes in two vectors and outputs the cosine similarity between them. In linear algebra, cosine + similarity is defined as the measure of how much two vectors are pointing in the same direction. It can also + be thought of cosine of the angle between the two vectors.''' + +def cosine_similarity(v1, v2): + # Find the dot product between the two vectors inputted + dot_prod = np.transpose(v1) @ v2 + + # Calculate and return the cosine similarity of the two vectors + return (dot_prod) / ((np.sqrt(np.sum(np.square(v1))) * np.sqrt(np.sum(np.square(v2))))) + + + +''' make_undirected_unweighted Documentation + --------------------------------------------- + This method takes in an adjacency matrix and threshold. It outputs an adjusted adjacency matrix that + corresponds to the undirected and unweighted graph of the one inputted.''' + +def make_undirected_unweighted(arr, threshold = 0): + # Make sure that the array is a numpy array + arr = np.array(arr) + + # Add the array to the transpose of itself. This makes the graph undirected. + arr = arr + arr.T + + # Use the method from graphBuilder.py to binarize the matrix. This makes the graph unweighted. + make_binary(arr, threshold) + + # Since the array now only consists of zeros and ones, make sure that it is of integer type + arr = arr.astype(int) + + # Return the adjusted matrix + return arr \ No newline at end of file diff --git a/searchMethods.py b/searchMethods.py new file mode 100644 index 00000000..11b3ffe9 --- /dev/null +++ b/searchMethods.py @@ -0,0 +1,429 @@ +import numpy as np +import networkx as nx +import matplotlib.pyplot as plt +from graphBuilder import * + +''' searchMethods-------------------------------------------------------------------------------------------------- + + ****************************** Last Updated: 10 April 2024 ****************************** + + Methods: + 1) breadth_first_child: inputs adjacency matrix, names of nodes, start node --> outputs tree, adjacency matrix, + dictionary of nodes and generations, list of 'effect' nodes, list of 'mode' nodes + + 2) breadth_first_parent: inputs adjacency matrix, names of nodes, start node --> outputs tree, adjacency matrix, + dictionary of nodes and generations, list of 'effect' nodes, list of 'mode' nodes + + 3) draw_bfs_multipartite: nputs adjacency matrix, list of node names, start node --> plots breadth first tree. + Nothing is returned. + + 4) breadth_first_multi: adjacency matrix, list of node names, starting node, type of breadth first search --> + outputs tree, adjacency matrix, dictionary of nodes, arrays for effects and modes, and array of nodes + +-------------------------------------------------------------------------------------------------------------------- +-------------------------------------------------------------------------------------------------------------------- + + + breadth_first_child Documentation + ----------------------------------- + This method takes in an adjacency matrix, list of node names, and an integer indicating te starting node. We + traverse through the graph. For each generation, starting from the source node, we find and add all the children + of the nodes in this generation, unless the node has aldready been placed in the tree. As such, we create a tree. + This method returns this tree, the tree's adjacency matrix, a dictionary of nodes, and arrays for effects and modes. + + --> Note: a tree is a graph with no cycles or loops. It does not have to be directed, but it is acyclic.''' + +def breadth_first_child(arr, nodeNames, source): + # Initialize a new digraph for the tree we are going to make and add all the nodes (we will determine edges later) + G = nx.DiGraph() + + # Initialize effects and modes arrays for plotting purposes + effects = [] + modes = [] + + # Add the source node to the graph + G.add_node(nodeNames[source - 1]) + + # Determine if the source node is an effect or a mode, and add it to the correct array + if source < 27: effects.append(nodeNames[source - 1]) + elif source <= 47: modes.append(nodeNames[source - 1]) + + '''if source < 49: modes.append(nodeNames[source - 1]) + else: effects.append(nodeNames[source - 1])''' + + # Note that we are changing the numerical names of the nodes so that the values of nodes are from 1 to 47 rather + # than from 0 to 46. This is so we can find all the children of the nodes. Using the 0-46 range poses a problem + # since we do not know if a zero indicates no relation or that the 0 node is a child of the node we are looking at. + # We binarize the adjacency matrix so that relationships either exist or not (rather than having an intensity) + adj = make_binary(arr, 0.5).astype(int) + + # Create an array of booleans such that the ith entry indicates if the node has already been visited. Nodes + # that have been visited are set to TRUE. + nodeList = np.reshape(np.repeat(False, arr.shape[0]), (arr.shape[0], 1)) + + # Create a diagonal matrix such that the value of the i,i entry is 1+1, referencing the node with name i+1 + nodes = diagonal_nodes(adj) + # print("nodes", nodes.shape) + + # Visit the source node + nodeList[source-1] = True + + # Add the source node to the queue. We use a queue to iterate through the nodes. This allows us to search through + # the graph generation by generation rather than following one specific path at a time. + queue = [[source, 0]] + + # Initialize a dictionary that will tell us what generation each node is in. Label the generation of the source node + # as zero. + gens = {nodeNames[source-1]: {"layer": 0}} + + # Continue while there are still nodes in the queue (reference algorithms for a breadth first search for more info) + while len(queue) > 0: + # Set the node we are looking at equal to the node next in line in the queue + current = queue[0] + + # Determine the children of the current node. + # Proof/Reason Why This Works --> Using the unweighted adjacency matrix, we can find the children + # by multiplying the row of the adjacency matrix corresponding to the current node by the diagonal matrix of + # node names. If the jth node is a child of the current node, then there is a 1 in the j-1 column of the + # current row. When multiplied by the diagonal matrix, this 1 will be multiplied by the j-1 column of the. + # since the only non zero entry in the j-1 column of the diagonal matrix is j (located on the diagonal), then + # the entry in the j-1 column of the returned vector will be j. However, if the jth node is not a child of + # the current node, then there is a zero in the j-1 column of the current row of the adjacency matrix. When + # multiplied by the diagonal matrix, this zero is multiplied by every entry in the j-1 column of the diagonal + # matrix. So, the j-1 column of the returned vector is zero. + + children_bool = adj[current[0]-1] @ nodes # vector of zeros and child names (numerical names) + children = children_bool[np.nonzero(children_bool)] #list of just the child names (numerical names) + + for child in children: # For every child of the current node that was found above... + # Check if the child has been visited or if it is in the same generation as child. If either not visited or + # in the same generation, continue with the following: + if nodeList[child - 1] == False or gens[nodeNames[child - 1]]['layer'] > current[1]: + + # Add the child to the graph + G.add_node(nodeNames[child-1]) + + # Determine if the child is an effect or mode and add it to the correct array + if child < 27: effects.append(nodeNames[child - 1]) + elif child <= 47: modes.append(nodeNames[child - 1]) + '''if child < 49: modes.append(nodeNames[child - 1]) + else: effects.append(nodeNames[child - 1])''' + + # Add an edge between the current node and child in the tree we are building + G.add_edge(nodeNames[current[0]-1], nodeNames[child-1]) + + queue.append([child, current[1]+1]) # Append the child to the queue + nodeList[child - 1] = True # Change the status of the child to say we have visited it + gens.update({nodeNames[child-1]: {"layer": current[1]+1}}) # Add child to dictionary of nodes + + # Remove the current node from the queue + queue = queue[1:] + + # Return the tree we made, its adjacency matrix, the dictionary of nodes, the effects array, and the modes arrat + return G, nx.to_numpy_array(G), gens, effects, modes + + + +''' breadth_first_parent Documentation + --------------------------------------- + This method takes in an adjacency matrix, list of node names, and an integer indicating te starting node. We + traverse through the graph. For each generation, starting from the source node, we find and add all the parents + of the nodes in this generation, unless the node has aldready been placed in the tree. As such, we create a tree. + This method returns this tree, the tree's adjacency matrix, a dictionary of nodes, and arrays for effects and modes.''' + +def breadth_first_parent(arr, nodeNames, source): + # Initialize a new digraph for the tree we are going to make and add all the nodes (we will determine edges later) + # Initialize a graph in Networkx + G = nx.DiGraph() + + # Initialize effects and modes arrays for plotting purposes + effects = [] + modes = [] + names_of_nodes = [] + + # Add the source node to the graph + G.add_node(nodeNames[source - 1]) + names_of_nodes.append(nodeNames[source - 1]) + + # Determine if the source node is an effect or a mode, and add it to the correct array + '''if source < 27: effects.append(nodeNames[source - 1]) + else: modes.append(nodeNames[source - 1])''' + if source < 49: modes.append(nodeNames[source - 1]) + else: effects.append(nodeNames[source - 1]) + + # Binarize the adjacency matrix + arr2 = arr.copy() + adj = make_binary(arr2).astype(int) + + # Create an array of booleans such that the ith entry indicates if the node has already been visited. Nodes + # that have been visited are set to TRUE. + nodeList = np.reshape(np.repeat(False, arr.shape[0]), (arr.shape[0], 1)) + + # Create a diagonal matrix such that the value of the i,i entry is 1+1, referencing the node with name i+1 + nodes = diagonal_nodes(adj) + + # Visit the source node + nodeList[source-1] = True + + # Add the source node to the queue. We use a queue to iterate through the nodes. This allows us to search through + # the graph generation by generation rather than following one specific path at a time. + queue = [[source, 100]] + + # Initialize a dictionary that will tell us what generation each node is in. Label the generation of the source node + # as zero. + gens = {nodeNames[source-1]: {"layer": 100}} + + # Continue while there are still nodes in the queue (reference algorithms for a breadth first search for more info) + while len(queue) > 0: + # Set the node we are looking at equal to the node next in line in the queue + current = queue[0] + + # Determine the parents of the current node. + parents_bool = nodes @ adj[:, current[0]-1] # vector of zeros and parent names (numerical names) + parents = parents_bool[np.nonzero(parents_bool)] #list of just the parent names (numerical names) + + for parent in parents: # For every child of the current node that was found above... + # Check if the parent has been visited or if it is in the same generation as parent. If either not visited or + # in the same generation, continue with the following: + if nodeList[parent - 1] == False or gens[nodeNames[parent - 1]]['layer'] < current[1]: + + # Add the parent to the graph + G.add_node(nodeNames[parent-1]) + + # Determine if the parent is an effect or mode and add it to the correct array + if parent < 27: effects.append(nodeNames[parent- 1]) + else: modes.append(nodeNames[parent - 1]) + '''if parent < 49: modes.append(nodeNames[parent- 1]) + else: effects.append(nodeNames[parent - 1])''' + + # Add an edge between the current node and parent in the tree we are building + G.add_edge(nodeNames[parent-1], nodeNames[current[0]-1]) + + queue.append([parent, current[1]-1]) # Append the parent to the queue + nodeList[parent - 1] = True # Change the status of the parent to say we have visited it + gens.update({nodeNames[parent-1]: {"layer": current[1]-1}}) # Add parent to dictionary of nodes + + # Remove the current node from the queue + queue = queue[1:] + + # Return the tree we made, its adjacency matrix, the dictionary of nodes, the effects array, and the modes arrat + return G, nx.to_numpy_array(G), gens, effects, modes + + + +''' draw_bfs_multipartite Documentation + --------------------------------------- + This method inputs an adjacency matrix, list of node names, and a start node. We use the breadth first searh to + find generations of nodes starting from the start node. This method plots the resulting graph from the breadth + first search. Nothing is returned.''' + +def draw_bfs_multipartite(arr, nodeNames, start, type = "child", multi_turbine = False): + # Obtain the graph, adjacency matrix, dictionary, effects, and modes from breadth_first_tree + if type == "child": + G, arr, gens, effects, modes = breadth_first_child(arr, nodeNames, start) + elif type == "parent": + G, arr, gens, effects, modes = breadth_first_parent(arr, nodeNames, start) + elif type == "multi-child": + G, arr, gens, effects, modes, non = breadth_first_multi(arr, nodeNames, start, "child") + else: + G, arr, gens, effects, modes, non = breadth_first_multi(arr, nodeNames, start, "parent") + + # Give each node the attribute of their generation + nx.set_node_attributes(G, gens) + # print(effects) + + if multi_turbine: + effect_colors = ["#ffd6ed", "#ffb3ba", "#ffdfba", "#ffffba", "#baffc9", "#bae1ff", "#b1adff", "#e4adff", "#e5e5e5", "#e8d9c5"] + mode_colors = ["#e5c0d5", "#e5a1a7", "#e5c8a7", "#e5e5a7", "#a7e5b4", "#a7cae5", "#9f9be5", "#cd9be5", "#cecece", "#d0c3b1"] + pos = nx.multipartite_layout(G, subset_key='layer') + for node in G.nodes: + # print(int(str(node)[0])) + if str(node) in effects: + nx.draw_networkx_nodes(G, pos, nodelist=[node], node_color = effect_colors[int(str(node)[0])], node_size=750, node_shape="s") + else: + nx.draw_networkx_nodes(G, pos, nodelist=[node], node_color = effect_colors[int(str(node)[0])], node_size=750) + nx.draw_networkx_labels(G, pos, font_size=5, verticalalignment='center_baseline') + nx.draw_networkx_edges(G, pos, arrowsize=20) + plt.box(False) + plt.show() + else: + # Plot the graph + pos = nx.multipartite_layout(G, subset_key='layer') + nx.draw_networkx_nodes(G, pos, nodelist=effects, node_color="#98c5ed", node_size=2700, edgecolors="#799dbd", node_shape="s") + nx.draw_networkx_nodes(G, pos, nodelist=modes, node_color="#fabc98", node_size=2700, edgecolors="#c89679", node_shape="s") + nx.draw_networkx_labels(G, pos, font_size=10, verticalalignment='center_baseline') + nx.draw_networkx_edges(G, pos, arrowsize=60) + plt.box(False) + plt.show() + + # Nothing is returned + return + + + +'''breadth_first_multi Documentation + ----------------------------------- + This method takes in an adjacency matrix, list of node names, an integer indicating te starting node, and a string + that indicates which type of breadth first search we are conducting (child or parent). We traverse through the graph. + For each generation, starting from the source nodes (handles multiple start nodes), we find and add all the children/parents + of the nodes in this generation, unless the node has aldready been placed in the tree. As such, we create a tree. + This method returns this tree, the tree's adjacency matrix, a dictionary of nodes, arrays for effects and modes, and array + of nodes (in the order they are added).''' + +def breadth_first_multi(arr, nodeNames, sources, type_poc): + # Function that determines how value of the layer changes, depending on the type of calculation + def layer_fcn(layer, type_poc): + if type_poc == "child": + return layer + 1 + elif type_poc == "parent": + return layer - 1 + # Determining if the current generation is after the former node's generation, depending on the type of calculation + def same_layer(layer1, layer2, type_poc): + if type_poc == "child": + if layer1 > layer2: + return True + else: + return False + elif type_poc == "parent": + if layer1 < layer2: + return True + else: + return False + + # Initialize a new digraph for the tree we are going to make and add all the nodes (we will determine edges later) + G = nx.DiGraph() + + # Initialize arrays for tracking nodes + effects = [] + modes = [] + names_of_nodes = [] + adj = make_binary(arr).astype(int) + nodeList = np.reshape(np.repeat(False, arr.shape[0]), (arr.shape[0], 1)) + nodes = diagonal_nodes(adj) + queue = [] + gens = {} + + # Initialize value for the first layer created + if type_poc == "child": + layer_val = 0 + else: + layer_val = 100 + + # Add the source node to the graph + for start in sources: + G.add_node(nodeNames[start - 1]) + + # Determine if the source node is an effect or a mode, and add it to the correct array + if start < 27: effects.append(nodeNames[start - 1]) + else: modes.append(nodeNames[start - 1]) + + '''if start < 49: modes.append(nodeNames[start - 1]) + else: effects.append(nodeNames[start - 1])''' + + # Visit the source node + nodeList[start-1] = True + names_of_nodes.append(nodeNames[start - 1]) + + # Add the source node to the queue. We use a queue to iterate through the nodes. This allows us to search through + # the graph generation by generation rather than following one specific path at a time. + queue.append([start, 0]) + + # Initialize a dictionary that will tell us what generation each node is in. Label the generation of the source node + # as zero. + gens.update({nodeNames[start-1]: {"layer": layer_val}}) + # print("non", names_of_nodes) + + # Continue while there are still nodes in the queue (reference algorithms for a breadth first search for more info) + while len(queue) > 0: + # Set the node we are looking at equal to the node next in line in the queue + current = queue[0] + + if type_poc == "child": + # Determine the children of the current node. + children_bool = adj[current[0]-1] @ nodes # vector of zeros and child names (numerical names) + children = children_bool[np.nonzero(children_bool)] #list of just the child names (numerical names) + else: + children_bool = nodes @ adj[:, current[0]-1] # vector of zeros and parent names (numerical names) + children = children_bool[np.nonzero(children_bool)] #list of just the parent names (numerical names) + + + for child in children: # For every child of the current node that was found above... + # Check if the child has been visited or if it is in the same generation as child. If either not visited or + # in the same generation, continue with the following: + # print("child", child) + + if nodeList[child - 1] == False or same_layer(gens[nodeNames[child - 1]]['layer'], current[1], type_poc): + + # Add the child to the graph, and to the array of node names + G.add_node(nodeNames[child-1]) + if nodeNames[child - 1] in names_of_nodes: + x = 14 + else: + names_of_nodes.append(nodeNames[child - 1]) + + # Determine if the child is an effect or mode and add it to the correct array + '''if child < 27: effects.append(nodeNames[child - 1]) + else: modes.append(nodeNames[child - 1])''' + if child < 49: modes.append(nodeNames[child - 1]) + else: effects.append(nodeNames[child - 1]) + + # Add an edge between the current node and child in the tree we are building + if type_poc == "child": + G.add_edge(nodeNames[current[0]-1], nodeNames[child-1]) + else: + G.add_edge(nodeNames[child-1], nodeNames[current[0]-1]) + + queue.append([child, layer_fcn(current[1], type_poc)]) # Append the child to the queue + nodeList[child - 1] = True # Change the status of the child to say we have visited it + gens.update({nodeNames[child-1]: {"layer": layer_fcn(current[1], type_poc)}}) # Add child to dictionary of nodes + + # Remove the current node from the queue + queue = queue[1:] + + # Return the tree we made, its adjacency matrix, the dictionary of nodes, the effects array, and the modes arrat + return G, nx.to_numpy_array(G), gens, effects, modes, names_of_nodes + + +'''# **** Below code is still being worked on ***** + +arr, nodeNames = excel2Matrix("failureData.xlsx", "bigMatrix") +source = 11 +G = nx.DiGraph() + +effects = [] +modes = [] + +G.add_node(nodeNames[source - 1]) + +if source < 49: modes.append(nodeNames[source - 1]) +else: effects.append(nodeNames[source - 1]) + +adj = make_binary(arr).astype(int) + +nodeList = np.reshape(np.repeat(False, arr.shape[0]), (arr.shape[0], 1)) +nodes = diagonal_nodes(adj) + +nodeList[source-1] = True +queue = [[source, 0]] +gens = {nodeNames[source-1]: {"layer": 0}} + +while len(queue) > 0: + current = queue[0] + + children_bool = adj[current[0]-1] @ nodes # vector of zeros and child names (numerical names) + children = children_bool[np.nonzero(children_bool)] #list of just the child names (numerical names) + + for child in children: + if nodeList[child - 1] == False or gens[nodeNames[child - 1]]['layer'] > current[1]: + G.add_node(nodeNames[child-1]) + + if child < 27: effects.append(nodeNames[child - 1]) + else: modes.append(nodeNames[child - 1]) + + G.add_edge(nodeNames[current[0]-1], nodeNames[child-1]) + + queue.append([child, current[1]+1]) # Append the child to the queue + nodeList[child - 1] = True # Change the status of the child to say we have visited it + gens.update({nodeNames[child-1]: {"layer": current[1]+1}}) # Add child to dictionary of nodes + + queue = queue[1:]''' \ No newline at end of file diff --git a/twoTurbineCaseStudy.py b/twoTurbineCaseStudy.py new file mode 100644 index 00000000..f49be228 --- /dev/null +++ b/twoTurbineCaseStudy.py @@ -0,0 +1,76 @@ +import numpy as np +from graphBuilder import * +from searchMethods import * + +''' twoTurbineCaseStudy.py ----------------------------------------------------------------------------------------------- + + ****************************** Last Updated: 18 April 2024 ****************************** + + Methods: + 1) twoTurbine_bayesian_table: input adjusted adjacency matrix, adjacency matrix, current node, list of node names, + adjusted list of node names --> outputs list of parents, probability table + +----------------------------------------------------------------------------------------------------------------------- +----------------------------------------------------------------------------------------------------------------------- + + + twoTurbine_bayesian_table Documentation + ------------------------------------------ + This method inputs an adjusted adjacency matrix, regular adjacency matrix, current node (integer), array of node names (strings), + and adjusted node names (same indexing as adjusted adjacency matrix). We then calculate the probability table for the current node + given its parents in the adjusted adjacency matrix. We use the weights from the regular adjacency matrix to determine the transitional + probabilities between nodes. We output an array of the current node's parents and its probability table.''' + + +def twoTurbine_bayesian_table(a, arr, current, nodeNames, non): + # arr, nodeNames = excel2Matrix("ExcelFiles/failureData.xlsx", "twoTurbines_simplified") # For debugging, feel free to uncomment + adj = a.copy() + nodes = diagonal_nodes(adj) # Diagonal matrix of node's numerical names +1 + adj = make_binary(adj, 0) # Binarize adjacency matrix + parents_bool = nodes @ adj[:, current-1] # vector of zeros and parent names (numerical names) + parents = list(parents_bool[np.nonzero(parents_bool)]) # list of just the parent names (numerical names) + prob_table = np.zeros((2 ** len(parents), len(parents) + 2)) # Initialize the array for the node's conditional probability distribution + + current_index = np.where(nodeNames == non[int(current - 1)])[0][0] + + for iteration in range(2 ** len(parents)): # For each combination of parents having either failed or not + true_false_array = [] # Iniitalize to record which parents have failed + + for p in range(len(parents)): + parent_index = np.where(nodeNames == non[int(parents[p] - 1)])[0][0] + prob_table[iteration][p] = int(iteration / (2 ** p)) % 2 # Append whether or not the parent node has failed + prob_table[iteration][-2] += (1- int(iteration / (2 ** p)) % 2) * arr[parent_index][current_index] # Determine parent probability (given if the parent has failed or not) + true_false_array.append((int(iteration / (2 ** p)) % 2)) # Append whether or not the parent node has failed + + if prob_table[iteration][-2] > 1: # If the value is greater than 1, set the probability to 1 + prob_table[iteration][-2] = 1 + prob_table[iteration][-1] = 1 - prob_table[iteration][-2] # Add the probability of the node not failing to the array + # print(prob_table) + return parents, prob_table + + + +''' +# The following code is for writing excel file with all pair-wise probabilities. To run, copy and paste the code below into main.py and hit 'Run.' + +adjacency_matrix, nodeNames = excel2Matrix("ExcelFiles/failureData.xlsx", "twoTurbines_simplified") +current = 1 +midpoint = True +num_iterations = 1 +probabilities, nodeNames = getProbabilities("ExcelFiles/failureProbabilities.xlsx", sheetName = "Conditional Probabilities (2)") +all_probs = np.zeros(adjacency_matrix.shape) +with pd.ExcelWriter("bayesian_simulations3.xlsx") as writer: + for i in range(1, len(nodeNames) + 1): + print("----------------------------------", i, "----------------------------------") + array_of_probs = np.zeros((len(nodeNames), 2)) + for j in range(1,len(nodeNames)+1): + adjacency_matrix2 = adjacency_matrix.copy() + probabilities = np.array(probabilities).copy() + A, B = backward_bayesian_inference(adjacency_matrix2, nodeNames, [i], [i], [j], probabilities, start_bool = True, multi = False, poc="child", twoTurbine = True) + array_of_probs[j-1] = A + df = pd.DataFrame(np.array(array_of_probs)) + df.to_excel(writer, sheet_name="Probabilities given "+str(i)) + all_probs[:,i-1] = array_of_probs[:,0] + df2 = pd.DataFrame(np.array(all_probs)) + df2.to_excel(writer, sheet_name="allProbs") +''' \ No newline at end of file From 171e3929690eb4ffad6d2a0d6475ac15f164ec92 Mon Sep 17 00:00:00 2001 From: EmmaSlackGraph Date: Wed, 5 Jun 2024 12:07:45 -0600 Subject: [PATCH 02/24] Add Excel spreadsheets --- failureData.xlsx | Bin 0 -> 193244 bytes failureProbabilities.xlsx | Bin 0 -> 73055 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 failureData.xlsx create mode 100644 failureProbabilities.xlsx diff --git a/failureData.xlsx b/failureData.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..8d71b22ddaf613cdf7bf60ee74e16c8b1a0aac8b GIT binary patch literal 193244 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

!7i!h+w2N>Sl(A98qUnm(brLx1jh+K`(M)e_1 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

*$1o>Q6wYRA-28ku3 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

8y+4x}vQRnm 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 NF|dGGCIQUo3; Date: Thu, 6 Jun 2024 08:38:49 -0600 Subject: [PATCH 03/24] Add failure folder --- failure/failureData.xlsx | Bin 0 -> 193244 bytes failure/failureGraphs.py | 232 ++++++++++ failure/failureProbabilities.py | 741 ++++++++++++++++++++++++++++++ failure/failureProbabilities.xlsx | Bin 0 -> 73055 bytes failure/graphBuilder.py | 488 ++++++++++++++++++++ failure/searchMethods.py | 429 +++++++++++++++++ failure/twoTurbineCaseStudy.py | 76 +++ failure/zzz_failureGraphs.py | 526 +++++++++++++++++++++ 8 files changed, 2492 insertions(+) create mode 100644 failure/failureData.xlsx create mode 100644 failure/failureGraphs.py create mode 100644 failure/failureProbabilities.py create mode 100644 failure/failureProbabilities.xlsx create mode 100644 failure/graphBuilder.py create mode 100644 failure/searchMethods.py create mode 100644 failure/twoTurbineCaseStudy.py create mode 100644 failure/zzz_failureGraphs.py diff --git a/failure/failureData.xlsx b/failure/failureData.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..8d71b22ddaf613cdf7bf60ee74e16c8b1a0aac8b GIT binary patch literal 193244 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

!7i!h+w2N>Sl(A98qUnm(brLx1jh+K`(M)e_1 z&$pvp8?)FvCEKMf^|BuAQre=eGQsH2rO`&+-nQ&Iy@wLq9_eL|4`1W!mKp!of zr6+svH}L`gOSS=hw13$AXdiQGX(DR=ML7!Eq?IFH<=_|Gx-BkatzCiQKg|W%03g|C zH{iOHUD;)dr?hbSieL*~u|n~)Vuep&(LV=HYSYzsdQD%933B2@7+(2Nt1kohHWj#2 zz4UcOg0(NwG?{XkS2o`%*>GQL%(z`U@!Vlz(kU~nhdJ>4`P5$byfaDSQwJNj{7FXW z#fbwKbbj*q8FW&rvkM_)A_QL*qgucNC{+1b2m!QDH3ZtHG68~mc>Y2Ya`{)EvE6VR z;-ZN_nV`PO#ip^xIlP#?X!`!-8BK_?Axr+PZV^UK`ZjKRw6e7MvKhb&haGIX42IA& zS|X5a@hQKT4)7-3^-QZDjwV89cVXnd>5xTEyPlj^E~HoT+k@M zB@+N;5I<313jLKapygS@2GGZp82|WJJrrGKr@^8=%UsM;^IgXh#_oK3n_BB`u-x*Q zp?b0wFK-HOS7ab8($vXzcON5voQ*zTw#+C6H^6x=A&J=51C*FVd(CmT~4Es{ZDa{%aibwB z(DS594qVo@GFHAJyiAx;-g&vdIzCR?yA4Q2ioOLi|2LtO54UFkcF+WRg#ITz%MbF1 zc2PiHIrn5aX$sXlWs6lgzTT?$>-k_5t;?%K&kMbncLv2T#o%e+YJD>vU8)TI-cvX0 z4fD3PZ~J!mimSTKF6X#1GQDe`=BSUqQha{tVO!seubF!=X~hjR#bMXx`caBjBM3CI)STslZ2qI45?OK%0%b_!xlp>P@s{I zJ3a1{RzOVT*1vOJ9$Ke&b~#>a%Yeoy`k%Eb|0yA$R>g^|Zki{58nXR&oqbVtqSvvY zxF}wrPd3B#8Mjf5KR7xTnW6KFd*B{qsNufl=YTd=guDMdVH;9T*H!&IafTFsrBbdp zr0#@a0x3Cnf9*vHH0N+q0M{y3hn4Dj^B^E??S}aixt)4jFsJFYKNS6Yz<7-?Dqt%C9%_;51IWG7aU3;!0-FBi01(*Phm7>Y z@ZW}PtZ8MHVq`>vV+4X5Y9dB}Z)r-#KYAl4FKf6Dz7l2^r_i`9*V+cz-6nq*=qkAbs?!<^3e>t?KqNEE&{txlYdJY~ zAWn1&d_M`cx`RK~@k^oOX>EOm7}8y%;YyM72p{UyJ|Mpfc5z)JBR^*0m#yOP$$f(A zRC-oIb1K;BUlfnT^4loQ*^4KaP|Hpg$gE$GP{{I_#ph zYm)iN$4Jri1RJ}~%Zx9;X0eV{c{&mhrJDlmn5S)P)*Cik9=Mg_ct0|}2Atn&XY;l_ z;QJTouy3JdD`e?(z5~|4#<$gjC`Rq~(=Gv{{<_;gCemEkdL$!2{FNLQ0LT52JRQfH zjoSgrHvbZT1tfHqKO3GK*Qlo!A}0XMO3!I(r1^A$5r1gBaTe?d;L|hg1Z`8ZUf2gO zC=6JP0kdeC31Hj$X$s(LD0A~joopJ8@gE#=3;JD`gMQyO>$)h0Bi^1$weLZ7klOA$ zKEikJ7l0kGt2OxF;`dLl0C)xf*Lls+{iiLoP40O-Q~;=R5q9Lm1`D$mGP7fVfY~ye z=Q)W^o%PK}2!A}i|9D2#5y0Qv_O0cGKiL!5&xrcCM!%^CDY)@c)2bH^NLf05{+yYF zeo#0dW?4f|M=uj8XX&W+*+wQ(*V0k<^I9T$Q{e#Mt^Q};C1M>#&g8};naJYV?Us6X zD!R`xgV28b{O(kCIydXSt_{^3nzhO5k)x|9yfYY}*U&ND(%I2;sv zbDFejKE_svSe0LIeB*wvXufTK7>?Yw($&#L*T~pDE^A&v*J#^5Zf$O*sqVumKV7lNG{$!ZfW+Us{GtOR6Ia}$Y81ELr?2V;;#=n=2F?^`Kc;xuleCS zP)}8iN_P2rf0Z6e7q;BXG4oaVK!4R6a#uQlSfRZt|IuHSh1`WLt5fzyb31@c?=If&anO zG5@Q)1vH@A2U9Y{ifL@66=EvvQp{aD`&MF9E+bJc!mgf{sH`CoH6u<=KDNXvemwOg z%@1C580rB4OC`oOpdI%~C_?w^;d591bJdU2n6O2O$b886D~F2S3o)$KZMI6CSyiNG zP|-mZJLdo6EI{aSKN^A<9g||o=I^azM4BQ_OD;?;!Kef$(nf3q&(%ik1Sir#90bqR zL7W69(nVYV&(%fz1x}NoSPKyU>j+Ogz;Pcj^+oml3Z%Ptn8Wy z(St7cdt`T>9wmTRiS^Q;mtm>I7+qc_TZH}KRZfCGc$Hh>4_@WAkJW1q4|TD*&ZG0h z@pZ#Ih2;I4RRyx;Acyi2d#YJju@2t{_Z_w*rVc37U{v0EK zAZn3T#ML@fFB=azikk`)y$b-v;uq#@6CVVWihxFcj9EYjcgOTcOLZ`SxdKcmqc^*V z?J8luUl41MDTCs6_kQ7G#O;V0jqo8D+f*8VHvIp4q>vlkm)Qe~AOMST#z2qXJn`-C zfY~nh=J>-Hn7LDAflp`9i?p51dP`Kl1lIw1)BaLr;w;2{yvX!!R}07O%iSCO$AJC!*)0kf{7zLs z$yof$BgQa;L?GqwN?-F41NLLM)KUQfYgda1D5O&cn*oJ1f7wR~h8N=4p*V@q*Dr@2 z2>StDO{rg!X1x#Z+UWrzOMt^Z)p!wjQc8XWA6*le;Bkh!G1fYw6|At_y>a3Y&K-PZ4L*9(FzD% z0zgfOxd^*QJJkOP6N>(0Lh%DsY~4Ko1PddgPYf2pernf5jyJ9=P6>XN{8LqqcRLz@ zSlL@hRnJ-sY;9mK==2N&xbi5uQL=J_L%ziPvC%ie_8zA~zC5&LrETd|E=Cl*#7arIvpjr?oF z^PydUawlS*(Y)y#wR+m@>34BHXo9e|KWyt+`DYmdSb!}9yV`ltb^lssgbvYz?1i8( zf*ge?F@l_hBr$@%2w7m9|7saim*VmBZa}MYMrPo_N4XVlj~jn(n_?XK#oF^Oit<7t z&;#&C{8o;g>FV?q=HH^W|IAAinH0-s15O0xHfpc*^vzFzU8+EaaX&r3*I)Jkh?S|! zl$4>oa)*idDD&Z9+hmiET7wjYH9F|3a-oc4`Ag#4$eQzG6?{%0>}e$^igyJqbM(bhifVTSV1}H z1+W@S06t}ULRnT8fX+k#MhpdD&{1GDJ=9s?B|X#??ZC@#0IrhZd$k!({vhESD4-sW zx&!UI3ZW83@$jE#%|8TGp2O-;+d3=}{y6I?)bw!rV^IDiBQVOAwa65T0P#@)SX%*D zo{z_Q4kn;lptv9VYuyLnDu6N_^TP%JC8Kv}Mnz5z2+l!3a1v|*3|WLdIo13l z2N~&PXK={YaJ(Y)s=VL@8Ad!Jy=jsu!#SF+8blCfIRS8$d+&Xnj{i?t>I2A5#q$)E z0uracvSBr}W0E8uJ`4e@Xc~j=yE@Hg2ZM&~jcpg0-)ltr`|-8K z!3}_tLiznG`2ZLZlMh}c58#X;W22yt0r_V_mW=|0Nq?QS@tQ=5`QjiTG>2ZG{2Xs? zkJx4TFnxS(5}s=$`!N*GSJ^T3d0VFD>%!_DddXq!l>e9Q=3kqb{&RvhfKS|$4(|SI za4rizS$!>j9iT?8gh+PhPB2Rjpkct%=#0}-io)8nr zePas65y@zbOy&8bGOV-HicAy;z{S3QU6MKLL(HT}Q_8SB1~;c5d(*B4f&ncDhlpvv@d2V??TNN4RZ$*eqDt=UVvb zpTZ7C0atG&+;KP{^jFQJ&(LoC;m-fw$xAdPA(o2_1Qf*u1Vjn=4|f{|7Yip-LlaXI z5qn!(Q#%*u|NaI0#}(uC+N(|{gHApQYHx<)p{E6T0gPXPA^J`u9dDIJV-irv-Y}8?6gcmfT1i0w5(S~Eh&CS(${+N%B)%RVuTUur} z_Gl?d7HfH8eQtVAd}djw*J>GOS4;Ct$GbMW+xxBmK0oyQ<-7TqH`P3K5*=FxQRFO~mF?4ul_sP|vIbzP7kP4X*gh^h1zKunp z-FzmoArnP(7Pth%D2;GGEJnINTl(F>+5Mj1HSIR)XDbkiGqMt48Y$MmpdB(_T~S#F z+GYxG)HR~jRz(oOZ2q`o+-Z@4lT3$Xr=BD`tiAf~zN!l;AkVSQvGCG2l5R^s-f?*_ z`h9iuIx~SSRN6C_HKJS-k{4;zA-`b1H`?X+;WO1@ly!3B?B@a|g|E|FQvMHMhP%J} zWoDr+I0Eil_sw+AIArcJ>r7|rHTOC#7EX!mvwydn)N$*dV`v%@gzDaAE>^5GwfXQE zOLi|}M18}`dg9NT)`0gseesMO!lWR=`Csxy?#;m(ikGW1*Fpc}jV6h7nkK)h-i zna8)rcq*yYpb?_AaxXlUB!Dp+e=e2!y<5&EAHFGA(80K{nSeY(LLPHai88o$57Vr$ z7d2Njm2YUjG@myH64ecF%AHABg$t%?-IOiHD5W`peLu!FfRv7UfSB*=R<_$V*Wf@KdH%&Hnq|bY( zO=ISjjPad?j2tPk80ZsZF^zwhhKf^yls8 zh4%H`Df)YGu?quM>%_*{T1t03%cs|3!0o1y6(d)B?2y)Lnt_@?zP}%LE^%;t@rCqp z(;4F{w*#!b)|8}44uGIm`b#e;Nkk|_A3`}?rBX0?B7%YSf((cAgAf(H`0quno?$G% zr(@a+@s+N&Ii-QF1#^g$jH$H^URb$4Kkl5Jq$*D+)$n?E2m4pPQZm}* zw8Wf+t-?TC#Dm3Q_BUo6Pluz*9|9frlugENrm;f~52$-cS%_RBKxyx3y(;2Wi@9Up zqH53WmzVL&@;U}^RT?l@&>!^H!38?jZ&F#rSfQ;p;yIOiC)Q|kQR-DrpbVkpBIkbR z4tg1jfev%rtE!HH8DK}7l(`+ws;O-)ETDDfjo;Ge`pLYYy84pF`~o}s%rr!WbqT5X zvNj(EDV(2aOSAdAwnPc&D*n1O@|fJZ^kFXCqgSbw*@aKI&4~Vn4GJ%^JGGm04R+3d zHP^`_&f+QI`j@hE$~4L&F#`kG}h<(qH* zXlq?ThiBYmyVY;9yxV)n@=9{qW~@PTI~63(gmGbWVUy5t43gW%%$_`Fx(htfhwrEo zED$tlb~rCgl`#pa>8JXU+3V(l$T>R1t*ij#3+7Lmn6;f1?tLVdHH(L+n~n~Q7gDrx zYKgME9$LkTJk4x#4?@R4C0ybIVHzYiHQ#LrEIWiXSKS3}iOco8A;)K;>?e~Z>2}_l z%s+5TB}*NZYjmpVuPq+NOjBmJ}ysB%zZmK8z z&i^H&h+o-5ubQ<7vZUr`^49};TFueO+&;x>?_3fDz=|%rTRpjZ_|ldZq~l+OKZKcWl*hg%Bdi5 z?FVE6Grv+2Cuf4KIC{r5ZlW@8U=-%gzL>}K3F+*I@cDPqb|Vu(TWwdUehW3yIpC!3 zCtMsrsFuD-?FS`huU=uOAQAO(sJp)GRh997ETY+L%8%(Yzj24*Rt1T#HKmAFTE080 z^`u`WiA#EY;aGOP|0M7QH3uRA0=Vde|NNwKny${aG5z!WuU)bl;9atxk=0S$qt_eIoHHl>_o#eKClrfKBDv=;(Q6724DiwW@e?#cA69(x3 zzcuLjz=HOLc0EzS8@9v^A55>qXS^x!GD7AC5Jo%zzw;7$AlqDUhL= z!fPtp9dp+>+WsirbF@VMBGxkLc7k0=niq_ChlfDyx)}@Js&FZjTdrsG_N%ID6sH+m z45L2aJH!3%mhf1#Audr#-D+73Mq{Y1#OY8mCuCm`kywa8>t}xk_aYR>2Likxx9%;H z| zBH_!}t{Tebl4*-#Pg58Sajqoc?(J`SAjZAml#kM_ya-ChI{MZW!AwlF)ZFnn6V37A zZNC{1G`ppeP9;0?hw;8lVFnr-o%I!mpe|S#QH}J8lJf?lSb?Rf>=!PAt7B&XwF4C@ zUHudjy6OPC!8ZkSr%~Rx-%uaXmy|~CAguxU^t(a+jum-?$NeFW|Ay73V8|#zVZF1d z9Q_{T=X4s+;ic}cd07wwylJ3%LMTwy%tZfLxGmy>a9C&g+%nE$hs#b*c$_!T)28HC zkL-L7jiim-*T9Gs83k6hWUlm%vD|kjQKpzyitqDMc(LuDHqtXEdw%A#WP{pYnY>55 zRk-+k8-N|S$cv$woc?~!6YUa>{N~ezvOf9E_d&N&G~mZnA(w!Xl`!#2{Cwd#`?@0?6khm!!Br%?jJ1P~+L^$0wRq-_U>*dSh@M zu45bXn?RrbVUiS;2%7r(xlvryos0-eG0#6cFxicxWA?t;FuA>(b*8p7=6Dr(F;Wz4 zR`Utwh(IFXAZNzE2!%TfM#@TO%Q2i@)Ivha2>Ie3A~HL>RqquK6ndZ;Nlr7@dJ(XD zTEF#E->3)2H9t23+W9$G%agY}{u zC0{yB_>Ihfj?AK^$gLWbBV;F{n!X(yfYieZi|bDY6*}tXGR2~D9&!d(%XhykZHS(R zG9%ex_M4xNJ{$3PN`2i~Qz@NW+Qf|Rb~|pVN=O2nC6r%I6JI4lipSyJa8-NG<5#prgP=)NK!7%!vw;TgGD!vy zo?yM)@MuT1EwRZllmam%@DyW7_*%RLJW>*BKL?Bm@(+n|D`m{LRQvL(N2fA^FYgdJ z;>*!@L0;l}6eW0HOI65n9Bl+5JTiNG+3aNH6qX=!rHBQ@(2hUNG=)D3Ao+;=GR}0t zj20>68%XUWkhpH47Gfr^ZmlW!;rr-D{8Uy!|chS)# zSHceILAV1>y6;y$I4w8jt)=3tiyaQRA6^@9Rm61gM8)z-ZqmJmiJbDfvVtIn@Ufsb zkf0oc-Wla81T5nY1CwxDQd>VR^*Jbl4N=lZL&hJblc+N1kGaZ6-g42cK^dSD5*b($ zz#~wiCkQC0(V7#6@8oBFCD^ZWSNu4TdNzVwC{4bz4>lhcLg9}tDUjKrNiuN^Gd-PM z0(IcfM+0}Tev4)rr(wPu+R2+IJ9MM%&e?O$9)v%}4q<6}tq|uJ5pTjEbX_hgZMBR! zcmdrvM8_E)y00T(WnQYTni$@$g4%VY3IY;8xV~0C4CY2ca+WneZ+y@VFB$RGje!5h|kdgg)tMi4k#c$(xUHBF>>fo*dt+k?h zzxa5P8$S)JSP#}(abB>0))yn3IejPv|2ueW7)E$+ifE8*n`#!P4Fv%;wje4c_gP2X zDu{V!NWJt$Q;w}?C*x60q?)qw*Gey#i6deQOL=dHJvDG>&l&Mt3RD2J@*xCDp^65DTA3Rphaq3 zY@=f|3sD_N#g2fiX!E@(HGKE|JvQN|lM#X}XLa+L@1~T_d2o+>jhS3zoQizb;U;I= zrMH6f-f+9mtNS}f_;jjo2RZy#w(_h&9-USmZQ45){AQ8+1h)XuWV9SMYh)jv2wy;?ZRuKNYGGM`C>V7_R1ddF{8o+qq;%ViF6Gp}tK`VbC9`H~#Y#>q}Qm zGt}!WUhh7VoVXiat)T~$UJmg`ocx05hM3Jq?U8%P-r#sPc{J{+cI{a4brc>lap`w{ zGm1?I9E!eg0D|Q|z%7LmU)Gcnfq=-F|If!cjJyhReSr%C9&hQ{ z)V&m`ehL%EcT?ijOG=EyBQGK|=2Rv&c*kGnLHU*E=m0ZmOp_tf_E|5%@(rn3usvm! zvUZ81w-paFU%v=TKYwNzeqeasa+(n(R#2sb|m)F zllFeJJ05j?nRSw;%0gb;>E^Vr2_t}>TL2KwFw+TPO(f3IArm@35|lc_>|nr|iJ@&Bz9(Asi<@aGc-ujb>@gOGO0InGsTj4fA)@jiS6~LQ3-=_c zTsx9Zf6bF)UB4n2 zaLo05`}>lE*_$u0lNDh{loGYxi2t?#&LR^OF_3YhfkFhyi>X(jeTGc8mzW`mxR_{2 zJJm;~1dWhIS`+u6hN3{u@+P6EtsEUF?>1GnZaHj z!qXYl36YLIw5_4T;^y>8-h~M}lL4{!81~zhUQf;xme^$!udu2@4K-)0uGq6F+P>d9~7NW zw~vVJ}zP!#K>v@B9X#O(lo-1)|)zVQzKqlFLQD3FS_7bdrR0=)v@T7nlqd8 zr+8}@+eZtY?*sjvlrg@2wWS;;cIgOFuZrLqtr%#PAigIaJL^hKeF;m4&uxALtdCJO zrGb|%h^)?AN2KG%*s#+aYx?pe_>R;*2&`(I#eIwHSD-?_J9dCrZ^siwBeT7p!s@{!|t%FcbkCD`C!!0%@Jf| znL`0l7!q{EgA+fLJX-{T|7pVE3>i+djh@>&L6gxJWF0eu%{=#{p<+8GQMb|*E0iXl z(8V&|R1%Hm1XC1b6SEw71odEXkO|RxgxhC`|iFZ3qL;BhtmMRC-vX z5&3hm9%ybS(d#~N3XGW~^I8SePPQ#xx)2z0-wK_&4B497D*LNU5jCu!z&2FG*>-)H z4O9KC=Lz8M6pHB~c%2Or3$B8&`6hEkvl{5=3b*I(*;tk$G65|Jf#wW>*nuLyEy0{7 z7|q(WSrs3yG?^{OT_#py_skr`C#2!#Dj_go^gg%9BuJEd+@%YfRL}WPVy(VoM;Juf zFG@BrCG{YZ${h07TvQfY?L+pM{Muvb_w~;=Un(!BskpTx*($R3U5#~#O8T92dj}lv z)ZWWi36Mon$$MnM4w!KJ2(^I16pY zZvzA@%pS^V{4V$zRwYB%dSQ&J4*f1gd22E0uZ6a}^b6Vo`^D)^_0KP-F1PJV zy@WLQ>Amto2u5ZaxeP8j#qhX0R8n5h4aN3?{c=}92lYi(6BcyWWm4a+Ww06|g0CIQff+je|BT zS!JlZHI*3s-VE$h)ezFi>F4(Wm?1U%vh-<(--Pu^DPbYM(Q9jO4VY+|5E!$l6(YJ^wNBI2FPcr2G z*H)257QF+)1|N#51E-4BsQmLWCY+4-5d}Vk-z99Wy$;?LQ1KYx+!EaB$CPYZte9bU zwI8_6nUAOSlyR&1v*4rqdDqYRA6|IJ1E`;JWRt_S?2H0RFE}YC-y}?DGMaB6`nCv9 zN+%LXSWFV%G2Dq@yNkL@1jW4F_Z-$U%5d6*48#7?>bfWP5j*tc>_1h+L9#fawIb@Oo!g2iLD8CIqhbaC&}<9hQ*v zM+<~^GYm39z3O;s=*;C$o(7qw0eHfxOlqDKN^p=)KByh|m zh^}5`j^Ef(bzeE4_Tc)Eq?gN%I69Ggg78$#U2tz)50qPlpXMwYNfBe*rcpvrPDMOw ze8RYtY2y%7Ve65xawA1sq)UF5ra<^6?;YdO^l&vi5EKO!Inp_$@^OD^)HOtI=X8+A z1nH>9tGMDDs^1`(()ktn)i|0ub##t1N@z{Cuv3|<5z2<+mXF6)hIc%xK4pKDJARz` zB0up_b2r4Z6Lmh?mI3f}%#SFkN|HusH3?eUdb`wEQZee-VYZxxdJkN|FFX8G}i zK)uvR#kFNCWmvUL)VQFKPu!VYB^4rEp-#u#FmcQ3+Kn?N!F`@$c=O2v{lU7xif_4f=~oH7Et$&4>$O!HGUEZ-oB_AlZCga^<;ccNw_p@?Rn znhCNEH%lA{er7PKtG4_=Z~ATgRUmapB$Y5oA|bp<=_n1=1sxrh!Cd(pfe}1q2p&pr z>IkSPxMjE;YF1I2Iv4IeiyRL%Knwa8y;=y@zG4(mb6o+KUFKJ$*4jGSoTOX#JC*Tl zguS=Aan=71cu^ zQtYd{r;_waoUmn1RiVOzGc{>Yg-%D{Wc+Fl3?VBe=I-y6@)CY1(VoYw;zaMZ@zYdT zA1U0TWr%Zc+da>uD28VBCTb0pc0BRkb}+`YWPp5!pZ28I=YTzRyYZa?rS7I1;5W9P zVok~@!BaQ8f7wM^d^`0Gaq#vxQUUnL}f*_(C*=)SVaeg7JYw#-vIK#sp)SRrF>e4RpP!`0ozmR;?+ zv>;cPu`NJ|CO9&PpMcj^D5=NT*TQb~^=xx?q%Uk&c$KKSo@)!*lwTJ6)QyRTyXGM{ zm_3UeNy5B?+A6e^4+K)x6$!f?a1|IU&d%)YomyK;_}-|eEm3THE_SXt+Ry>fy4tS>ItQyApVl$on~WWCjx$;`9%T6@je?ij z?%uWSfznHHq!E|E$^?0kNZra{H{u9U5$R=l@`RQSGrsPia|xWLtxM?KZtC4~Xz?BN zKb=eH*~x>oq5%QT0<>ZQ#}gmzH^zUp-Ee%IKb-1V$L@%`zS?!a=h71Lu+JtwBrn}H z%PAGC4~}~pD=aVtf*Gh3qBl_~%P8k71-{&8YO5tmHgb-6$CzR4WF>1(J#*gwa(-Oz zE7l$4zpj;zd^zh+Jw_S)U7%U~_?X4Ou`@Z;)8xJE+gMc2!ATfx$q zyPgId=@lrLh;5bK)dLhN&nnN_}=WoY4Rz`&|-k2i5G$H2_D_@(3UDZ$Qy z?#l)Ot9Rex4Of=VQ%wHR-6#UO^Hk0L;4Q^%FbepLRpF7l>YT=%j&_B&1`F+CA7T+w z%fc-MMzS4_uZ6|fyiuiC0DMjLOX2>ZE9$XX_0RRaTM9n?rU?&3i{zNJ@$S27g(#%s zn7Y}NkS^TMXTtgr=*b0iFN%v5P&|5?mH8gNITDqIhI{1!bzk`AoW4rJz79|G&gogE z4$ld@VYO5?zW0wZcYqK5e&u=UzVL5bUeuO{vBiV9KFWGJ=Ss*J9Gz_E zJ?(Q4ejX?W0%I{JlasRo@W(oQEDH`#qozgVI#Pzc3>1+hlr?zCyF7nE%;-W_w z(hsRd_-(Nl6asI}=OD~YKM6&wpq&<(ggztu+Oj1QN85L4Xyom87$i*atM9YbnB*r* zd5O;`c*zTJ`GhyOz%oC)T4hyvi!i7D&1eMeV{rfY}naeuGB&x$*uSN|y`VX=b(m z{%{@1(DjhdnFx<XOt=&i$gKMxKGw>LG z^##T~kYY9+Z;sY@s(o;-ZJWeQj>A2(PA=UsBp0f3+TIzxE|cK#L``;?2S0^t6^*RR zJjj*0`}={>$^dePaq>Px9pn=ZWL;_>>S}$1;CA6NLzlsW@mgAaEvHn3h#pHfDSC>j zw-Jjvu&1HYZLYVp^W=kBUqWkd@`B4}u}L`RB8JY4Ell%kR`X*hFN}NRgYC=Matscb z@HP5Mbqo#LNRdV}iOB^;G@W^(AC0HsixQL0>{O3WahvJ8rsqU4WHbo3Z;B$}*SCAH z6Srlb2$qhSakK0pRE<`=apEJSh(_ZUksmnxI*t8!RM{f0{DvDddBg7=L3F%_2-Q85 z{2m9YytBXmM8MZd?iB2l{5s1P+m?NPDW$ZwwZrLGT67wko#W$n>Z2o~8$-LJNOj7~ zrHW>W1MR3g=+V$4UL{RWn%oJhn_wOwo3ZrvHMR8=U= zDl(`xVEq|2R4NdpRs#K^+BoMOyY(6je&d>2EK_+d0-5#=%M<>KLUt3|11$q|x);O?baRfcJ$n z`ObpC?+MX`@JmvLbB=J^optQCy1-Kg><(RCw4Rol@wxsF+vM=ru$6evDecexi$?6_GXTnxVG41{!1)O+@@ z2@D}QxQLlXcqDk>^^|yuXnk#GbiT(XWfm$OIIaRPo!QoXRs&1B@z6KY;_WNYsxybfN8Cxe9YVA&xitybdsomv(pQvkuWh50& zT!t>7-pN3Qp-m*Y$>z197t;+@(A(+|*dRN;4&>rCaL#Oyy7<}jh}dZPVdm=)aF6aG zaHTVGjb%8!W+$C+AVXPJEk7j=LA4at-tvKoA%O`Zl2bO2?Ug2FBDsT=RK$v*sOd)u z>{_D~FXx^bA}tv}!DaT66-`Tk!|CC8Ooj$tFqIx>bhjgPeuFdA3cy5_$|Knhs3q3{ zAp|NX_HLHVsD^>ql-$1bx3<}VgbHT$K}4+OY6A(8)RGxp57R6NfTNFrY$$V8*^6uLyW#QW8|^im?w0gU z%1vSO!2;cW!;YW9QGikP8$)?+g~TJ(^#6(U8Pi$gZ3@ERhzx&dUbo}NK&sKoS!4RD zC+U~OC6qft5plUq#R#TtUAPL&dtt}g;Ms=}N^uCLS(rN!R|8f`Qq)#K@gJE=cNu|h zKi7dP4u#ys(<@t%dsQ-Bg0zT}*j-+;HN`@#-OAh>(`qGl@h|3EyCBBLiflY6<)I)I zOyECZs%$pegFz)DnZKwGqK0LC+ol?-weAi;#+sqhmhxWd2I9QceF=1=&eCWnQ0QC^ zPYM^iN|a^Uj(=T86`;$e(nwjokorqXO2%8 zIIyoa{FWmok8YN+-Oaa_OiEwys*;dfh$|vq>yZ$ShP9;By~6=|d6Q={s5oGzy7gFO z%6B>w6p(o2i3awD75Hm6jjAIm555pu8Rm^=OYz%O|2-i$uWw#&fhD54H%s z*J~4#JO?k2Z}tc+!%9F9_=eAlA;=igBdm8|9K!DR0S%;;1gg;e(zCx3%L-;xrhjB3 zi@qZ7Y1fA25j#_yRK-29KwQz@a)^yZ8t$_Ox|oBA#Kkm$|>IZb+Y*plVS+bbpcAS*B19PuE9uO2b)(!|9}?G-6P zr&ry2r-K?LUq&l7Y08&7JD3^~G6UQ`s7PjoI&e@VVGai_6kiHT3@Qkz3WZ^wG>vI_ zzPv~#Axo*ESav91qzF@C!0!8-TdRT@Qn7={V@)8#VTtaZPqiLb zp1+mLi_gHTl~6h17t_Q_P9MUe5IB*n?cF`hl)StK5ik&YN`q9LuuHE8yP;1#^9!o( zKEjD1qI_sXq|Ahg6j~U)Y$Hnt&3xZa@DeXD_CR?T{esNn4D*7tFXo1+M#VoszK!U?Bj$_{DsP5w#F!@h6NLcz%WZdl~L{>+!|FBt)kj0O}W$*@DvPd3?dPZA#h+Sr_t?>A(Ac$l`iSXIt0n3{> zp&d*neLxoW_icJi*UN+m*`IINPRW~%ifZLi+1Zb+a&}g_@QnNVC6W-su-C+umtqcS zK`Z5?)nx}tcP(mxZHu0gqMwn{Qt6RVZ)$$VgLKLqP7en`5vYQEC%nb{bfZx>#*Rz< zEjrI+qnSOMESA5q8qOr`@P}N7P=v_=G2jv#zty?ByQ-j<4thJulWFg;heB(H5{Dlk z<1ti1{o+iR$)vpSrAJ!bxvOQK0XcOePdnD4@1O^PVR%@ak?U(F0)xtG7%;FRKgaa9 zsjSV*k#O|Fg0U!*I=+nM!J=uOQ^u%LW)IU^@|lusSNSm^IW}xQDw;B2&j~vG*0yz* z|0%E~D{46Ijwd-Fih-M8eY`%SsAi#BK6n;0X(Oj*Co7mku?B0$JdaXXsCet#iQiYm zn9D|ohg}95Z57t?7%}w((>2G&7@-;-lYDoJsMz(n2xtC_i%Pakgp}tIo1ZX@6o{CT zd_tP*whFtRj%kJgWVD_xe0L@^3uPZ0(4up$y2%OIx{OI_SXGPMJs|?3T($!glI0Q|*XmH(ro?1=7AW;Q%%Fcxk0P>~OQj$=!fh*OAf5Plx&B*F-cr*A zxGjioohojDBS)yR1HK7at@W&*drwHVQkUktgW#A0o7gi5$r>U0Gj*mQ5b+X^Xg%n? zK}uKI0W>~HlBsBdyh=};KKYQn;u~f_Z#oF6kQ%y-DC$o_-xo^QQkX2AJk!(d8wfBs zA)l|ypugoZ+0aPl=U5&sIAFcQCB!iY3B1xdR7ulfk5<|&^#meGA|1~VFV+4Zd+!va zNz`^}7rW49+qP}nwrxCxE_K;Pmu=g&ZQHh|-Y+I%CjR*j=KP;L$;ik&%X{t2z4yJ= zr9Vmf7~C<6f{7OQq#o}>E3q|9`dFirh3z;gU7oN<+TgU<6Kz%7p?m+D=4_-k3P->c zY-^-=eFi3vhH_pdsDz@vS5kh&Z9)GJUQ@VW<+jD%m5(Jzcl5DPWp^9C2BuG9jE+{4 z!8$CBB^dJc6~)D`;n#F?YG`QLX92{7*66eLL8vKs^RDfv7uY{!aIR8;0_hk$l(Z>0Z78{RiVQ@ zkh_UquAzT+JL$3dv_G_(U|4BunY%~bYpdXJ@R821eZcwpqE|92J6WNDDVqUE?tnQwF;NWms&+EieR0n-@XkBMnyK2o}(hxBj;n&09DQRjYD8tt%wQZ}pN;bm*D6LQvd&a1^ z`~an7l0i7iu9N-)GHr_F_@$9n)|2cEv-^g;Z;5OI?TQ|xQ`DkxtvOgq`g{QW31&Pa zVL$F_wQA0EOxp{pH6(e;GUc4#2|ML)7R%B+Fx4&YRkvPSLKr}mNZ~74|NIHGqHB{+ z@Qq^9ov9~6Cc`^O>upg&Ftm=5!r2EYmUD=p`;%vNVXkH}7lG#>P-AoMtF^SQ_Dzm> z%jcHiH#zsMyL4nP7)4xOOt6`Nu5p<(CSyulT?B8;Z957fbX&ftbEHj(*oUASNxALK zeCG~AC$tK#y9g8Nf$r@RSuBER3kTKBcf{skp)lu%V)a1FYml65G#wlvU!3T4pw*zZ z6^Lq*q_xwP_Z+W_h|b|ZnDuHvv#vLVH18QIX>&Rb3`x7HS{Z=C$YecKQ7B+dmV$$D zdj3?ZaL(sm%wO)b9>2E4%*rxc-?fQNSOT+6nXaUfU@?|z0-_caJ3$yWcnl#<(0Y#B z+Df6oz^Vy+6!X$w1zF_nfP7Evx9UUw$s!uWGA^xf8PpK2R}F=BJnt4rci8>jKZjOv z5IITBMP+0e>XAH>pUqr4Oex8?s2NGNskoR4oVQV0`OVxT-Z-2WP<$N1l%bxWo4=*w zs=OvY=LM!-lR!j6VX|QKN0nos z958iZj`BJ65|5IUWxGdks|R*d7#G7eYmHCx+iwYSwt)Qwh)A8c>Hr*3I0??6FEmu@ zQP?D}x9GqqT}a^84nL7KoJKcc40p5|$@ln!ju3^u#+5}xfcZ2NI!{5NwtMT-=h>b& z{wNvMJt4qrI_{#X_cyx5Z_?#~K$Sf(Z7XTIYC@KoQKXTt@W%ID@RII3A-^Xp2Y(kB zd5SD0u*2}GYmMjDG&#-SAAH;Wob2|gx_B8{yCOSZHCe4Ve`cqDd^*|>>HcoN=9^}JG*O$rGixkc zZm%4~u57-xKWg9He|Z9MXD*kI0vfbr*e9D>x1)+C&zf4h^<~c2FY`Yx0D8;qi+4-T z8!v$6F5K!ldpF}P`;YroZnl`mH2(9h)e5rq%aT=|jLvN2ttzsJ$H|cEwr1P!a5hanj+)7!-Ts)&CI>*hsn!x!8xXdZ-u}%@e{0(vs?_fCIUwgnU$;pR zLxn2dg-^1{43UHkZilqyv)2Yld#}ypurw%Nkfx%C>Sri@n(oMCmmg1k)X}aV1Pm+l z(Kb1sS8eewKd!Z{F4PxO%3ECAtne&jmwT-7@?F}|;N0B!PcdDx^J@+6UcS6u!JMnU9&8D^UUyV`160*jj4)3>;#M;|ClyOx{em(_cmwRdlxDUf zexV+yB9clffN@rPXV|oauQ@{CPLNx-zW5g&tynOYlP~i%e6Pw5MYF5jQgC^n5I&cV zeNx>z6fMLstxtRTE8?{@6|2OlQ-vCbyt~{ASG(^Nc?>JdFX=ZCpR;$T)1+KiT$6Km z9Ia|C4X}K;yftiU-5+V%o!c>M+Fv`4t5^R-ChG-^W5Vk#@h?h`Hx%K5>ZquJ_g2Bf?wD17v#{<4N*KzEg=js*Z+#Z85Pt~qpI0m ziO69f!jfd$0J4|r({`?c+5dTd)}-J6`96B;cWVym-oQ52PV2^+*>tE_pg4eUVaN$7 z9Lrdkw)NMr;zsPHl@90ueDqE4Q$VR4Tm@*q)s)8c)#Jd$jeC|XBiWL4Aot;&jUH8b z?1sLuy(Z?bF2YY9neIfrizIWIFywg@`cxxYS{wwJ5#UBsjx6$h!HimcHd z#cuCz&%dpalgEG?(_@r79K56ByJ{XrrysTI25TCS|HWU^#86`!<)_doj=W>xiDI>{ zPa1tv8joxyL4&4{%C_x%E$w>E;SH9u(jfZI^?fk;=Z+IQ5}+vY2)H<>qRhkiri~z} zD_?lNsCba#uDeD(0!AlH!g~ zu_=b`A86HIu}mvgMD@Vz&d7RNpO{|)d@J5}Us|xlQ+M0FOpcVHVN1c5t$UU$4@}N` za<{T4&6x03isxg$wgp}VH1g?L(;=f})E-A=jQ0y^9F&f!T9I2u7J^VERY2nSsF!5K zR)L83y(vC2j_71pyr+q*CKEjJxzmhBYs-+$n#u*wC#P=2flJOJzeS`@jjmX86Q=Uh zk#k!@SA;d)gmWp7D`ZM_uOj(Yc1>2ZR)_IgRebm?;3^%P*u{JNO<&VXRRK-5W|^K;lp zvPnyY_k~o{;WQ`43lkh6?F*5?f)ZLwn{57A7Y_YdnkqWq;ogvO(|fczVfA`+6tZO4 zv^dD*n<5Y&-P_0qCFXA><=dm?QaVFS;_!&$M8sU zFlxn4?4ffmIOjgd(rkBYqe=3Z9Kxkel9ck! zg?Zcb>u7J^106lqcG4iH3lfs$ygEw)DJ0Q@H9H&=L69m0u(@SDtGaFppmf$H>HC3w z<6lBER<&8)4PDEwuARAkR{dzZGQ;h{NA81RP#!J>$iN_L?{uMl(LV8NfC$x3xwiUJ zyLy*xvf4S5?5EFYr}@t!%}ZlrH%7Gs@7|FI*q7NJg6!xt_UJiGFQ06E__V7PN3mW@ zQVtS}A-QB~DsOH%>0ve0N8<{i@I4l{vKdttu)(hLSMY!%>lopgNYeJU&OQJTRf@YH zDi>X*1tNLV^;=?#BAkReXD*0In3p>A`u?72D6kI-J|k)f>mN<|9&XtV9IT$VnC)LR z1{c3R=W&pFbX2MAaBHOp3;p6Z#`ALeVvxvb#wkYYwrp`+zOoArwWaqV^k$3X!_M2E zq$FdcuiO9FapBx6Bs(Tukw+ZPn_0mB%UT^1q2mIzNBNV&{x=n0O;e}@dcSWfQKYIw z;&qw@)Xceb;?4C6K|PmeZ%R2BPXOCIW~2l4Ou{p&`>$hXEp~ML`0qjj86H$6?_|rv zwJ>pd5!XZscAcQyu{)O7r{?8P+#8)7OY2pjMApO*0j%`Q2cN`iFm+uIY=reSB#7)m zRX(He{WAOz##GqjxS4jV&|zDq0{EU@Kjk)%Y=aL|cly*P8S|Xgo<-5s!x#h zaorN2^arshO(c#0BEYLB%AnweFy*$%MHAH`L2sNaI9C}y8^MB_wGT}bJGBMz(c+_j zS1bs(q>uGArdcLzmyLJ5<1&lm4mk1kUAvmXu$d-NhLjMv_FF1$im9P+&OjkFuR@95 z{5>1K?IZyDsH^@+9_U*oPhNQh6sh%rvrn+IA?`Od?z)A^WkSO)%s_8z>xVTV>W#_j zqA`2J0}62YV6y+t&Rd9a+v@S-H~9=}#28ZnQ)A~0?Fk{JqM)Y}cewEI-(9Is4T>`3 znIs!e)+0}c-}t{kLvQMjp_%r{iJueYvEr-3B?>A1{B=lrbxF0~2}TrAD269h!6E>Y zw(;XNw#o8sQLhiMhx^b-_Y@5mx*|2{dK3vOB1FswsH$(Z!#0)nh5q)VZTj#NU%u{( zX?&md3*uTZnjRbe-febh zY(Krh&pSG37aU@|{X4zZ1-XQBsKoZhV+j?%MP)6A2gH{QM1K}E;I{dINXD-}d>=d{ z`tr1bc78lccJ#6|5BQUUMcKK4U;L{eW64jzi>1{ZFQ5c1#%YUi?ny5K@g+E5L10N= zPjcX7>5n+{mlN=bz0E@1bDg_2(ms6I}G7zED z%iwNZxoJ`GNmz_QP9;#B9?)u~s@neuXiT!`@LXLQe~){FI&<8>8);_I8IMciWu@=Q zr;l&VVo1Kq@u}Pe1*MU{Vngmtf972uPkU(2hu-eFkFMx*3D-@M+R>Noy5U>*?5u=n z)-i9gFMn{jQ1s1|f(X`8xw70TVQ7rV0ji4GbxRm=w2@+JmibJiPgGy|nrElBb+Wzm zBzy#hgx&F9fh@L{ZQ;#%MLy59G*kP$H+gIsh}^cO(U*c1u$E%Jg5<6DL#T;sOvHU; z&nP&W&z+I110m#7LZZ8Jc-`DXzi|$bFLA%nT}Dt7)M~*aQTO8j-M}YU&ljXB{ZLI- zQ$%}IU^)9@7fD%~hTX7=G!{5_9cHw)^Kcma&Q~a?9*}|MZZGCzFPILAiYf=oqL+$m zdg0uZD>A}~8Cr1$#h(;>TTJT?kO+Eg2O{u(OtpAI4y}lD*5ZQ^l*n9AmIR9c&%0Wu zLK#Y)`o%XsdOt#4*LS*^->jL-yUvf)#0AimaAn~HDW?acv1cSskV|{4^uc{D%-T@I zlJ#sNMIQ1D*>r+_r{%x4I`e*~S$rfC`1*;krl@2M#+)VZ~Rju7-wsKU>G(j-XMTCdih3yjStx& z9FY)4*{9o!VS<`dKn%-3YwkX~$DHrD~lK=Ovgh0{;cg$x!`AjRAxH4$z$-UtewG7z1@P@)_N*#j!C1+u3A7P~t@ zGmRAmcfkwcAcyET({Wwu1O*@vmwzF(&qK=TuyuLz?aQ(f3Vm(TypGM2-4^;Yk6TJj zP>@ecyty2{|1?-%NUb=G`Lyj)sj8{aaKFD+c-MmyJDb#Thd`yrA5J8Ke(g6qF{^sk zd7-Lmuw?ZOj>+eD$*f!-nuK?rsM0k}B@7A4H6b~yG8R3H&A6S@aC2>|!c&YF_Cpp3 zs*5yS)oWgVGhbvU5AwWHkWff?Me%#CcZ`G}LqL?k?JD<5m2+%1HWuVmCwDv@j{yGe z^I`B8&~-FZD4pPUT#q{lmN2l2oY}GCshVD>2HBYo4N&N&OVEZ^?*>)Oi?K)#6Pvd z>W-1o0ELILPaiLNG6(iUsV2x{lh#GVS(Nmp=5#EQl^gp?9i8~V))p-lK9m#==70t) z5YwvVPn~EZP(S15B#6mwfTu z8DvDF05_2mhP&H{-$^Fm=tN@B_R82fo8VmmJTXod8f#N##X+pMwog*;*!sD7d$;$quvHB|m z%PYo8_fVb`&n1G4)8eQqDh2aJo-x)jjRcoI5XAUfL{~PX=-QELD;j2iMNxk8*|sa@ z3c=x&!O(5)7(G0j`Q>r{{LnDphM9ytl!Nsb`8L!M zZ-=So9bHgK8rDKef%%KxGBEq+$m@GDXm zlXk0%HMY;U#_j87Q5%bc_}J+P!?GK_-4qhyV4z9aQF=aJE6XE^#$}IVBtl(*MAyB# z`!7AGWr)ZnrJ`;NI4`K6U}x4;YcB2L-8c_`18!j@N__Y5!BoHi1PZ~O(u}PT<<`M( zriP%BJ27aY;qhV5o_Rq}P-fa_$dSc+oM)`a1Fc&qBTTvp8PBsO|Iez&05?1U*T7T< zx+@?HvdBE@XktOxPvxjMax-M>i<#cqp1*Nn_1`LiK_66k!w3_mJC!Xd(&_{}6E}@6 z%@v8ol;>Ea;L+CpzAJ4_W7gAby@Jt)TF^{d7!5avaF+IZpLMoSgwx zq#i9*pzt(kfh?te@xmxEN6>tmQLL(qu3=&iZOf{j7>LfarQFzWP@xG?l)`ZUN0S=W z$K_Ysw^l#C|2Iy~{L>~ldOn-sRH>$wswI1TF}a0^Z*wXS&qDn^VQD^vVD$@m-2H6v zGIvoz1-&Pk6vS=vt37H`UI7Qv@94{3m!sQWynTnT#F^22#Q7 zY=}{b$M8I0Nbp1qsjFB>0%gryJawp4#mX|NBoBcSiUFa|U-u>JJ%zWmydWk#_XoA# zw}#KtogdC0q?f_{rV__!v$DUO+#n#o?@squD4h=D{O95^B?-J3tnnfF6=50`sDf(G zD9N|X`oEq1M@f#SkPJ|YPqj>TX!|_kNB=H?(e!_{sl{W|cP|JfV5Gv8m>1XVm@;vV z0)@zg(WFlClI}t53lA4NcsUDWF2KqO-^Gfcl4~ZlIJ8KHJGY^LoD4E2(j-muuyiYQ zW(6PU@1U66&G;peD!tPu#JN@+0@d=!>jpEph7|-k2n)vF zA2T+i#&uwErfe@k268{0?Bzwa`p*>mL`u_}+I7kFciimfU9h^^*Y_YYu^={MK&Rv3 zZr?WE1ZkXALSe9UUlHPpiy0ALfC4LO>7V(-aT=5g!qFZ4g#bv!sim^pw%qq+ZdspZ zhOvr(KtVs+SXiDpmQJVeF>pZd?g3g(pUq-YRk53*KEQZD(7 zyip$#kYD10$$~H)?^}%mZ8tdwaw2er-iM8c$bk)kpAfW1Z2rYG)T<*PRCRc(bBJn3 z7Iy*D$BOBywhcU+wk9W?4KqxhkmET3?Ncu5+?sr)0K4$Y>`YC3dS=1AHx8drdZpoL z(*&_htqR_iHiF^+_i^A%n(DeKVmN_3A#&-&(_(!J;$JhpHwI4OYlm!|+(UFv-jR-HBx zPU(260z1I3c!hGel@tpxl~PKF0qcvo0;opOBYdr9+Ni?RtG~iMZo#2~1>dvmSl&!7 zX$%5t;!vxdgsy&p^y3!;BIHAy?EvY48Buz54%J2-zvdI0c0+NQxN#~~_Y=-~L;nRU zH-a2+2qCIX7!SJvT6Vfl5};XGRtlJ-qM!cW$V{-lzgnXI6#0(uxqZvk*ZRdC& zyXgR@JWEs+M$M4Mk)Qo&4A4pf0}W4^7vLCi;G(oY8|M6|ibDe^OZ^HJRoboz4@gQ(l~N03);@4D+JFSaW3W0a1yNE}jMpc+*6 z`%jR^I)@jF2FaL#)I0UB2pzRGN5#_`(O805t3D~#BLix*t^+t7ROgyZs$Go)2Ntts z*+CfCUf-87VJPm6ZF|O&O0`^VQQ*JmL;`FDlyTF7Qmx?Nc+@*{A2p6Qi?3OKi|^^n zszsNbqz=bFEN99Qf=4I`t(mJ$8r6d6h!)RNysjiOjw5M_c^~`LcQ(2us3{K%Rg%;2 zE_rGbRPnfoKQGnLm>yoHvbBEWdsp_rSNqxUs^`i)r?!boj=?~V&KK{)d8w7p4i#vw zpeJ;W%2lF&qvg4qe?EqJz(ODEGkW*bLCt&mcT8Eai4#b&z#ioeAov&R$`pr4M!l(} z%Tr|9+06Md&`MI1hF`@@jJ2MYZUnk@7}`f_?onLVnk6qox;%_Ze&0mkKv5(mV?MAA z!6@>ndGsNnnvGE5*MbX!DOfoI*W~wwnr6!gKDynS2uB|%4|7-`(*p!nb)V_jE-L2O zl*JN_mI!jcI^U|=v~bE&27*!oNNUlc`zmb))1)jk5X0V2+GpwyZBPsZ^=62Rga4gS zYg?pcrcNWv)Vms14zlXT_GV0glm<6C)%BI2M(ux9a0N;kD328xTC1Gb5z+G)(l!cc zw-tBd8P|5{-_2iqT9^}rw-=9=F;kMO(*XF}gtpe<+3xn4)RMuUoCe@JL!g5jdADE^pFTKleA!8iVOzg|BRb z-tN3q9usE}!yWtQsA6zM7D78*)j5mqs^J8QfagYDkS%7@8Mo zhJ{MGg?)Td@Iv_n2^NF8{cHjq`>0X>anD%4UvhS*!pJw0Yo~aW;$s zYh6odXrGi$ifMjUc2TtU@u%%+|A0-{<`QyZ6T(@JBlmXoG<^@PdqtRA;!=b@t2O)@I1oty2^ zhQ_Ld7_%7&HCJ=;nJrlmq?e_`&sr)THJhCxNy_%Fz+Bn-_`?H>D&r@h1G&}pVWtx> zPiXEp_+Ds}Yu$FEcW7puUN8o~88USl0Wtw~P7t*~aKQ<4P{_=3aso_5^HlFb1;-2` zmVk+yV^8o>UhHsjZ@%9Oy6dpKh!0Y)z_xCRMP77&h14_!J>!Eg-jfd$8W=`hV5iD&C*GBqUJWn(HX3ET36vDGcbmDy+wG89dVWr0+8~XGSx4o|rJd*aI zqDtCO;ZK9uigB9)McHnIDeZ4WZ+b3e&twtW&-x*6pBGPCfsx1t3Qn@D%Jw!E33?Ml zbGzaXUR&VP9i3&1+g8Z|P?2;XZw#>P&&CsF?Qy@H8NFamXbO{g9_rlO5@wiXxT%Y+ zF+=XYQPw=K#BC8lqFVenmj9JAf{^gF%VB8=@rf#|{tWrhh;2V=T@tSTOL%_l_v-qduW1CDTWzb}6#n3XS6j7(}_J|$!y(B%T@@_3*j4~lC!^GjC(C?774#=_|IAb#I zl|;%pr9tw}nj1O-@#HSwDEqI!kBpnR=$!)85b^MRt*PA`wrDU7S<%*?^QO#DU**)r zJH1|J-1PWfhKKq|gWxJnzoT~A_RRURJ^%h{-rIK;N(Swo>&5gbT%GwR)lcW0abz`; z6|+$I7(DnI*!`*CQJn29ko{TGJt$u}QMsrrS?BY>)Gevc4D;$=1yQ+;N=clfXTlMa z2l5J4&rkrvmm5rox%7G4i%AFaqzCm#rhL_k$p|>YcZ8Vu_hAm-8=xc2bGnZEazV_A&Dyz!V{eb$n2!uCE@Uj_INHn*iIy ztcB`ad*aJ9|7}(4`{|1Q`pdh7;eH|~cEO>NqPACp9zR`$lNm*-CwRqFs0VpAOr@1E z0aY{dDzQ;q9fKuPJLum!XekzEZV3UN0jb_*#b4gic-~$De%44F$)$@lP|ICcU$)66Myf~&1=lnm<|1s!ACal^mFrWx*$GwD%bUexJkvihIu0_{F z7Wh*EPvPO5>pWUtsvql^};=Vqqj!~En4*lHXX zYiD$^UAIPlvt%o~&14+s2qfvVY;kcDF49zw8luD7ysukF7h%V2&suErP>I-X@%Z*f zC*3XajF`gv@Gp9v&*}12+Nb)~gYB2RQKXeN@5;@wZ#pq244ROfFhCcFQW)YG3BUG> z$djT(7EyZr4Gv*{1b_ZktBr{cVCy%#wo_o3yehK4I~U5mtgS9iO@j zcmMoVMsuv1O(>3z4Rt6I-(48|8nQ}c#tu!k~C z0Hp#ndt(JBdk5zq^QV*Pf69s=4-A+h2k7UK|9>BW@%qyJ z3@C%!;N8Nb^}2I_DaL#Sw|BMiI0xp<)`p(tJgXY$If zK)sAA1cICq`I3RY9dMBizIrE#amqj2INrs^8;sgFdN><@wBd$|EXic&25(+}P!pb7 zZmRuCjqQ7WcPb8Jung+yUb?SM>b?1iG=upb?fv1J&3ETpi3C@0$4lFF#Q;)Ime8Kz zJnyiCyR;2{6zRlalB{47=>2t~XiE`_Ru6l9{`T#m3y8aayZU=HB=baVMWY#HCL%n* zcPQU)Sg-6q74-e%K30$h1w#S)1qAtXO$Zb;^5>FX;heW?_toC=SH!a?RUYT`7Mg3K@mh zvXvPtI$3zM+*?rH8iOSxdKtjo!12*BPiGci=oQpCq90 z99)h5QfGNCdJJ(}%{AxP^?%##2TcYFWHJ-lz8GV4(^-!>t0Q)Bl(7N#B!iVC?GbVsHEZ!2@){Pv|f8%;n>z0IDD=1Fo; z0q+A6ie(4mN_=f%L+*ud_1qO(!AwL%On+#xJsxxQPiuH%FXFFt4Bfnw;2fX^ma^Gt z(zlM*VNB=>WUa6z3yN7NnzqbtUv(bXqL5xz15o;0+pwpp$9jeaC-}@7_O43rVm4v& z4Em7cnFUwkT&BC3zW_o~64=9DuKC@s7@W6CcJ8*?_^}b@%+ET`M?5#Zj__>%Hs>g+ z*_d$J@^Hl9Q!4$vP?6sm!<@q;#DR1tcYNL2ojE!D{6!QqdH4DJRrpy=_=9k3wo`YB zr5T2?@jHRa{%#qE9A*0&WGE==M(uM(%l>!Y_?s1%-J0wZn+|?N9Sfo_Lr>X>w;E%t zEm9mUc?E%F|3v;BVN01OP8fZ;BAuDgJQBBbwl)Dit)(GYp(wC_zl~N;B{ky<^b@Q} zQq0dv&v3Z6G%3fX9fgAyrK)h0YWmlcs1V@u~}$`6a~PtCf9h5NSQ>gBnAA^&&dzdeEj z8UIhKvJC0}5j^o0iz6OdEkIy9PnT(IM!9UGE5mzo$|=$0oRj^K zRV`1~A01SsThZVkzEZOt@<*E;o~Wb+Crto@f;wG_B~4c4^kch}m;9-TRyN;VnT~W} z`nMO={=4rZ`K$w1l$acST4aXeLAxBr)VmxIl3zvHclL z{?(V4Rb431)dx7~&W1L^N)vSl_`bFQQG ze4LTQc6kP*`*W_j>$~_J{b!srM@e8hdR_3MH&(~(#q@l6GDUKcJ)O5t#eELI>~^@m zW(H#e41XF}n9Qr$AA@_{Pg>&Ghg)a($OX4g|7;Q9xNo_zbk{@A3hJ3^8kP_Oh?(%Z4>|Ji9YyRs1!9FEb2Ej`jRUZsFd1Y9Jdf(}N{T-v8B| zC8}zI^HH~(A5%o`WO^w2cwepW76>%R+jr|+zTc@s18-qa4-6JqDaPTL^pC#>7yZ{PA~RZ_S6 z>wS2!l!M4*6#So`>uIs{NNUp`G0pZ{U9X~QWrlcUDK=jy5s|kv3EY!Ubw-5)dG38b zTfn!6UZLA{jT=x1>gl&e(<1Ty;WuJhb-HP$Hl$J=nd8_0SatVYdy_56FoKD!^0Lt) z>xC``?p0k@V*Jbf50%2W#U_dbZK+o|kE0I%L#%!N+qGvf^z+{DEv=QuqulJb89p4> z@hT&SYb(Ozf4r~i;n$iemNPVHs%lnoX#Qb`N&|&M$w*wZnheY5Peb_a979#SlXl*L z=;+Vy^%4E-^m-ez75AQwrrwU8wB(ewJHT`R zG{}A^06cXqA~Nf17iA-IwCZ@pcSjkqd+r}oHSL5{%RwB$^?LzLlkNuBcf!; z_W_|i%y?^BDXl~xQeeM}xLX`Y5h`Hi#lo)sW8ATm|`w_XKV!e2v@ONb@CU zGKu9@fQW^bNukZwkAX^h~)gkvF_imytXwLwDZ8Ryk4<3|onY=F}{N0Oi(f6PYbGnd9tS0!Z(hQS4EP^N{%c=%%B4ZNq(5-*S|C~W=wGuRSD zxNDh68?Ztwv!&a4^-R*vMN@H*g?^ibpk|6-SOCt4VTgJU-4#lYQcQF#=zZ~h?GOfK)_jEZZxwcp_6TDXnj&56tXlK8EfbkhTEYk{PuCWk*LVeKr>p08EPr zj515|vc_h5{Hi*73zxr*i|qG?lBXB=swkIr2KeGr_G>sl;S@H%JR9C1W1o5(7sy1P z3YzXc4w3i`IO+?GbOKTJV0I;-`if}ro5Se`ggkLOOPK?Gq$9!`ILVDq0VtC~$pQ0TFju=)xwiB8lPZ!+bxjjcPL5OHR+ERSqMR&?OTWzhoqP8u zqp6MGofr=Cqw+in&4AsTVh8J}A}^09_Wqs{SfWX2F%+A0&@L-utlg~(jl%YbY;F)A8x38Pr#2z<6A4jL zZe3QCxW@B@Lo_`*i_jE>bwXic?_m75E|S(%Kx)1D;*O5SmY5y^0Bps+1^{@oddk>Lt<`;PwINsjSiJ~Y$0gqpE2C+<_Y9&A^smp~;5yXqMaUSrkP z_>zPIRL3BiDoo!j*-1@cZ_U@I%}F)6G{0hG|M_6b2_Hp&=P@Lk%lk4?1~PBuav6DL z@?BvN*YoGV;ie*4>XQ2f^a^-#221fS#|JBI?@efg4{3GXh>#2jKQ+#3F#{95C~%bw zk=}L97(*8-G$MmM3`4)J|tp^vFq#MPRrEEXop`#?%O?Hk`?=d zC5rIUwT(C*m7bO9+W2^4&%Rc3jiV12)v;w8Y_f%-=x*pU91_%0*AzU$eedh+qo)VU8TW`s_nH@SH|wK z?ymZIbLQ#8v3YO^aJu*KUES6G0mkUb)1=1^ZaxOS^W;P->u)>qAzK|uPaAZ~`{l1~ z(N&RuBNEA~lwqm@KbK01NXhOmjyM7536#ze-`!}?|b#0&(XwXG?tn0*i z0k=ptl19pVe|oI_z!j(1_t2L=c&AQFTHS$!bEYn^Q4=I;$24TKcOC_1}<0Z^PSlP?1n)PCj{b|OE2C3Xk z>xrI4-*r&5nMxxD?V?eiICxa2MPkJ zHVPw$p0Wx8u2+0t;UCt^4J5<*C}Q8c_aBtmDVO7@z0o%1PXP?h-_fP1Gt5K0bkhcK z6*AuWFprp^Enx^M;-xu`2%?fWj$~e4A)KbAY?ko~``E%BG+tIwD2WhEri}Z9(1_LE zW7Z8u>Mi+fN68iA;slK#VuyJn{B0JF!xIz7^#Xs+yk?ExD0dyrW=&aSh$V?5xFY=n z=Q(6FFxCY^S9}a+v1wY$s%p->-dEfYVz!UZA5mYWxALsAM%#djbLEcPiwMilR$Z;N zkJrt@>ORaO@>n;m6`Z`mt0Srh(-fGt7wxo{=)vF@=mk991^gcJH1fL9y|1t7Q`u!_ z+vrvO%F5aZnn|{E_?AuA{ZX#QlPkiDz=)f#uqSU1@uB_2&+iNu^Ng}3TQA+}JgAOH zUy>*8i+WPDyWQWTTv2}a+1v5`2yyoq>X9@q%qW_ueh!{;MvY}m0CJ2Z6)tdB5drvX zH%fQbGAS_L++aMAIgJ6)XkA07H#5~#Nn8--my4%TbMv|=u#@>Nn1Vh28mbR^!s+Dk z>Fs!D+`3)wsyH7+b7}PYK;(NCUv)R*2AL|7xs(`-+>FOqc%W740L$)Qa3&e2(V8^T z;s_i0iydc&4YF$d5Ff30S=B|m>GYH$S~k1$?6WiLQx>$2`G(K>l}iuCx_iA!oC-Q~ zohknr~z1bi% z$C0RWeKM>jji8HE7lfV(-ox&Cd)K7IdC|4MjTLRuvZT;mTWyL5<$rdEE3kyvbbY7p zaXlTorm%-W@O5e0je8^A_WZXzs_hjw21GMIW%m4n0 zbe{I*rwyz5G?5&p5=y^O0HMwN7jc9EzD%o$78@lP8v*K3k&6mT;CxXB zvh6l<@H_(ssRq~Fd4dA%&dOPEX_?tPMU>DX><+5FFe*t-lZ}NzF4xXAr1!Ipx`z-mf4z^46FN{BDPTt}BYlZ5(>?Di}9`nrI*DxRPmc zmiFC)I|8ekx8o=Oy@h_lfo=~NQdl7L!JG1VMahB{36SM4K&CsO>YVJR_uygFC3GRn z?Cz}X4yQE`GuP3)eP5H^a7V+qP|H*tTukwj;x~ZQHi3j+3v9mx|;$7{)_AC#EhdZv>5 zrO8s25bO{jtb#H2;7MHg^3K0QWc25PoGa3e)KQO+0R`6FlNni^qMpnT(>{njTT8*dDe8oo-(;s` z8Lmex;OEEtVbP~s0w6eMfKs%K7Jo_?T7Zgv;Tql+1n3I#4Fm}lN+$KSkE6+Zp7cI2 z=^iD4Vf9osPi(pHGXFTP1>kuU9!;XPfu*y{ybH=k`J9x-v>#VTY6c>#mdd*U@Y;gk ziKa|iQhSczO?xd)e&F~bk^`r)=K1&$&_xJ~(h!nT${48}n)XLT!eR%1^Fb9Q(=A;i zskcUwREwt$`NzY7{t;?mLYxMBY+5;fal=gOsN02*BT{-ErRfaWXg#lkqHkZp1DvxF zw3`F3c6M9HH(a4LxMp0AjNaydN&xdx{|+2}fi~Yjr|580@8}XXe*CAC~6J zyF~{78>Gw(4V@^F6wK|YKOxb}W+K+jW546G4n2ONUS*$=zVm{j3-}JVV!;Ln{2ld{ z8zZk?AA_pQJuN*ZzY8eEoWRT!{b#1FH zdX&{7<)bYH>%*kWhC|ej4wZA#(ca6xCYLq<5X}{Oiq*n3j8Tc$l1hKO?SPr8bq1A; z?hIPUkzgsL!7U?QUG`t@saE&lk5+Mm8RBV_tM~upiq%|Tw4ma=iF&5nwD#HiwAp5Po zh$4N*Ft|RV3}o5S8bZAn2nKGaYIh0qdSKj!y8%;!a3%!Ib1=MczLhBUFZn*XZq;kD zUHsY(IaP$=DcYDdFjw)5-ul8E)BsmpH`_|KR_vpFoYAauLvK2&Ll_D685$oxK(q!#jWB&l+Op~0jQ>8pXeJf zP`%Q<%n5LJ@Ta&1OWr}FZ8)!1ptf`HLXTuvye7l!oWv0$0Cmzhz$D9Q8CZTgTQvi- zYljMjC@o~e_8EUL!DXQ%6^#TgrCY|&RlR2J7kt}`4w~bZwr7ooIJvxikV*dJlOyur z%2+=7WOx}py9Ac=;9mM`sM@(as4*qi)N6IjvSS7;@6@Xm(T*O>yN++tKy;VInH zKrY>vND$0#0&WZsE=nDjh7AJeWwc)YmSXJZvn$@+0?X-o!uHdVUs|eFFO0M~)kp}p>yS4MlK>9EF zfDB;wa21d~VD8B^UM~*09yt#Bh-|k6Y=vzov6W8#iT)vdY+G4nDIC2&QLq66YuT~c z`3l?o?yG{NTsGN~Vay-)dY=hHDUfTcJc)?tC}wGyECX>!J2^kb&`e36J^H&#)pT7h zl`Ng@5vK>;|KnuYQhbL+2;$ElZ^HlAWZ}O>&85bM&A}*wH=f>ietzgA?QV*21fs-^ z8+JshHc%pT!WytTu)A3zO*jJ`V2Ak{#QhD&P^3*Ws#k23|465ktFv0RQfWl)jqZIE z&xB;Lu{|5(@B%`jh!&}NT{qU()k+u_QAr~?VtjINCsvmvOZ(6L-M~rfq@#w}5WPVD zwdk*5(vlb+!N!PYp=D$2PgYq!9z1N^lv8F+B2vJrc+Q=fws2;U5ZQ>{#I$*Q7boGM zz_au>W~oK6G}b)je8r?1Vd7vT)LB7wpCK@xPKEDI>d9oEJGxRanTlsvIMJa=((rAV zcOXOgo})Ms!~2ceqKkrR>E}*l#Pz3ck{o<%^dO|I@pZGo>)Ji0uQbP8uJFvtoa5og zC#O+JPShg=Vbz8GM4wsPfoX8G*midwc8lKOO7BegCN^usA8+k}@O3l$FYAld#C{0gs*>wD=k{htZ3#yMHJ4MXej5Ut z4AtRvfA-xd+t+Mw`!k`JZxIHK9Y0hc-EqkX5!8gc65pZY$RPAJ;Uo4(zxd?`g4R z>G8au@urJV=FCaT0M1U7+wQ5QN+=ZPq?CiY11lD6Y`>E67n)rc;7 ziG3d>Up!Qy(hZfA66ROTEFl6SY5V33`YH}@t@Dvy@Sx5@%D3$ z$mHLR@3dAc|V42TH>lSM`l< z8Ji2X*ik^A>8RFIwi-{*2Vfz12gAe@!q}VTVUtas{TAdfI0$$F}S=ZR5tj5_SR%9B0WxXoS z6|9#E=h;ahSSpO=hOOn!$EbVib@j^|nn1!L4sQ9bwlB9iAfadU-MO_;3*Xkll&Jq1 zFHjkc8F&wTt58h=e-kus3>ns;Fw~#G2&tnKf4}b4IAnIYaJlJeya&A!0vCrPm&!SE zQ%qoSqYfovHt>A|K5o_WLBOrrdjXtFI8zPZgQnM&`S-IUMcn$H zckK}SEV^-%?o&&CGMFf6`d`J}5EuK7i8|*GR*)Df3_5V*&UyF0>#u(}0%}Rf%Z~&& z;OU63r8PzOz_b>o2S~!!R-%^q;B#q`DgiRLO$C(@hJ_C&VP|d0D+wv)BA@1DW4T(4 zw3K4)GZdd^m2CSB9Jp{?w#2>xH#XCfA7h~i6Ue#b+byt*F_v*?h83d-PlHnU57p0? zb=US`1BS_7Z77xEMghp*ZW>lJ&?;=j_3uFjx6}hL#u$^-lO0~kn!QH&`HcpKX6BiL z;3Brn?GNAj|H-OegZaqfMUl|Hqo_TKA04< z@h`JJ3W$<&YN=3r4)n7n_v5n4_Y>&~Ei1HQ98}whM)Iz0J^V+I9bMVrVfqEAk&`sq z_J^$qfSeu#zX9>dyAoAVM$q*6l0P8&E zUv5!8=Sf`tx5mEvatiK@UzN(6=xuB8V+oeS3ntOTO>EVq6Q=}36Yf;Y-$~nwhMHK_EB>B){x%=nm>y>r%Bg$RYiywPaE?E*?GQU zi*qt-Q+9z-8%HkS@6W~_K}HfPT+)D{Jd%`-nH)v|!!O+~e)l&lY&wO_1d>)22TYHg zcwZ99k6)8wsl4gX@7%6W3O7VGRL_Q9 zZUbbHiZjYnExTYb5c7-}^XZKF=&|%pA&6OUt5l7%)YuO7yKs_l_i; zdF9ph?sgE{iB+m0v(oT(zIQOy<5j+OIJ|aETDfLar^AA0tCM*(^^*I2?d$z=WWOM7 zGWAcLE_KSH#o4WJ>G@#lz~+UyyK74pFZ0NVOLMw3^U1}#2TNvUV&m!S%WHkpCik#u zW29t_2CZfMA#p?d!{sx{y#wpR=RkLQ+x~0u-)qP9%I~)=I8|(LrZ*=dEfZs2&JSlD z*VY?5^J~m6c(#l`!|C2{ZiU>%+PZBz_af!?@UdNXN%x&{X@}+Mc?D3U?-BHbaek7VcYw>;UlO5n=d-jpflg(s-QI4x%whCaF+8lwE zXM$3?eAR|oW)BL$muxY8*z1t$D7R5o5#e&n+bDYEV2wz67SF3_k$sqM^H%4gF`X3K zn><{_lzQdZ;$?T3yv#FGotP|4=9F^hHw`Sj7q78joNO&TTo3mF!12v$lzbh($hy06 z>FjjveQvMu;9j!t?2kSS_4oO+BE4PkvH#q-cQ}%M1{$vRcyYRCNS}19z`>H$Yu;2d zH{5r>-m!}hcVm{{4o-4HQPD*!tvq-~{RQ>UJXG05<(!!yRP)&_@3Ktx!3_~P@=(EU zcbOJ0&Sgoo#N&M3FEx~lc4mx_ z!}A#Rj%dJ-NrK&Chmg9Xfde|r2#s-80r9Wyx{L_bx$nJr0?YBJe{aX(==m5hs(ao2 zq+i=(p_?usDA_ArIJsJO*XkLy-m)gGyyCHWJ4QLO7@yg7SPn#+nwjdi&pvw)jWNDE zzYU(EQq|#MSNB-nbnG#GzIL_cShLB@{#1m?x9Bp@nwd5V_oY4Vz!BK9>2}1;yvhpe zOx!UWr~8>@_i|?Pb=%ylqnq|JmUH-6ls*~!Ys$IC`son4qTpJKXaMoLA-B`y>#+WV z8{Ew(_aGSTpBb!kc#a>o)k%s!Xs$uVDXvltVY&qsy?`R|i<7CgHcj6x1tCo|(#_#l zs~mwS91V2_dDX9Z>r7z=bl`32oi7bs$Mb=)wd8hp@O(PlL|JRxnee|9)-4x?_3ZZh z>7cp8A94ptf}bj2yxOROtkYC%K2V`7Qt|;mUg|Gk2b*$W6Q(}Q{v%!&cOwhpVNRUd zdQT?W-#Ao?Fxu#2XIZ5h zOSWmqE63A53{D&lPeCKzcKrU*5mnHUW!6{&x4EWVJc)LCeY9_}NZCJ^PQ9PBkuiMoYV8dMQ4ZUr%c+?&4@46-s(S*H6!CiN(Dp3Xjr?drT1BNq>bgA zEln)J*Q|H^}JrmqC%^xixKh|P}@mg<78GKP$9^C*Iem0p^5(sQFl~;$` zg!y9@>rtX?_s$SoV3yv#Rc%)4x5JXrotFK8G0MonnWda0tibu1<%FPTBEhMp>0ohf z{aU%%A0fhPf^jBz+^9w)G^B~A_u?T;{~MG$k4zu?A5e~ll#2o~E4=O&5!(#0Orm(l zD!fN_=XfP!Wf+%dS%B{xwvWRH_03sOea0b5vhIpgB&r&IpQFxkY@Vs< zx|IvEM%IRdEKW%5&udea!l!@8)b~7`g*q#`r5s`_X&W9$6Tus`>^QSDA)rTwvQ3%a zlzX033v4I#GYKGNSB)(2K^M9y6Z%(tYdpFVS!h4_bi_FUcoRGqi@uHftrHB*K)Kcnc(c<$;ae2PxQ zh>bc}vA*)LP-Go@#gFi129Z!*C2t7eMPrE)aLS%Zm6YSI4vx_ipR`Vc;J27t;VOv9 z+$pxz)~dH`u0MxCw(`(Uy7jp4>~9(sXeajfz*zg6QVwnFQppq>fWFwkL^lqLTKpi$ z#iW1b#}dfPu<8qxZH|m9#vN;Dzjhtyz8=*e^ctET+I^1i7=Q@OICQ7FU47_V+t>VR z*K0-CneMLRIJWH!1aVPDg~R-wyP=5m3UZ5*x4hQ8;?Aus#;KKBB>pw7m3sFSZaVd7 z(7n{vb)ZY#ciR~qgMJ#B$IXZhVTql=@b=Kzg30(^x)30a6TSLlH-KY#Z05w z0b}i{n&${gAiF1^{U8ZERE-drktS$=;nw2xju!{xnF&y#6h~!x2pp3p%R8!6cNX0; z9lhAx;sbpW^BN$8GI?st>~dNE|0FrP%h3Nya`p34e;S)6c$9@1+&?!%H=rhpBs&?& z2XyqAPVe}n##$2k;kpZCauHWtuaXlmQ`cQz>aX1d76F4ZRg?X;IHjS$;|e;5Rs6&@-StlW8}FW@COykz8r5F^ zVyZ=dnI-AKCY@8p2;r5KKqA>dE`p-rL55O7?2|=D5dI;5g8BiI1l6u?__sVzU;nB*LZ2I&#R<>L(a% zf_u{qUN7!L?VzZ_V=FrtJ=ibSgaYz%dfGO9IuIt6UWBSst&X^H|JhiRWY+1n$sGC` zeUrZUFC~Yp6&at%SEHLRrc2_;V@Rze@U=V(mZF}sX-(SCc4OJRf^udH2DvkPg2Pc< zh0K-oOUcQ0-}7#mG+BeD{)duN@L*Qmtx-Dbjf>29W!Xid2q+-B^ngst3E0%G3h0kK zmq!tC4!+{^ZD1y4Tedk4fYUR>M;jDn_qOc6@}J6Ohr4*1H1|oT&P7eT0~p`<8uGDFJX=(ELT>KSb0nYBK*kB6M7SwV6Lz6J+%=y z`(&fx#-1xdDB+lvalWd7zr!(N`l=+A6c?@QG}M4(Wa%3bKIZR!^+MNQEtwE%H`f;(EEIqO7AooA#@&Xs4ENW?<(po&4Up zXT{5qT`X0-aa>C=wEk?9ZMQ6KQuEkh~T^ z4>=MWEu)hh#ibe|MD1-s8NMi)z;fi^O^2A+RNB_NggD{3@j|S*s#_jDQ&a^*3idO0q6EHw0WsqMSsNzHJ ztf}=*Y!uNInYl=MP?BX@dS%Bk00az+VGDq6wL<~K#_5-ji3LZI$YVH?{u6AFY^bn|{ zV<-)*u1tpDw|p-A%3Ay#u3t$`ZXo@4tobX+HJr6q{YR3!8WuBz+jVzm!>tNWeap*c zFHs4dBz1J`M5V2O9A}(Wxqh+e7Fhmi0lgyL0_-CbC4TB-80Qzf&v9U6E`Ac+uwIk7 zer07%P5DCF3F!jM>$e%}tLn+PwT3mMn;C4(lV8s#yg1WotU&+(W@<+u=k)~JP=UQ~ z&-dTm>@Y0v7kWex(6vE_zP*Omuhc~?z7T+H7XV=%HSFAYNEV~&1tNW#KaE3j@0cS; zUvi*P#6~aTY-_;f8+laws5o0%PfU0e?`!^99v>o+ zSdFw4J`!>dXJQgBCs0l)8&8<{o9}<`tN>JLky{N|i^)Kui{Eul7+!!+r&9BF&pUwQ zAKv^wmRtiM`J-^q=q?WeWfb7&z95N)RuBRRaDlKKQTcx3mUNkYPf$?BIJt;)n_P5m z4}wI*6+RBwVfe#O<`gF8vwVUJVm+%xWYT!ulxm^BNSjwH7JdE8!_e%8oecujZT`M= zO<$7Ekh-7lE~s;|KD0Rk@vI&HCB8r}!m)ST8a|eZVjwp}A%{!7j!Rpz6-Ylj1g6rt z_qa=?U@?asKZaI&pWb8auOr97!f2s4uoj2!v{+lHNfw1P%$L zeE$x^P+|V|K)1LsH5~Wr$ZePnHpl+*+7)DpmC?*Aps|KjFef3~^?`dwg4YX2x4(=e zHME%!sk3aLtc11d03u3TDzOH7Nc**;&`rG-mTLawet?0v~NGgoS%ozzr~5pc?26!;In5H97XJyW`;Tbfl=t2O8y@G<>; zpgqPO!{?b;DUjT?z^*%RRwRfpfE?CDL zxcVQetu7|`E#74|-}gBUOjo?C`w6?6^FREAP5M9eVj!3Y)UoMkW%tC3oW;f8q9?0= zZkE^{rDVqm6g(xBQ?~=s{r||0vF<~vDMh!5BpUTXF5Z@C>*dgJd z+K9i@Ojdx)Q|P!XW}+Ki^YCpm3;xYC$76yb8MajcuSy@S4`e!QQ=wJ^sn*-Qcc`Wm z-O?O%dNw#)&1+2L1PVX-f zN{009=(+A;c?`Yf13Pc@cHJB5C7tA`LK9V4k3LL?z?|E$CWV8Wx^xpiCzq6``c52u zD_h9KS&*1kV?r_xaNGF58M&>#tI2~vp5r}5|Jk!&Mvhx*fN~*<6kK|-d=W;TK-wVL zTxB`V$)0Wv#^HsIbWHdmmiHa1{5@%5xr?pL>P0s27qiur~T%X7-T$< z@iz^7zFd7qiH@ca>=6r2G>o??d)#A(@|J=r`o4k5#KV_#O^p)&q`{UOl+ARHWO=yV7SchbJF-WnU-+Im4U zx|@xSLzaK7jE!U=w4|=B(NCU-3!P}5aIDB(IHxnkT z{Cde7kTRpY%80Y{;udK(cZte2md8u!uwBG49wJ*G)OLf=GJ`ZB^Ce;D#39jO*Ke+KwQP*m6Ms(Z4dKYU2OAQSe9YHh{zdh z@@5sSiziZe=-9;v*t)-tTreTg3z6FJ(-1@c_;0B`hF&H5cdB#F2cW+u%0K6yGdee% zW+iX1Wytp`rysU>?z~&zX{uzgPSX760<=@Zl;ASbqI9vny2xR18(L4Y{zeq1<#nDJ zO*7Lx*~USlggldhh;OCwFCzE-KZu-R`8hdL!UP;sQFohw1xDh;Fy_zKUyPE(s%v6RFNpb<)bMMb-v8~vGd0a?ApG>wD5*8^cv%AbBX>Va zQRJ8kKBOJ}87|&+Xg7%G(`=HCyoYVX(0O@>39x3`5n0H(qjh8xK3VC0U+#Wv)ORVH z1LM57@g)gEl-Hx7gDWUk8?nc%+YYwEKLlyWlvdmOry~Dqu3liuOQ*zX+pteKu+#|R z=2{i}kGaDNc9RGD5WIPwS#D#3XAy0tN^gMDl4Oy*wG#$aadP<5K_6O8c#A&x5o9dI zN+bf3JgT$82(C}T{GPW1C(*c5o%J8~CBrO`!>?#nvlzF9I47>V4MEdMpMN*>9?Yp< zEJ*87ph)y+#G^PWo2i_x*bSz2LWQwYOQl21f=x9Qnsfe^xTXa1hVS2|Wm)Pdq6keT z3hU+7k+dY|92PiHUBDl#q45ZK_hB`cQ-Kj}T=BW?Xte;_uyV=IPJ$hRuU-Vlkd<)~ zf&fLbv);p(ffm=ykG>hft=c9L zKsY)y*?r?$8m!6(n?x}v_#L1$zs{ReT&1-=-hY3kzuM+SYHu5d{)dwbCbtWi0$;oH z8VPeZJNXGNXGz?OZhjLD%Uc5XBr=63jC)o5)v?+N5WqrJ0qb+)PSzAX~f?y z4k^8;(>w?F>_*dEpRL#^T%GvIQdNPWt$ruZL@mE6o_wS2lZP{x2MY&t)A}M*8=TW3 z^;$))P`URRh+jrTUrafNioc~yE09O#5|Ax4`)Im2<$_w*b zgS4J-VDQD>^!e zj}r&PA&5?&vI~Ia)mS@PzSZ1TNACl<>1GbV7_RXLFISV!rzhqR*U3(9`pO^npO7=4 z6q(g|q+{phS`I*o0vdWacp*&cD+5j@X^W+nVXyi5vtd#ihy_-2kb<=L(dw+soN*bq z5v=FoRg#!{Tq+ayD*lnItVKBAL?Zv+ppd}r;WUEr5l%Hs(K@Gi`r>C@w?eBU35>{! zR=F`2vHP|Qc66i}SNS#Vr~hJds7k6y1}pyylY1A%wn!NiNlhdcfEiCr_Oq8(vA+Z?XR$mo3Ra-P9Qo34 z7N_!;p~z(UTd0|e;=U)bG7sKZ?6H2qa9sHTQe$!>#s$QY1;YoG8j>0KA5G3fL__10 zkhTv&7{ZGwX&-mZ|8~^9&j3?StbYmzML7^qEcM%p&3}to&eEfJYZ^Dc2HkK>R3hDi zBfWJa(EkB`Gri-=T3*%RECYKYxDNvK*&|KR7MlQmHcqc&5q1yW^x4Bwb<#edfF;1D24Itex!TcBTscUTsgrfH1(gTRjt6udYPm+RLFh^2uws?y2{SC2^?V2dk=1#q=1TERND zO*dksELP?;$(@^d>Y1XuSn)K?yo^p=xC+8+8Jm{&nAZ$ehAqa7wLOhV|K0|5cX%6F zWo%g&6!^pb1WNzK%JO0k7Uad^Dfj1xaPT1z1rOmEyfIh!@vF?N6rHMJV1m9oFkvnJjKqPYQ4>m za_IhFJ?(@g_8r|+LdmlgIo{I|HNUel+f{fi*imR%S$&#H0=JpQEU`fj0xzWzn4P3r zg{*T{9ZxHf$TsM)FDSj-ohR#4ONKJzRe8vwOArZpe0@+!Ds|HjMq9!T+%F=x@Lxpk zqww_?k&7ok9x0rpF`oplqxg(_L{(sC62k>-{Mf(+Q@>RTy<3O3>Ro53?J$s9XcM6p z=`)4vRo9_qcVCj7fMc71CWBParHB0JK@LQ#clGcFqv`)A z-f8?Fa$!xBXI>-#)SWpIfUvO~E$d*X<+mxT!WgP5v}C$JVZ;R`mbdXRz`IzAo7LVg zJlmryHq#8Nq>9Z|LEMUTsW)+ZLZ$#iufyfU_%T^zO~}+_Z`<+$FRN^-gNRCDX=QYw zPhE+t6d#%b%yJ&9#jxTVEb-%%)fg4{9g<(^3H&}B-EzpHJoI~%ig6U|`bN#PnNrKVWsj<&Medf0)ceYH zgIU*0Dykbd=EluAcMuCmW-Ig5lMbaahMV)761?i50P7wMxe}^w@ge`9y)XGjLDMm+ z$KO#qeAg;`nx}hwUN1s_n3U7pP2@8cA2k&W2I6%dFyI(+HxwH55YIX$FqM`G5!+VZ zmER$93zAmeSU{(6vsogau0p}qhGZ{$&{8*;s~L*O)zS?-lL~5-+9zPp9M0LM?=USL znFGZ(VlG{Xu?=B1-&kV=3RN}2{RS>^4Pl4^Q|<-uPkGbLrS_p^(peLV4>$sTJ-NvL z+mloHk0&?q>&fk+aIONXz1GLMS2=3J{MqPlYT>jKNu5l4-(+(6!P5C^#3?>Fo3 ze&->}Ce9DRbL_-yf+Z(Duj7oDZieH8!|UbNJhd*i^WQw7FW18HdDI;@#j^SLr^|B< z9^p`WT}8};a~2ARFBjLH#q;e~s;*ZmatF@LMTEXzrw7-@^3%6ZU0DLn&d^fl>I3$U zRfGfD%4>s{j`Bys$c4p(d)EYNdj&082m6$%L(qpdqj8+JRyX!Dq{+^R^Lvtsxi_NW z)GBS8uh(=WqtAHltei2|ZR!0|ZwE6)WX0;DBXlDxv9yHFB;yt zwSwvu-OkF`#!vIVVl2$+!nBYEWveMI%9wJni0f0$)7JKDny;TX8v@&_t@1UW-LeV! zxh=Es4H<2FKou3Mzif;AY=T@4)jJqfUJH*hD2aO0#(EYJXc-~(klrd#&P?-P@f<=d zi-a3@C{yYNoIB7cGYif`Olo`c;9g=+ofnQFR5CBvQ!0Q?D-J0o1?{qB>g{Kg-R8iOMin_#=2ARw1n+Y+i&&98?CFw~j1~VqTP zFT|cD8LT%J?vf*Vs%OsLst?}s{kxW6KowOWOn?G~A)Hzv4mK3G2HGOqaD zq)1;1n?_MJ=S;u`NEbp7$%Vd z<&)r+N-8bz%&n?XsC1d@pa5w=+_kQcF{Su7oEGUku@u4!d%#!yMcwH5|z)T>qn`jZ` z^*lHZ5B;V}Su~F+yyiBf=2=LN0VLFMNW6h&{`DRMvshF0PB|^jOa!)>#^FC?HbgFA zP1JX(ttM&T;%M>CtF`J2b^6~4s>?*sV;kyZ4OX9aD`>ZisO(U4|EQwAH(`Bpv#bLa zx)gQ-+H1ZcaK%@=GW( zo}6Q_T_MX)5XAY-&(}c8>>u3*ljEP)`$)Q;*FyFr8chA6klu^Wv1Y)HG4wzQYJ!Q-^zw~2j!9|J?|zs}ByxYi_h z^S=)h{pZ59lcq)%@en}`h8|+%1S4-9Nadfd+Ol_f-eL|@uZBGd?Y;Qr#=j!d%-;E& zJ2bV?P;Hr!CQx8tFNQa}wpCzJLq+h$qm~e#nS48S zJvbE^L$7f4G-Pu7-dTUpb{N7`bQX1e?x=Q5OOGLNHB!3yyTt?NSzQc{+v3#QLEA}Rhyw#mr1i)0 zsS)^$W|hn?AtuIhC6HFUfa-LmlFdS+DkD}J$zc*H^hzBQZ0f`1(@oR1O80|+23Nua?Cn^5X~!3W3_XP(J?^xJN6Td(qi9P- z@6NqJP=u1g^S%;O^Jvgykt zy!`H@>stvMw)ndE*uPaC-m;R|=x!&nM9d+`K+^gln(SG*4plnid6}n?cynh?9U13R z_3=V{EC=>DFi(bN_Cbn*6^cKt_3ww^lYj!0{uve(`k@vBRT#Lnc%!Z)B^%VKkHc20 zx*_lV-BzaOuxf4n=JauIBYII%3B`fw3cipzbaLBPJzlvLo#7bk{8K}G+DL0OZ*fUP zBW^`Qe+|0o1SL0(sIx2{x(DFeTh(aU)5)ACm${$sb9ENLQUH~R$jGK`=IoC@t&qkF zfSZG_3>f?H#LvkWw+qSO7hmbWdY1NgB9&|n01-f8BKQd?WPsex!Sy6|6hFlru+VM9 zEqij!K9lq>`XUl7hunCMEOn%m9u@)b>`1p&cdT41XBELnulFDEi^f6C4Ez9|zP904 zK>CU=ao$bVZ}HYxf_fqRpfd1tPPg72iP@Flu>Qd#_$BR(8oX7{y-arq-PN#`uqnGY z5tJG$NM#^9M$+4LD~f#ee3GJ4=k^2FpdUnlLtsYY?1b~psFiemCDe`v*e0y5z@dEX zrq#I}Vkh6rULo%KcY2XF-p3?MjsmSjhZMG>cRs2WTVnWmorWihSOfC>&eq(Ry^|E(^PXgUc_9@t-KastlLVCN%v<$(V6nb_YqYJo^XeC7z zwtx?uucC}W#vlgr#NjbvVB;Bf5PpCDMcnQ&TK7|;`w@i?0L9cr;^8CDKML@V9$!MD z7Yu7EUW*PA(kT0(c(?~U0!v;=%K||9(jvGL%@JyWhVahBhM`@+r>KdOfv_0?VC4^d zUicqA5#4Y#^Wa~HCbAQcf`%2TWGQ@KERJ%8T)?uLntGBcfi@b)8Gh1{oFJQ4L8U1$ zz$`OW>HVe4ba&5L>qT*Eysa38?H}Xm_P<#rNOtvCO;tez#m<7WW@nZ!k=xB|kE~u@ zIc&+uT46(?e(1=_QK-Auba7DBKPfS-%BlME6pxuOhb0H8>^p(<;qeEDSlw?CK{L%k zb$bR=?~DX@WKNr>N@A#S!@Q~V>33RDM|LR?5|z=Lk8C*Dfv(Pg8?+Sm1~% z?zP_khy6+WGr*oQN6iaeu6r#!Ib7r}Nt$Vw9i9;;n8wPaf_BbsGAHuaQ!RCCnW5kC zM1V?67NdrG@y7wnBO#79A|{KR+1d3M2|(r0NVARrda?VP&>9EpDif080F5zrY)z_a zq(=r2yTjbO5Sd_Zh>~e0x%XCL40Jgc;-7Tpfdc%E5wLxnPSF)r08I=~Cm&BY z=nRj8%crm>(W(rYi)lo~wBTKQPHZw=5auN6Gfi=+Etq=-#3jiJKe~2vM`;H5R zOd$dIV~YUs0C_+%;Ap1Fv*_m6YbzvT(&S3=GIQC|`N-;hR-ebt^{r#r{7)r^_CNmy z^j$Y!)aS-?`Ul%_Zr}WNz~{q8VV$RBC%+?8K?07TCM_p0qpb@nKoZwP6jVi*J`0A! zGQ$NK|MVO_z93`oFxl!^q8fGlf%SC32UrF+N+OXx1g34qC}4+p{Nj$ws8dX13l4To z%uuxuCdA_F<=idizu8hm3{aD&)k%M3mA=NktJR#P5ZA^irjx}BD<>F;D`j`*7%na| zyMp)c#ezg9MI-#gt-g9AXkE{MtdR?wk1H!B3=#$nx+lI6Y$e6`z-}pZ>&1Lhq22t%T|AbLA@DI3$n63V1A6-m1gez?i4#{0cvC_Ixd{&p2dlGeWeyNr(0SGr)2S50f|sZhC;;?> zKP$#&p*;Hy#ZGb^JA%3YupXS|0GSW)Ay$`e942+$hyvZ<;&;di8yp;zAz!}yqij$c zlw+OJ>MFGa<_{caVGh}(oBQ&L<=;wS*ADD4b&BlTR~wkCY*g^PlF$I$5?rBa{P{V# zqV1q`V&=#2BPhbiuM>x6A4C^0LA=;40m$~mJYTQ?b_- z0!K3)eorWopG=%*2JOP+y{dAub~a1iO7pnGW8mS;JRL^pVew zHf%NCzhQMo2G;Oj$+@7uewfFb6u0S?6^p?DR2@A~u|utmhEu-51kO-A%s3gNl;+G@ z(CdY+ldu+!8?WkG<|4n{!(VMT*+srRX<>b7PaN?fVYI9SOU$e}brmzk%HgZ1X{)J{ z%``uLxmL7IqY5%fskz0OHxI~6!O&$VC$oRB%;f~f$5gx*RGm62y~K(n8Pe?|Li}GI z={Axpb28qv6r^XxE5C*gF$A&*hm6U3xZIk@9q9%zR~gK?#h+v8Gy#lifYuzSCc?jT zXF7yZB3@J0?O^tPX753X@o(0vPjJ%j3JNKCI_W7}2@T#^5I%rBM5_jao$p6-{8PuzV%dG%3d^X63O_XZB1m`AY)!N;^A-qIWmRL5ZP8AG%U=5|kkBAw9-M%zt)8%_oWOJz6@UN8ZW?eFopZ|(AHNhf4x84DXv->wAQ5>zv5Dvo zUPlV)I3FnHbX|_vtq;-~j4n9r3m-pcHnh+VGF0SZ5~&=7dU(j$GTrg$egq zYFexut*bwl2PHChNzlhHSPy2**9=_d9v*(rT+^A=mPBeBzTVxl!fuvp9G=`s*}5*A z?pGjycgTIpdreW%$oZwzybvG-V2E~_$l%%zNvAR$U&9!qs3e=+tiBt`^uz|u1aA$H_b;+2UVZ4jlLro=jJU6oqd<~slCr+!R~_eV+l_Fb z9Ll+~?r}So_5q9zjkt79=pp&HdT)FA|K0*nZdN4_XoeZRJLO z$H>6*n}LT_R1OwBe%L#YU}+y&=ly$BbfAdYf}z}MseldmyN zSX8O{1{{pLKiLUXE&Q^ue*_&XW}kKw#FpS*g*Gw!_>=Yyi1~bJk8@LFjqU7dQhq%r zpt5vLmyOKy;PsQ#9;e7`TqmybHjLyqusz|OV6)>#+KLBWDA#ROawisK-iOAniA93O zevnfra-i!OECtK*`>jfnvf@q6orxFBdcFz!YMt1j)-X=Y&mwi5np*@#)7vjKXHWX3 z1}f-s2V*U#Vw%#LL&KxhGk4S#f(k(v{!))lpP?3I;>(xz#J8-!7QxbcFfOaV4npFS zk{HC z2U{t>VrlNf3UR|fIBC0M)=c0sWwR4M zU%L3z7LqVSTE+n#@vYILbxOt_@*R;%WWT#!$ae!I&iP^^5`zrWM8}NL`5cz0H z&U>CF-QlT6tj=|ms%KVL`b6$puj{m|zpb({80-_0vld1aRZpFq6v_011A?MAv7#v^7y@6y_hxT$ncDq91rh`{w zYwpKgie}#oBwaqdykBc&y?9y;c1ApT$4-Mu`Nj5D7+@h!_}(-T?(-p9Z@8)e^}@l# z9X()hE$G%nG{LB)_5g^&f(nv6g2d?N;?&sk;G~-uezn3e1!i(B1TTuaSLs_s{<|R9 zSTOL@zg>%bNs$!JtDL&FKGwYRTMR)q_i?xE2;0oAdqG_NV=46b$!&b88i0*T0gFV=Cxh$c%7J*`Bno-kfck<#BqHd&<8FEM06etZT(ytsMOg!nJFOm47tA2oGctRyofqL6gHEN%~%t){ds_jj9? zYEP5JG?zA4uRP(&h=p?E3;Rx2XRo`fEDDa!8s;@urV5mcQWvkgS#wKn&*vkr*TXyW zjrlpF)TS}Rx>ZZcO_K%}FNQTsoS~+sw2^Yv#k3h)Zq=r=M^BE_`mwdgPfzYYmMl@X zs}tpa8pj4I&WfKM*E+De3brl>+qa&F>YU7R(vZEDJleDt*CvWJdD1(w5I3pMUQQ4D zSlXH{J`)yOTfDsDN-@k{Up^wvnlmdj)6zSSJgNp6swjV!W_&jsy}jT8l}(LUP^0H-`D7e_{zX49^X10jd(o#s&=%Oua9S<>U`#WB`h#ecNYP`ODrm)mK{-JBg z#4uyuGItp*dNDHEU|KsWR8?g;GkRs*fr!AuUOO_fXpmkTD!PaW0LJ#zHBPwFkL)3JVATt}uH!+fx1ds9#ZI`Wm7e*XAz4IU8|*yPez>RJwQ+ zXK0%=MvnM!E?XBpk26@a64L>iultz|qhO{_C zrT&6E@-gv%NcH8&EZaSD==jpQi9u9igRprsoIx6fp^9kicLYAlr1F7}~;)lW=oF-?q3s@z7NowQ4evbQVQ zV~SeBu?>JcFewu^Ztf(-AO56drOS7SEO+$dTOBeSn%mBva7KVW^rh#FkH8!ceZ8=? zxWjTJQ{Uw-qW!ps$2rqgxOGKmzY^u2YiQbUEKN9_EnM$=o2YBE?+f0Qy){P>{GYy1 zz^>b%Ldgdu7wobIMJcR0Daww>H`gvoKQgs1{nTHpEhBsDFkxfHTuqb^EC|~Xd$Chp zJ(fDH1>ck1M`z5_f*e2SuLnKwC9vpGXE3Z;?<5-tv?(I}q; zIL7e#Tb1K0zFRKkZ7$ zgX=NI@w9ig;tp3yNTNXWSy0_}hM#D-Pw^{z>BSJ$Z4)yP)z6nB^kKPzKYAnXhNGnQ zC)UgjYdhxY_h3Q-fI?@D=gPfIEV)gha$?aFhr7;jxX_5P!3b}4kmNeB-dcya!pQ_i z@k7qZA2c~AHfATw+C{-DGO0y><4;Kx4Z9<+dH(LF5?flo8=@1V-E@gxa29}1BGA;A zRg=$kN9E$iR9nXfgQc9sIc!Cfwjt!NfJK3<*!YbQwvG>5uhKtYsYWAd&T0R4kQ&rV zOEvgxE^eAdQfX+sj%Al2k;xG6T7~nf=$xoxstV!ST)umgbrQ{ple1K&`$I2QHvFiW zRaIMFpE3Is_v7gP_8Rie^XVW12|jINPL7f}Z#@H*VmG6Wm#d)ASw$?n5ZpA2h&`-j zWL}OWIOE}?(PX>)=q3Dg<$D>6O7hoHWBOds&fb>Ci7OJ*boB|&8z0QrtNv8XkIR_# zDBsm+_e4e2SPn5hAUl&W%$B2;IN4sfQv#tXrRffbCMDB$;|udv*v`HQ`=f>5L0Wyv zI3)3)@fI4o`W3!ieMPCl2E<|PHEp`wl zm37Q7Sfo`AcJkO#SEb&uhUgdN-aE+|qO??Mn7+=uDlIKcq(YWKsrxfvUBRa8A(IgA&-ly({jh)1gagF-AgnMy8avFe zH%8nrI_Z?uD;VM4c*8mFp4A(&&?b_4cvXE)ts6*s4JcqgQA|jNf-naPbse`-u9PTEzv+$B=kl>KbqXpdbZe z(`ylN-W<%CMn2WB2i`pjVo)+VKJw1%I-9pUfTibsH6ZePKvz;*iOIPrBuM~RPf47KiVvy8%s|TFn^qyC2q7_jDjhqR?#Mvka*J3r;()zauK z(^)KipV?_B;+=PZvFB)G3vdb~wec4fud0L#cz(^M_?5_ul=VVF6JSjRjLo&G#sNVg zYvs4li~CpTB|5JY3A_SeLbDnwZzG0xL0=KgWu8DYVvuUaYr;;nVuLI9Y}_(<+sb(T zbZ0(@ia(@})R4={KKMS&vKYJsDhGv#G$)^yy{iVzX=K0Cujx?b!=S6GM7a+@S{A!i zcbcL-v%fYxON(eL3;||AZAsc{VDx9X=BwA_6=?IOz~z-EkMD#U;57jHJ|;3R(z)a? zd;D=NP4vp>!-*Rskwt(xFM-lhJzEnYyvQwvG0BUWo;K!nCYc`dWy-F>tPcHnc{?ftr^24aV@DOT=H#~-$TaYF}I zv`+qu>CRDS+GE_E zP>c={5gy8#th5E|uyn`}*MtZf4O5if>qo7qXiI=>#W-}KCVzOnl`?s}ZWTvSLe~^Z zEKjm|wcT_6R(nA%&_Gz#jp0DPcF!m0?Vz6B_jZit0d2A%ri#CM9e8bB@=A{MTf?Us znjZS?l?Pr=(%oQr?=p>xJ&?3iE+hw{h@^D?oMRUBeXfSK$IPeNFPl)Na!{3>l+};% z+fIT~g?IW`Sh+jWRdp_WkqcM7Hc?C-REo_W<;a^w66BM2_z#}RVw1#zeAMQ1U{&;6=9#B{%e>bg53HB4`)25s1H+@4t0>zuC?guus1i;xUDMa! z66>tZfy()=)7VWl=Ce^@$IN*%Mlo5+M!5jtIq3mq^ZSL#Jh7d4HvXuYfE}rM`mfA8 zRsO-n6Jh~2y%Cwc47e$u8b9wex@?1|n>{04GTG$pZu};#P1P{c(2&+L4~EO=4W&nr z-pR0Y=zX}fD6l3~aX94q(%c=sWlE_n4#R6E&Tt9NRp$*?9TO3cHKClIe>j&jEmjMJ z_r2#cd1d`_@s6{71e$fdTjQoEklf$Tcd9mxnf%>;=#%OL@NKzIzyZa>%L~edxwQjs zLf=E3c5T>8W@_SW-u==domJK=49r+4nSwdF8-ql^A_GHChP=QhW;1|{T`K8LqfmBH zr3^5Ox;Y7*#1bSg-vR+RDVq|k%i=YNNT0ja1?N}m+1w8ZE5!FNfot!T;+w|H?!&bs z*M^^wx|&%lpWevZf^T#@Z;d`RvHhWx1*Nj7D@>eGU#9-yww_ejnMWV*JC}We#c4e{ z)Yb6DQI{6N6UvW=9g>9!cp$JVjSh8#44PanbqGmWCKy^@Qv8Y?Y-dc_?xD-^>?kbH!s9c3;fdPADzaOV) zZeODZQgpDV>K)z0X=!wIRp&;{9^Ns!OA%2w91H0?AHshr?F*&hU4LvB&Q`azzCgk1e9%2VNJ&m4 zM`Bkn;A%s;a1(^|B{TmKI$s?rg|v@!|)7I<&8v z_S>+V2oygf6r&Gu)ofx}JM?xf0?%$kkr1)|gYz;Za;6dC@%@|xi)n9f!k`ii0x%B$ zfNUzTnhYZnQHq}wPWSK~7l~$O5PV+GDdUULLC@OoR#trdS)`&p&mr0n8*VM8k|Q6B zu22IGvwg8<;WfBA3d?EQS;rUN78pv=ke3f67ZfRloO$H!r-SAF9U!YHTT0G?XVPD~ zwDGe>mz--j4>whx;=(XVeMCtCm4r{9ze{?}QlU5+IEdU&f~6VtVt9TaTdAzbSEf>-Kj~Q3g2pUsz zqIrrl?JE~&H4r%?D;O~`Tqj`Blc1WVlafSiF6p=s*c_ut?~wrC4bT}#z8VDTjOVN2vYO+?M)h)2tE!#Vb8L*Wc(su{TEA#3^E3muxJKp`;?-qYw*Zgv`QiEE z$<7Np=v=)r+F_0#Io(#ECIrWFiFn#8zhaH>DggDBg~OVxVDkS0y{(UZDwy;bJ2ty; zUx0DeJ`(*w&t9Ob{X`&gz>)y5gi=b+0Jg<|r&EgO*f>Yg`Amd`0QG4GIHdWqk#LMv zS*ow(A=n`w7Nr}~_ViBZz3gje?o7ne3M3;USb^*{dND{wWZm_pu&xOiHxN3mFtnkW zd#XFqvq#l;NE>466-npgp2&@=43YLbohIP&aETtLX~drU^6B&xSIFqm_R(dJ;|rd< z+RI0IiHwL8m^=^yWO|7}KM>OE>2umx;XN!N%R*GOVIK-Tew_{aO zI?p2h<8i@6taQAo3cYIbQ%F#1nxWEYC^w-*{NpLRs<@gq8ExHUem6S6L$k#*QCHew z^43aP@om`kb7}&>$|FXSg8)E0BmB@=Lk>)J668-;uMMx9ou55ku+x8@E`7sf(`b+8 zsq0JaSz?PwOa#4}Dsiw=Pi~^;W#hAz-#dn1j_1;-=EN3JxDX5coQK|>Ld9j}Fhf9w z9(CC7o^_&Zngzv=_aP-ARMbbDku3^%f46$r4s3+5#{@aAw4LhkoEaD^1Yh|3z)b2; zd9MRzuMTL!$T{p{0kQ_ZetHG3zqF6YT(XJ^YgLh=d&^`)U%Mj}$U)Kt!-XB^cuO3C)d@|g)O!@yghQ1I99qfCSwznh#rdDhOB9OzTfB0Lwk7~f;I2Zm=L zTt*J#Tu3}k@RDC(@Uyf@eC61-kId4aZFUBr<&-h2UL_)nHHpCex3oU3I&!T}qQZE@ zqt#BnmX66`-LrQWI&GhPgt}GhPF|SB3|H9JTl)3G^b%4}BL2po5Gkiy>#T z;=-tlm!KhAaRriF1!wu2b8tsq?vmF+MK>|nlC~Bx6Wn+ETP}(~WXDi=Y+eW?F{4g6 zS({3W(B0EOBBOKEK;?pZ|CM>}c!{oNK%kmXB-Y6dHpN?zU=~T%6Ct9D{H$uzF}@|< zeS{W8<7xHlE;~|J4fm7%==eTA0*u(}868^wg?Rxsk%{f^9!Z^Jv4Tcd}_%Ef_Y93IvpmPt^Zd1DGCnY)jo`89j>R ztUN8a1^YG}W@`ar+DiZ^P0vDc0lT!R$KcU(SEwxX+TrCu zgEgdZSrlC`6D=fQ( zt^Rg-x5&O0Tw5T2CY_TyF>>$Dv^>nTT*Z^ITEO4G5RRUFo1>nAi%)q~e^J8;Gi62q*=bErxi02f8~CmFH{cj?jsKw!?u5eq5lg zyu`IBcM0gV>iC#0NenreS#`2L5wb!d(S`H=bQ$D&=8xnc_8{Z8ox6bFAdmL{26-v+ z1-IY-lmskJhAGC}7?@Auj+;z04Bdx8E@#3Qz)NVmmM^ZE&t#Nh{|oZYXa5CxAef+d z(P+<&@#W{bqs!Sj3$f*aJ{vIKQ~b1-b9Xy@(rc0pqqf187brE*7teYmLfXs^+v~QD zx6`Y`0^aBvSLoafPbn-(FX7N7#oN@-Pxu&h5|4!ncucey-BM%ue2vfTb8@M4c00G9 zw}S6W1!1b2hJk-Y9+_1WoP@ei!~#Dv35x zPJ=`c6^m1uHfOYF-cvxN<|Wn`snN4T8_+JZ{D3wxBH(`f5-U}qf&EcWzf{$LMSNLi zyO+09_t$&}6P>0EoJ;;`c+FN1J}#-vQE;8!T41T4EZBz^yVo;1 zF{FNE7c?v$O0MLPhED?d;-_wqqP;X&>Kgqi@`0AA*oCRm{-h%Mj%_8$Ie5=aJU24E zuuo6w(6eU5$3okEDa-!&BIT(vP#h5OD`6YO(91gZ&c(_{ZlIZyIPIOkG{?xev=62I zv;t@i-el7An2Y9a<6>RZ;doSZ|D*x)$ra4Oor?p-lY!^Njn1d~P(O_&OLC!+`6GfP!C*OxEsQ8qymKWS+` z>rIO#MnM#M88trIGE%bc@6@hq6|TBVdR3#BxCCy0KO**Z{gVwrhMbDw*diP)-(4Y` zCs?im{92eK3FDyKQY6LCk;*3|>+Bl&yg+3Rl4iw_y zYQahIk4>=_0*V%FEUjE}z6@lvyuux&0^A^vjxnmDb}WG}Bl5)!b1@+CYX}wN#p3u8 zMBz$H_2O8(-k&nWd8k?%_e8`}Uml75~Q8k(2MlDmRooWn9W_6-kHr2+QRurA~ z28}b58&50R@Q%r|YV$l)3jo$iYpTYkQfaP1->#U4Cdd9;MnTTv;TNJlmGE}D8NT}&c>IMV>9^24<_Y#sUz5AkYUE9?Pml&i@b5M zg4Xse;qWBNE@E)FPe_i`tCF^H#%)Bo^}S>U5c|kIFJ0wHg_$GX8~&Y&*|8bWa*uXc z03Fm)*rCy>q;z1?x@pB)%KD2zK#8Jo%;73E};yPx#(^qq^<1_PGmB{aQCm2 zK(&6N{btla)QvFsGQk??A(=pqqM>eFD%HjUq+in2-yrYoFUT|9cA#zjbo)|N>Ao}2 zVQY2QL1QC$iy>G^!>mmhj9CZ=4>qS}A`sS611adX>+m&h%KR-%ZTQQMr(gpH?_8!i zGGODTR-kHrMZk%RQ4w2waE!@4U%oh&zHtnEHd7L$o=5jPdwL}Y(MyIrOn;F#e#6Xg zK^z`(@fsgwE8rCmqIn2CFalP|fX)tdY3WL_APhWPa7XURQ6wK4`pgKpsMPP8EJi#fcYk zEkvUWtN0?2N$9wa$h~dFP`ub2+b@+p;)2%LXhr0XDHv zt~fslc3X}uU)rG_BOe{e1t@PRROxft!t^iTKj{kg;8xngKk2wB!tRC=+tg#d*e7hu zwVCDhU5E21d^fM!Z&G(6l_$aqA!agpZrw0P;n4DmvV)G-r;l^0NC(;h=MF*I;V3fN zLv30pFe?wdToDt@bqBKR(MWTfD{E(zjWE5v3_^xLDT9d~-W=}D&!#N~YVyQ=IZCj8 zV|?C|mI4l%1xXoYrPNCpw6a`YJYo|v+47Ny^D>bn0V?q`kCKO0jz1uk<*T^PTt$Fw z>}+9^P+es6=pUbA`*b?)!MMOE-puNlsJY-}t@~cw!{u$#m9rF7vn4G&yYh5`3~!2_$GPN|B$?MIIE7w^c-8(DwJ=MxBp)x&w0-1AClKvU9j<&oXabzOP8m0dj5)2mNC`yc*jZD54zSpTzO(lqZB`!kWx zdy)E3egtxge|U6y=+nX2Fa!wfXG1xM{#jCDAZ_0bw@AgdliZ76}0iqPPB>p3(f z91%+9M0G)5Bz@wx!IH+-)a{L^$Y!5p;+eCEI$_!lqa$60TvfM@L`!cI$oN_MoC(14 zX9VL%(Z_k?*NQ#P=l{B%jNJe<)BnG#ynn7S&Na-U_JkcjEW5vQgn1p!&EZbxJ)=6qcVp23e&Zw0%9sb=yv`F79^IHVoH%+Xqf< zyQ9XP@riy2k1wyz%n=a|=|Rt9e=$AN)}(Q!u^KKI9#%C2)lU7OoTTNM2a=;hPRCx- z>E9a;4Y>6d<7ia%0cUBtNP~7`Qk6V^QhhG{H~H)E5z8XXMW6&w5Y=t;PHW6SBi%km zNN4t@nK>sO$5kY%nJ0b{NJAYBx?2i${z=_=An6l27HP%NN5C4&UrRg`axHG@#Cfjt zsSA7XSzzDng+_@yJk2RWgW_0x@26w*$Q-v=0;gJIs^a-W7xs$El2z?ocXGN~Q&L+crj8bd;vUq1v_^p7cdzIj zra7~tg(lQ{OHNB5&reAby2BhUYl_UhHeI+qAV~ozN{)G;Au&eIJoQ4e<`^zqh?*0@ z9-tt^v(AbaP)nlU8Q40BsMye?w8MD-QXQQZ}(j5oku$whj94;V4625%jJmZVao$v zIV{!`*#43BGGT{!jw19(%~HiKHC)>LvIZwJ@2{pd2<-l9L;U)hc37>_TQfFd(dxim z(1?4laUqB*@V^SEvPaMbkxTz@)EM#dS$xlO;-WVvxR|3sL7|`yL`JClKX=3NJ`&jF z1QEi1Kjg6GC;O#P#<6P$QYKYID<8yKO%LvZsAjEh}4jUnCCdkC& z1?`ME1HJCgT<7V*IGwjbW3xiB#@V0_m~9g#ip4xyMnufM0z%TIA613xFHx!+>7PC1 zot`WZ;Sh1y=Qav7gCI>U%bmi>i}of%wh*T~sQgke=IB%5;W2xAo-9jpff=1oiU3 zmz+s+)QKuMx8=?$T+>^us8b6bbA=T`2s9gBpJ;tJdz1MI1!IOSH0Yqo{j;UwQRaiD zyfl=hE6UQUJ!slhFSR8#1;`mnsV4Nc-8xSD zzmr#tT!@?Y-+GXyJ95A&h;gxKNt`; z@&!U2gThtf)P#whz?yN!u>yQ{g*6AArEom^e_bB@7+IzmqOvIn2JM_hHJ7Sy^J2r!pX6Jd z_xsSBKH^`ON9J7l93Yuj=2zG)g+jPtfS5x+McXEQ0FM$J1@E`Ji~gyhGz7o&7<~M2 z-d66Tj)9DtVHL5v@PA0ds?oN zoX6lFmuJ&;k2vo#(#9+r3?c{p*X3QVS8-W)<^9LyQ5>zQv4c7mKa)K zzGE670^fOH0Tb8v8g^E>ca+3o%j3|oH9L*byjhSA`(|P231W4x5j_rdE$%CI#!p<) z!#3;NKLfN^=_AQC8J26{qkZVk0AUi5eu2IMh_`tw0T1mzI8m6jaOZZ1Fx1$-CN z{}1B!4g^2ZHLLbR{v8)YB^)qn3utggWlMwm0R5H#KCWp%wP&|ADu~qO^Z92E;~v55NtBbgg}EoX(dh5s`zPFwAZyla0`9jGnzI+%C;D0#UM2>dz3 zB+~$xx9mmL{vq=TSg!|k3>FZcppeYcCa4zypLPO1B%*duv1Og!L$@bFXNvN5fOxx* zd0~CdFB1TXJW$oCD8v9H6&{yGEmar6dlM3`6t2K5S;?endl=fHVhh)ajN7!j z3g)P67~+Q_|I-kZb1>D}PyP5a50RF1JX|_rV;A|#uR7ZgaUi@4CUvYZB)let#?GK@ zQeOb@-X8^x`J5543_wu;2@^Tyd8c(ZLGBfElv~PNlt38JK0flSLrAa1-aTD{nU~(( z;o^#0KMozPBMiXDB>J;O5wPHXD!}X@0062W@AMh2`Y+G|fFT5dY3vbsddrB;fdS&B z)lumN$2a7!M+6J06$4P*-$EXN$Gg(8fsnqp@UKS*hFPPbzB2OQYn2PAXyIibtw)0> z@CUspbB&w{Z#tRRiLC!5snN#Az>QVAr6PD@W80qikN}mS97)9T%FPsKCef9puzoM5>aW_LX5|ZA1neT z?T=%1!FQ>(L>d09Q_Wj{5z^Ze>cF_K`zKVlO+spzQg($2}KX~af zp;cbg9KN`YV#m0(E1!J=(b8nqT5Ds&!(xx1U($WsJZ;FoHt)Vf{M+W?4e0wAvUmd{ z)91?nlK9u=S-kq%m}x~*z4NPJ0LhLVnY-Mul2Kus zxI;`bTL}Iv+5lNmwFih69``}y;13mq6-ce$ZYR+GCB6On5eL|~kbEe-h=3He;8@KS zwUp7$qq;KO0_C3*;5;8?Ga=dGIkxkp|Dk!ZdM}U- zYc^5vMF3Hqy)OjN#5G8X=9UrH!FM*m9Pw;E{)gse`dWU(&&?593Y{t@%N|9V(fQ%?|i`c_E<~ zEU$l!&71j;$kapq9=sHPud%IOuVTLRZ8d#)VcW!x>5K1z=A=;1asDL3zJ?Ma zxkY%12`W~%8MI<%^*C9o>7tC_l0H2zAeO5(t@r^Y?_?(-w*yywfX2A2&?7Up{tx z{gIgI1D2MxIQk`}PyqoDrw24A@tQ_u@EDmP0c@lR9S&o;P{`_+q>&1wS;Lg742A@| zRb45;&D7g3GKN+Pz&#UIn!R?ppAg3QkotpXyyQf!+sPX8SCN6G{CR3jmQMD1W~H`! z2tQnVJGP1Cxn$KK6?u-z=*%Gz)$DQv1RSzuVdz-2Z^X3W?OQ>GW5`jANCpIXKTK0B z75>t^$-gvD`TwDLHVGEKSd6X?OaOMoQlGqP0_)E&^8W#ye8KyW>q zc)3{3U~9XIT+-KY#gqi8_N$PA@APb*ez!y5NJ>b!5T&&$__^7?ZuUe}MHR32F{$)X zhMI77z)vy8v6HQK%S%v8GQWRmx2bO1ankqFdM?ei=irYNS9YWwKGd<+qB@}gb)J^7 z6>Ubdq~Q1A&wh%}R)LJP)#qX--q{gUvemPbY4KAi!<-lY8e3z^s%*hEN;n6=*LN19 zJk`wD9`V)NU=j`86~}Z~a0j~pxx=#xK~m3rteAXjtfpw`j3<9T!cOEv6t$cNyvh1F zZfDx+M-9X${4Jtl*y3b^eFy`>FPt(U?I|z!Aa;?y&la#VD1d92Vb$Zd=xEH6Dr6Bv zkYZ2-=PWFELpR0$N9N@t^xk5ic$AG(u zDdvhvAQKheZjN+9SwiVn%M+1PEV5+^D+ia>Juvu3mB!P|`5pVOBn(G1WiTpiBlyW3 z(aa=sK=;NixlzGf)XoFWP=FS9z)WakoUZzUadlrj!M^dV#1vuZK~wu6kiDAx0i9kJvE1}?6yF=<4gUik4P-7e#b~Ne7u@|Xb1?Aa*f}JVjUGNbhl;_(-wz2?%ko6DC z1N!P()zn~*?;9FZ|NIx`m5j^``~&ksWGb8`EV4`K|5!|RNVf(>2A5&R>AnqIHkSQ- z%nElA8o3|_10JzVNGQb$MRobpPnyGbL5!3heRN`9Aor)sgMhE-ekDppfuVtMpo(36 z4KE*hgAB#K26rKvd>pI>SUpYvCU6ukhRG*b>D4Q7|P3@ zul&;>;n!z0KDeov!d1kv=LgCQeIAI5^PW&xyn!_vX$i6@n)QT%R(Oj>gW^2<J9#r9B$C4axf zwbn@k-qX0bHvBA+g64llLjQ*2Pk8Y7Mt1fNLz{O`YqX3r1oVN&4lM0IraX)EpL%VPzV7`377!>3NMPD ze9?*7GauR@9g5II!x?AqO8s^hzUjo`-4JmKcJlo*|5wC9oYlGl!IDv)IrR|Yv_1I* zpUl+)vJchF{baqUjgyxg=%WRLrD7K4jrc}IwUjwB3V@DZYQgVt3aB)L{S|wLFw-fB zOs!N)JbrgIh&VmS<^|wTkZ29dTF9_Br5T8CXhL0xorFRK#%rvi?I?imfA;et?^nS_ zfU=qR8NY)t-op-26fVO3G6h`8wD`GlA8;)u)g{f#g>W+MYhZ>E^ixXtwqUnq{ab7u z65s<+=KtgJ=FhL3x;8%4qw-sHiT*E_x5_`jM2DBa+LO2%J!S?0V_*BDcB^{K(;4uz zsvU0ImWh>*{l8t_0_1;O9+Qt#gK7$oU>lbS#8N`J$4QdAzWQ6$Yuf+r@<%07$@0#+6lhL-kr|L+HM|BK73{dRdk*47;Vz`TT8SN$tO zsm^>ze7qR6SQ$~%266H)E@>QZ7MU{yQR9n^h0|X6*ZULCw~KF>=ke+C4f9Hu(p>7k zV|kJ37-d?LsVD9a&o_tKo$-$D`WB4~rfO8FiXS(J4V&xkpZ85(cNS1?bD`rH%5&c+!0n*EODwG<;-~p?zNW86Mc@1e-fJ}`ug1Ewk$LEjalkiH>pvY zGWQ0X)-Dg8sqbuAF3uvnxZMU0OSEo`xfst38#cmIANE$x*mf>zQ>!ewJ(}EBE}QBX zANq#U`a5b0wYfK)Ba6(J>N1|gj=rNf=RXK0?znX$jT~4$obPx#wfAM=+Z^*bhZbA3 zm(|#OE{qD2NJ<`eXJ59yVcr%-ff#zBr=GFuujQCW0gjv6dFtHpg_a_h-w$2491A>F zJS{k!W@{=72D6-1jlZPz70(F;<4SuLv;zU>-Q@U&y{bh-7s_%&o; zeW&ua|EBVKeHIhmZ*a=L+&9fDLcjJKLYXyEI2B|Z0vrc6a`orlS|%FK4@&+JQ^)NE9PKw-0bzkdT2<1X_+PoX zH)y}QU&%Kdk~cg|UEYV-Y!@AkLt zD=Kw|LY8Nanib4Hn-9{I21l2)O6A74KC*3E<&0YiU6)nU*&0LHYFMQ_)?~>g!tlvvN%usCNzN`dOw|zA zH?BeDtFW6GxF4-7c|4rlo)0$B)?)7`ye|Xw3xx5!x&r{Y6nsX<;2z~VLPIY;9r$T z|E==e{-g3-irb_63olqlpw{{ymgj0NTG*jQEo{n(#(%>gSUX8e{SD=nJuFfCa;l6K zs)i+5X|b%il1+ZrTY9EKmEz7?(gwNvdr6;~Wur7_7g&cd^ozQ@MT6cZ8^TYXgt#2DfQh8g49V&;D`c~4d2sOylR6GL?vYW4!i%FrKzqB{Lop_y1IO7Ep0zS-37dxNCsmE zxVyW%6WoKlyIXJx5C|bykPuvgySux~tE9W%bZ3}(^Db*4wbuS?pL_Q?r>eN${^ig9 zVUd^bTdTXtGExgHoze=Y8?xanM(XT-cRf2nb;#dI@opamNVgZ0L<+0@7lmh*u1VLw z-zF!-&2T?2+d@>`Pc&{Jf19QRFlf}G#X0KY$Y^iBYDg<>-rX6oy53w1RMc<$(ZlVX zNgS=XZX0h-q`oZAgz}z%dx!vPR#G{+RQ}|u^gRMo+nmI1L?G!JgYhwaP)S24LuD03 zgc(mrMm=+B)-7BDnA@9uKJpnK-kjJIKJ^P1=8w9jXT1n@$9tZNd9)+9Y2xK%;8HfpSNBM$RIEI z4}-j+sp*FDn@UfBaThifv`da9uaoKn*+c}eJX>F=0!D&C&9l_3j}7#p{bSD;aA1?I zI7)=yBNrt9O5v*S`j%Imz(Xb3P1$#U z5wNKhBQz38-qgse&+VIZ)=W2$Ia+h`$sHfthhdP}rWi#-1g{vZP$otM$I$mxmzd@^ z5P_$L>1FnkF$B8$am^Ks4yQ@ri8TbWno2ocFo|rXfybTtGn2ESy*D0~M7*elw)V-j ztKw;8!Er#<_Gt093mezMg71Zw3$3i&glB;M21(=zuajm%O$Z9JdP`F5BtQ1>mWY0E z%3=H-4(v@58k=JMM^V;p$u@oZA~os4F`b$JVap)2%f-Eb-Th#h*orM z#8X=dYbzmqFI2UgNa|<>KBO^IwoEa>qmRUduD!8b0GI&T889#Zlc|+Y#-l7cQiISR zz)Vt;+C zPAqnhE6A>15~DJRossRGN1oirU42D%%aiBMPm;nV<{@9Dv)&=u&4#CON49W@@U8vJ zmwPLQ7Y&;G$J2=9yhQq(OMt%tcqBS3{z<2>O72K1wx$i#OUf zqHtyKy9{h#3wnc$_YKrsd_Xetf+QWYn$jJim->hB4&;mS1padI;u=;$k8c-QZbDk~ zXz*C9=F)kz#$ul;s6@*N6i$Ie6n6bR7d8kO&X1ASuD(!$fkuaL~c&gM%zehwyySOJI9tR2V$yxX! z^TlI0+(Bpszg2PfKElU`1$HST2crWIhHF4Dw1a?k;Fv4LJpS~3pNDmNsmhZ!{ZD5+ z&)=Q#G~oA3zW(NnN0_oS^aSU#c#2L#=&6Poc+PuICR1ujE=^>YTeTCEt-~cN(Kj$p zwFg!&P;a|4Imtn9(K36HDr4vVdi5!xPJAgeE!f3H&f8WzOAqqxeqcjJ;vL)KcW-1J zGU&JXP_0J|u_IqYZ(K1-ogNs7#FxA%!o6ikt=2O;LWmJ8@Cue8%RKp9NVLrSgCyY^ zOYHS$jAUL)Rga*SdF5a>;#|UXOoFH|8D~~b!SdlCx+9MFY+0=3T4&z#DUFRVAYVM| zne%8uj)U&6pKrNqwO+o{_7&to!` zU+7%}n~tkW-$cE2)5*#-n49im)RoRgeAsq{+w3O@Fi^`zeC{!U)Vpilw5xA6AolJE zI4jWz23ofRe1)0Irc&G${mQ9t4X*dhU$f!`&A z*aaq(i#)$sm*h!KU?AC*B(`YvR0pp8t3_K@I8mD~Sky=eij{|2f6#Xm=es*pO{`)J zQ)KyfQGMXl)ob<}q&pO3*`fz8uhXK1a;h$=Rs~u;JhozVI&aMvsxqn?rhCnZuWi{P zICa`~qF(etr|%4TXUG-ppvCz2*N`LbP9uYNGqebg`ynLuHp;_m zNqX6~FKHiV$g+kBwFW9<^79@ZMZ>{YXNR{EXa`-u{L{SVZ)vy%<0v#z$|ffpl_MmB z&aq7(LnUEX9Z5xiy;y>;2#;5cpDMbRYJxf;mdp<+7CrZeQ;Scj? zi#e7ec&<3bBBGZHWC#Ah*OFjtnwP8xH^_vHg1vZS;`w)fyu*PQP1%?s^1Mq7EQDPl zvvgB9Wo6CpMQ_Uw$O8J6Et1!fZjHXiez%DkW1)Zd=;zU>`w}gG%=l&0v}v&5W3C7D zmsxSgx#hRkg)8$TS}w1fwcETK@d=s2{LtQnyuhaZ$cAYB$~NKYTxba*l@?ckbMo

*$1o>Q6wYRA-28ku3 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 a2MinX) and (corner_y <= a2MaxY and corner_y >= a2MinY): + overlap = True + print("TRUE!!!!! ------------->") + # print(corner_x < a2MaxX, corner_x > a2MinX) + # print([corner_x, corner_y], a2MaxX, a2MinX, a2MaxY, a2MinY) + # print(a1MaxX, a1MinX, a1MaxY, a1MinY, '\n') + return overlap + +def addMoreEdges(nearby_platforms, G, Array, node, node_id, failures_c, failures_p, ids): + for child in failures_c[node]: + for id in ids: + if child + "\n" + str(id) in G.nodes: + G.add_edge(node + "\n" + str(node_id), child + "\n" + str(id), weight=0.001) + for parent in failures_p[node]: + for id in ids: + if parent + "\n" + str(id) in G.nodes: + G.add_edge(parent + "\n" + str(id), node + "\n" + str(node_id), weight=0.001) + return G + +def edgeReweight(G, edge): + new_weight = input("Enter weight for (" + str(edge[0].replace("\n", " ")) + ", " + str(edge[1].replace("\n", " ")) + ") (press \'Enter\' for default value)") + if new_weight=='': + new_weight=0.01 + G[edge[0]][edge[1]]['weight']=float(new_weight) + return G + +def get_y_Value(point1, point2, x): + return ((point2[1]-point1[1])/(point2[0]-point1[0]))*(x-point1[0])+point1[1] + +def get_min_max_vals(pnt2, pnt1, angle_radians): + vector = pnt2 - pnt1 + # print('vect', vector) + length = math.sqrt(vector[0]**2 + vector[1]**2) + if vector[0] == 0.0 and vector[1] > 0: angle = math.pi/2 + elif vector[0] == 0.0 and vector[1] < 0: angle = 3*math.pi/2 + elif vector[0] == 0.0 and vector[1] == 0: angle = 0.0 + else: angle = math.atan(vector[1]/vector[0]) + if angle == 0 and vector[0] < 0: + angle = math.pi + if (angle > -math.pi*0.5 and angle < math.pi*0.5) and vector[0] < 0: + angle += math.pi + # print('angle', angle) + new_vector1 = np.array([length * math.cos(angle - angle_radians), length * math.sin(angle - angle_radians)]) + np.array(pnt1) + new_vector2 = np.array([length * math.cos(angle + angle_radians), length * math.sin(angle + angle_radians)]) + np.array(pnt1) + if angle == math.pi and angle_radians==0: + new_vector1 = np.array([length * math.cos(angle - angle_radians), 0]) + np.array(pnt1) + new_vector2 = np.array([length * math.cos(angle + angle_radians), 0]) + np.array(pnt1) + max_x = max([new_vector1[0], new_vector2[0], pnt1[0], pnt2[0]]) + min_x = min([new_vector1[0], new_vector2[0], pnt1[0], pnt2[0]]) + max_y = max([new_vector1[1], new_vector2[1], pnt1[1], pnt2[1]]) + min_y = min([new_vector1[1], new_vector2[1], pnt1[1], pnt2[1]]) + if max_x == min_x: + if max_x < 0: max_x = 0 + elif min_x > 0: min_x = 0 + return max_x, min_x, max_y, min_y + +# Create project class instance from yaml file +Array = Project(file='famodel/OntologySample600m.yaml') + +# Create adjacency matrix from failure matrix +df = pd.read_excel("/Users/eslack/Documents/Code/ExcelFiles/failureData.xlsx", sheet_name="bMMatch") +arr = df.to_numpy()[:,1:] +nodeNames = df.to_numpy()[:, 0].flatten() + +# Get initial failure probabilities for each failure mode and effect +probabilities, array_of_probs = getProbabilities("failureProbabilities.xlsx", "Sheet3") + + +# Determine angle of clashing we are interested in +# angle_degree = float(input("What angle do you want to use? (in degrees) ")) +# angle_radians = angle_degree/360 * math.pi * 2 +angle_radians = 30/360 * math.pi * 2 + +# Initialize and create the dictionaries of the children, parents, and probabilities for each failure +failures_c = {} +failures_p = {} +probability_dict = {} + +for i in range(arr.shape[0]): + node_children = [] + node_parents = [] + for j in range(arr.shape[1]): + if arr[i,j] > 0: + node_children.append(nodeNames[j]) + if arr[j,i] > 0: + node_parents.append(nodeNames[j]) + failures_c.update({nodeNames[i]: node_children}) + failures_p.update({nodeNames[i]: node_parents}) + probability_dict.update({nodeNames[i]: probabilities[i]}) + +# Create dictionary for each subsystem of failures +turbine = [0,1,2,3,4,5,26,27,28,29] +platform = [6,7,8,9,10,11,30,31,32] +mooringmooring = [12] +mooringcable = [13,14] +mooring_materials = [33,34,35] +mooring = [15,16,17] +connector = [36] +clump_weight = [37] +anchor = [19,20,38] +cable = [21,22,23,41,42, 45,46] +cable_mat = [43,44] +grid = [24,25] +buoyancy = [40] +sharedmooring = [15,16,18] +sharedanchor = [19,20,39] +systems = {'turbine':nodeNames[turbine], 'platform':nodeNames[platform], 'mooringmooring':nodeNames[mooringmooring], + 'moor_mat':nodeNames[mooring_materials], 'mooringcable':nodeNames[mooringcable], 'cablemooring':nodeNames[mooringcable], + 'mooring':nodeNames[mooring], 'connector':nodeNames[connector], 'weight':nodeNames[clump_weight], + 'anchor':nodeNames[anchor], 'cable':nodeNames[cable], 'grid':nodeNames[grid], 'cable_mat':nodeNames[cable_mat], + 'buoyancy':nodeNames[buoyancy], 'cablecable': [], 'sharedmooring':nodeNames[sharedmooring], + 'sharedmooringcable':nodeNames[mooringcable], 'cablesharedmooring':nodeNames[mooringcable], 'sharedmooringmooring':nodeNames[mooringmooring], + 'mooringsharedmooring':nodeNames[mooringmooring], 'sharedanchor': nodeNames[sharedanchor]} + + +# Initialize graph, boolean for plotting, and list of probabilities +G = nx.DiGraph() +plot = False + +# FIRST DEGREE NODES ------------------------------------------------------------------------------------------- +for platform in Array.platformList: + # print(platform, Array.platformList[platform].r) + attachments = Array.platformList[platform].attachments + nearby_platforms = [] + mooring_clashes = [] + cable_clashes = [] + + # Create platform failure nodes + for platform_failure in systems['platform']: + G.add_node(platform_failure + "\n" + str(platform)) + G = addMoreEdges(nearby_platforms, G, Array, platform_failure, platform, failures_c, failures_p, [platform]) + + # Create failure nodes + for turbine_failure in systems['turbine']: + G.add_node(turbine_failure + "\n" + str(platform)) + G = addMoreEdges(nearby_platforms, G, Array, turbine_failure, platform, failures_c, failures_p, [platform]) + + # FIRST DEGREE EDGES ------------------------------------------------------------------------------------------- + for attach1 in attachments.keys(): + attach1_name = str(attachments[attach1]['id']) + attach1_type = '' + if 'mooring' in str(type(attachments[attach1]['obj'])): + if attachments[attach1]['obj'].shared: attach1_type = 'sharedmooring' + else: attach1_type = 'mooring' + elif 'cable' in str(type(attachments[attach1]['obj'])): attach1_type = 'cable' + + subcomponents = attachments[attach1]['obj'].subcomponents + print('subcomponents') + for component in subcomponents: + print(component) + if 'mooring' in attach1_type: + print(component.keys()) + + # Create moroing/cable failure nodes + for attach1_failure in systems[attach1_type]: + G.add_node(attach1_failure + "\n" + attach1_name) + G = addMoreEdges(nearby_platforms, G, Array, attach1_failure, attach1_name, failures_c, failures_p, [platform, attach1_name]) + + # Create clashing failure nodes + for attach2 in attachments.keys(): + attach2_name = str(attachments[attach2]['id']) + attach2_type = '' + clash_name = str(attach1_name)+str(attach2_name) + if 'mooring' in str(type(attachments[attach2]['obj'])): attach2_type = 'mooring' + elif 'cable' in str(type(attachments[attach2]['obj'])): attach2_type = 'cable' + for clash_failure in systems[(str(attach1_type)+str(attach2_type))]: + # print('could clash --', couldClash(G, clash_failure, attachments[attach1]['obj'], attachments[attach2]['obj']), '--', attach1_name, attach2_name) + if couldClash(G, clash_failure, attachments[attach1]['obj'], attachments[attach2]['obj']): + G.add_node(clash_failure + "\n" + clash_name) + G = addMoreEdges([attach1_name, attach2_name], G, Array, clash_failure, clash_name, failures_c, failures_p, [platform, attach1_name, attach2_name, clash_name]) + + if attach1_type == 'mooring' and attach2_type == attach1_type: mooring_clashes.append(clash_failure + "\n" + clash_name) + else: cable_clashes.append(clash_failure + "\n" + clash_name) + + # SECOND ORDER NODES ------------------------------------------------------------------------------------------- + attached_to = attachments[attach1]['obj'].attached_to + for attach1A in attached_to: + attach1A_name = str(attach1A.id) + + # Create anchor failure nodes + if 'anchor' in str(type(attach1A)): + attach1A_type = 'anchor' + if len(attach1A.attachments) > 1: attach1A_type = 'sharedanchor' + for anchor_failure in systems[attach1A_type]: + G.add_node(anchor_failure + "\n" + attach1A_name) + G = addMoreEdges(nearby_platforms, G, Array, anchor_failure, attach1A_name, failures_c, failures_p, [platform, attach1_name, attach1A_name]) + + # Create edges between platforms + elif 'platform' in str(type(attach1A)): + attach1A_type = 'platform' + attach1A_name = attach1A.id + for platform_failure in systems['platform']: + G = addMoreEdges(nearby_platforms, G, Array, platform_failure, platform, failures_c, failures_p, [platform, attach1A_name]) + + # Create substation/grid failure nodes + elif 'substation' in str(type(attach1A)): + attach1A_type = 'substation' + for grid_failure in systems['grid']: + G.add_node(grid_failure + "\n" + attach1A_name) + G = addMoreEdges(nearby_platforms, G, Array, anchor_failure, attach1A_name, failures_c, failures_p, [platform, attach1_name, attach1A_name]) + + if len(mooring_clashes) < 1: + G.add_node(systems['mooringmooring'] + "\n" + str(platform)) + G = addMoreEdges(nearby_platforms, G, Array, systems['mooringmooring'], str(platform), failures_c, failures_p, [platform]) + + +# print('\n') +# for node in G.nodes: +# print(node.replace('\n', ' ')) \ No newline at end of file diff --git a/failure/failureProbabilities.py b/failure/failureProbabilities.py new file mode 100644 index 00000000..7322e725 --- /dev/null +++ b/failure/failureProbabilities.py @@ -0,0 +1,741 @@ +import numpy as np +import networkx as nx +import matplotlib.pyplot as plt +import scipy.stats +import copy +import math +import random + +from failure.graphBuilder import * +from failure.searchMethods import * +from failure.twoTurbineCaseStudy import * + +''' failureProbability ------------------------------------------------------------------------------------------------------------- + + ****************************** Last Updated: 23 April 2024 ****************************** + + Methods: + 1) transition_natrix: inputs adjacency matrix, probabilities --> output conditional probabilities, scaled probabilities + + 2) conditional_probabilities_update: input start node, list of probabilities --> output conditional probabilities + + 3) single_simulation: input start node, adjacency martrix, list of node nanes, update boolean --> output graph generated + + 4) single_backward_simulation: input start node, adjacency martrix, list of node nanes, update boolean --> output graph generated + + 5) monte_carlo_sim: number of iterations, plotting boolean, starting nodes, adjacency matrix, list of node names, random seed + booleanm, midpoint boolean --> output average probabilities, similarity between average and conditional probabilities + + 6) single_conditional_prob: inputs first probability, second probability, midpoint boolean --> output conditional probability + + 7) multi_cond_prob: input parent nodes, current nodes, probabilities, midpoint boolean --> output conditional probability + + 8) bayesian_table: input adjacency matrix, current node, midpoint boolean, node names, alternate probabilties boolean, + alternate probabilities, multiple turbines boolean --> output parents list, probability distribution table + + 9) write_bayesian_probabilities: input adjacency matrix, node names, probabilties, multiple turbines boolean, midpoint boolean calculation, + number of iterations --> writes probability distribution tables to Excel file (nothing returned) + +10) probability_over_time: input failure rate, time --> output probability of a failure at time t + +11) bayesian_inference: input adjacency matrix, node names, indicator nodes, evidence nodes, hypothesis nodes, probabilties, hypothesis +boolean, printing boolean, multiple turbine boolean, parent or child indicator, twoTurbine boolean --> outputs inference probabilities + +12) backward_bayesian_inference: input adjacency matrix, list of node names, array of nodes to start with, array of evidence nodes, + array of hypothesis nodes, probabilties, boolean for indexing into the right values of the array, boolean for multiple turbines, + parent or child indicator, boolean for twoTurbine method --> output two arrays of inference probabilities (one normalized and one not) + +------------------------------------------------------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------------------------------------------------------ + + + transition_natrix Documentation + ---------------------- + This method inputs an adjacency matrix and vector of probabilities (corresponding to the probabilitiy that each + failure will happen). Since the data we have does not tell us the probability that any two event will happen, we + find the lower and upper bounds of what the conditional probability of every pair of related events (related indicated + by the adjacency matrix). We then take the median of these values as the probability that the second failure will + occur given that the first already occured. This method returns the array of these conditional probabilities and + a scaled array of these probabilities (scaled so that the sum of the columns is 1).''' + +def transition_matrix(arr, probabilities, mid_point = True): + arr = make_binary(arr) # Array to show which failures lead to other failures + nodes = diagonal_nodes(arr) # Matrix with numbers on the diagonal + + bounds = [] # Initialize list of upper and lower bounds + transitions = np.zeros(arr.shape) # Initialize matrix of conditional probabilities + + for node in range(arr.shape[0]): # For each node... + children_bool = arr[node] @ nodes # Find the children of the node + children = children_bool[np.nonzero(children_bool)] + + parent_probability = probabilities[node][0] # Find the probabilitiy of the node occuring + lower_bound = [] # Initialize lower bounds of all children of the node + upper_bound = [] # Initialize upper bounds of all children of the node + + for child in children: # For each of the node's children... + child_probability = probabilities[child - 1][0] # Find the probability of the child occuring + + # The lower bound is the probability of the child occuring (i.e. child and parent are completely indepedent evens) + lb = child_probability + # The upper bound is the overlap of the child and parent probabilities (i.e. child and parent are completely dependent events) + ub = min(parent_probability, child_probability)/parent_probability + + if mid_point: + tm_val = (lb + ub) / 2 # Calculate the midpoint of the two bounds + # print("Midpoint = True") --> for debugging + else: + rand_val = np.random.rand() + tm_val = rand_val * (ub - lb) + lb + # print(rand_val, tm_val, tm_val == ((lb + ub) / 2), lb, ub) --> for debugging + # print("Midpoint = False") --> for debugging + + transitions[node][child - 1] = tm_val + lower_bound.append(lb) # Append the lower bound to the list + upper_bound.append(ub) # Append the upper bound to the list + + lower_bound = np.array(lower_bound) # Convert lower and upper bounds to numpy arrays + upper_bound = np.array(upper_bound) + + midpoint = (upper_bound + lower_bound) / 2 # Calculate the midpoints for all the children of the parent node + + bounds.append([lower_bound, upper_bound, midpoint]) # Append the lower, upper, and midpoints to the list + + transition_matrix = transitions / np.sum(transitions, axis = 0) # Scale the conditional probabilities + transition_matrix[np.isnan(transition_matrix)] = 0 # Replace any NaN values with 0s + return transitions, transition_matrix # Return conditional probabilities and scaled conditional probabilities + +''' conditional_probabilities_update Documentation + -------------------------------------------------- + This method inputs a start value (indicating the starting node) and a list of probabilities that each node will + occur. We then compute conditional probabilities of all the nodes given the starting node. This method returns + the list of calculated conditional probabilities.''' + +def conditional_probabilities_update(start_arr, probabilities): + # Create vector of the parent probabilities + for start in start_arr: + parent_probability = np.reshape(np.repeat(probabilities[start][0], probabilities.shape[0]), probabilities.shape) + + # Calculate the conditional probabilities (using the midpoint method described in transition_matrix() method) + conditional_probabilities = (probabilities + np.reshape(np.min(np.hstack((probabilities, parent_probability)), axis = 1), probabilities.shape)/parent_probability[0])/2 + probabilities = conditional_probabilities + + return conditional_probabilities # Return the calculated probabilities + +''' bayesian_probabilities Documentation + -------------------------------------------------- + This method inputs a start value (indicating the starting node) and a list of probabilities that each node will + occur. We then compute conditional probabilities of all the nodes given the starting node. This method returns + the list of calculated conditional probabilities.''' + +def bayesian_probabilities(parents, child, probabilities): + for i in range(len(parents)): + rand_val = np.random.rand() + + + +''' single_simulation Documentation + --------------------------------------------- + This method inputs a starting node, adjacency martrix, list of node nanes, and a boolean that either uses the method of + updating conditional probabilities based on already visited nodes. We then compute the failure propagation and graph the + propagation via Networkx. This method returns the graph generated.''' + +def single_simulation(start_arr, arr, nodeNames, update = False, plot = False, rand_seed = True, mid_point = True): + monte_carlo_array = np.zeros(arr.shape) + probs = np.zeros((arr.shape[0], 1)) + + # Use the probabilities from the COREWIND failure rates + probabilities = np.array([0.0195, 0.0195, 0.013625, 0.0055, 0.0175, 0.2075, 0.001, 0.001, 0.001, 0.093185, 0.001, 0.001, + 0.027310938, 0.033968125, 0.033968125, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, + 0.0205, 0.0205, 0.02, 0.01, 0.01, 0.233, 0.288, 0.543374, 0.1285, 0.01, 0.01, 0.01, 0.015, 0.0155, + 0.015, 0.0155, 0.015, 0.0155, 0.015, 0.33, 0.025, 0.025, 0.025, 0.025, 0.025, 0.105]) #0.01375, + probabilities = np.reshape(probabilities, (arr.shape[0], 1)) # Reshape these probabilities into a vector + + G = nx.DiGraph() # Initialize a graph and a list of modes and effects for plotting nodes in different colors + effects = [] + modes = [] + + if update: # If you want to update the probabilities, update the probabilities given the indicated starting node + probabilities = conditional_probabilities_update(start, probabilities) + transitions, tm = transition_matrix(arr, probabilities, mid_point) # Compute the probabilities for all linked nodes + + if rand_seed: random.seed(16) # Use a random seed to get the same results (yet random results) each time + + adj = make_binary(arr).astype(int) # Make a binary adjacency matrix to determine the children of each node + nodes = diagonal_nodes(arr) # Diagonal array of node indices + 1 + + nodeList = np.reshape(np.repeat(False, arr.shape[0]), (arr.shape[0], 1)) # Create a list ot determine which nodes have been visited + gens = {} + queue = [] + + for start in start_arr: + G.add_node(str(0) + ": " + str(nodeNames[start-1])) + nodeList[start-1][0] = False + queue.append([start, 0, 0]) + gens.update({str(0) + ": " + str(nodeNames[start-1]): {"layer": 0}}) + # Determine if the source node is an effect or a mode, and add it to the correct array + '''if source < 49: modes.append(nodeNames[source - 1]) + else: effects.append(nodeNames[source - 1])''' + if start < 27: effects.append(nodeNames[start - 1]) # Add the start node to either the effects or mode list + else: modes.append(nodeNames[start - 1]) + + while len(queue) > 0: # For each node in the queue... + current = queue[0] # Get the node from the front of the queue + children_bool = adj[current[0]-1] @ nodes # vector of zeros and child names (numerical names) + children = children_bool[np.nonzero(children_bool)] #list of just the child names (numerical names) + + for child in children: # For each child of the current node... + if nodeList[child - 1] == False or gens[nodeNames[child - 1]]['layer'] > current[1]: # If the node has not been visited... + if np.random.rand() < transitions[current[0] - 1][child - 1]: # If the random value is less than the probability... + monte_carlo_array[current[0] - 1][child - 1] = 1 + probs[child - 1] = 1 + G.add_node(nodeNames[child-1]) # Add the child to the graph + if update: # If updateing is desired, update the probabilities with the addition of the child node + probabilities = conditional_probabilities_update(current[0]-1, probabilities) + transitions, tm = transition_matrix(arr, probabilities, mid_point) + + # Determine if the child is an effect or mode and add it to the correct array + if child < 27: effects.append(nodeNames[child - 1]) + else: modes.append(nodeNames[child - 1]) + + # Add an edge between the current node and child in the tree we are building + G.add_edge(nodeNames[current[0]-1], nodeNames[child-1]) + + queue.append([child, current[1]+1]) # Append the child to the queue + nodeList[child - 1] = True # Change the status of the child to say we have visited it + gens.update({nodeNames[child-1]: {"layer": current[1]+1}}) # Add the child to the layer dictionary + queue = queue[1:] # Remove the current node from the queue + + nx.set_node_attributes(G, gens) # Set the layers of the graph + + # Plot the graph + if plot: + pos = nx.multipartite_layout(G, subset_key='layer') + nx.draw_networkx_nodes(G, pos, nodelist=effects, node_color="#98c5ed", node_size=2700, edgecolors="#799dbd", node_shape="s") + nx.draw_networkx_nodes(G, pos, nodelist=modes, node_color="#fabc98", node_size=2700, edgecolors="#c89679", node_shape="s") + nx.draw_networkx_labels(G, pos, font_size=10, verticalalignment='center_baseline') + nx.draw_networkx_edges(G, pos, arrowsize=60) + plt.box(False) + plt.show() + + # draw_bfs_multipartite(arr, nodeNames, start, "child") # Plot the graph when all probabilities are one for comparison + return G, monte_carlo_array, probs # Return the graph + + + + +''' single_backward_simulation Documentation + --------------------------------------------- + This method inputs a starting node, adjacency martrix, list of node nanes, and a boolean that either uses the method of + updating conditional probabilities based on already visited nodes. We then compute the backward failure propagation and graph the + propagation via Networkx. This method returns the graph generated.''' + +def single_backward_simulation(start_arr, arr, nodeNames, update = False): + # Use the probabilities from the COREWIND failure rates + probabilities = np.array([0.0195, 0.0195, 0.013625, 0.0055, 0.0175, 0.2075, 0.001, 0.001, 0.001, 0.093185, 0.001, 0.001, + 0.027310938, 0.033968125, 0.033968125, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, + 0.0205, 0.0205, 0.02, 0.01, 0.01, 0.233, 0.288, 0.543374, 0.1285, 0.01, 0.01, 0.01, 0.015, 0.0155, + 0.015, 0.0155, 0.015, 0.0155, 0.015, 0.33, 0.025, 0.025, 0.025, 0.025, 0.025, 0.105]) #0.01375, + probabilities = np.reshape(probabilities, (arr.shape[0], 1)) # Reshape these probabilities into a vector + + G = nx.DiGraph() # Initialize a graph and a list of modes and effects for plotting nodes in different colors + effects = [] + modes = [] + queue = [] + gens = {} + + if update: # If you want to update the probabilities, update the probabilities given the indicated starting node + probs = conditional_probabilities_update(start, probabilities) + else: probs = probabilities + transitions, tm = transition_matrix(arr.T, probs) # Compute the probabilities for all linked nodes + + # random.seed(16) # Use a random seed to get the same results (yet random results) each time --> feel free to comment out + + adj = make_binary(arr).astype(int) # Make a binary adjacency matrix to determine the children of each node + nodes = diagonal_nodes(adj) # Create a matrix with node names to determine the children of each node + nodeList = np.reshape(np.repeat(False, arr.shape[0]), (arr.shape[0], 1)) # Create a list ot determine which nodes have been visited + + for start in start_arr: + G.add_node(str(0) + ": " + str(nodeNames[start-1])) + nodeList[start-1][0] = False + queue.append([start, 0, 0]) + gens.update({str(0) + ": " + str(nodeNames[start-1]): {"layer": 0}}) + # Determine if the source node is an effect or a mode, and add it to the correct array + '''if source < 49: modes.append(nodeNames[source - 1]) + else: effects.append(nodeNames[source - 1])''' + if start < 27: effects.append(nodeNames[start - 1]) # Add the start node to either the effects or mode list + else: modes.append(nodeNames[start - 1]) + + while len(queue) > 0: # For each node in the queue... + current = queue[0] # Get the node from the front of the queue + children_bool = adj[current[0]-1] @ nodes # vector of zeros and child names (numerical names) + children = children_bool[np.nonzero(children_bool)] #list of just the child names (numerical names) + + for child in children: # For each child of the current node... + if nodeList[child - 1] == False or gens[nodeNames[child - 1]]['layer'] < current[1]: # If the node has not been visited... + if np.random.rand() < transitions[current[0] - 1][child - 1]: # If the random value is less than the probability... + G.add_node(nodeNames[child-1]) # Add the child to the graph + if update: # If updateing is desired, update the probabilities with the addition of the child node + probs = conditional_probabilities_update(current[0]-1, probs) + transitions, tm = transition_matrix(arr.T, probs) + + # Determine if the child is an effect or mode and add it to the correct array + if child < 27: effects.append(nodeNames[child - 1]) + else: modes.append(nodeNames[child - 1]) + + # Add an edge between the current node and child in the tree we are building + G.add_edge(nodeNames[child-1], nodeNames[current[0]-1]) + + queue.append([child, current[1]-1]) # Append the child to the queue + nodeList[child - 1] = True # Change the status of the child to say we have visited it + gens.update({nodeNames[child-1]: {"layer": current[1]-1}}) # Add the child to the layer dictionary + queue = queue[1:] # Remove the current node from the queue + + nx.set_node_attributes(G, gens) # Set the layers of the graph + + # Plot the graph + # pos = nx.multipartite_layout(G, subset_key='layer') + # nx.draw_networkx_nodes(G, pos, nodelist=effects, node_color="#98c5ed", node_size=2700, edgecolors="#799dbd", node_shape="s") + # nx.draw_networkx_nodes(G, pos, nodelist=modes, node_color="#fabc98", node_size=2700, edgecolors="#c89679", node_shape="s") + # nx.draw_networkx_labels(G, pos, font_size=10, verticalalignment='center_baseline') + # nx.draw_networkx_edges(G, pos, arrowsize=60) + # plt.box(False) + # plt.show() + return G, nx.to_numpy_array(G), probs # Return the graph + + + + +''' monte_carlo_sim Documentation + --------------------------------------------- + This method inputs the number of iterations, a boolean for plotting the average graph or not, an array of the nodes to start with, + an adjacency matrix, list of nodeNames, boolean for a random seed, and a boolean for using a midpoint calculation. We then generate + a graph with failure probabilities for the number of iterations and find the average graph and the probability that each node + is in the graph (the number of times the node shows up divided by the number of iterations). Lastly, we calculate the similarity + between the probability of the nodes in the graph (that we just calculated) compared to the estimated probability calculated via + conditional probabilities. We return the first list of probabilities and cosine similarity between the two lists of probabilities.''' + +def monte_carlo_sim(num_iterations, plot, start_arr, adjacency_matrix, nodeNames, rand_seed, mid_point): + # Initialize the adjacency matrix to trrack which nodes are in each graph (which we will then calculate an average from) + adj_matrices = np.zeros(adjacency_matrix.shape) + + # Initialize array for probabilities to track average probabilities for each node + probs = np.zeros((adjacency_matrix.shape[0], 1)) + + # List of probabilities for each failure happening + probabilities = np.array([0.0195, 0.0195, 0.013625, 0.0055, 0.0175, 0.2075, 0.001, 0.001, 0.001, 0.093185, 0.001, 0.001, + 0.027310938, 0.033968125, 0.033968125, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, + 0.0205, 0.0205, 0.02, 0.01, 0.01, 0.233, 0.288, 0.543374, 0.1285, 0.01, 0.01, 0.01, 0.015, 0.0155, + 0.015, 0.0155, 0.015, 0.0155, 0.015, 0.33, 0.025, 0.025, 0.025, 0.025, 0.025, 0.105]) #0.01375, + probabilities = np.reshape(probabilities, (adjacency_matrix.shape[0], 1)) + + # Run each simulaiton + for i in range(num_iterations): + arr = copy.deepcopy(adjacency_matrix) + G, adj_mat, prob = single_simulation(start_arr, arr, nodeNames, rand_seed = rand_seed, mid_point = mid_point) + adj_matrices += adj_mat.astype(float) + probs += prob + # print(i+1) # Debugging --> feel free to uncomment + + # Calculate average graph and average probabilities + adj_matrices = adj_matrices/num_iterations + probs = probs/num_iterations + K, abc = matrix2Graph(adj_matrices, nodeNames, True) + + # Calculate similarity between average and conditional probabilities + v1 = conditional_probabilities_update(start_arr, probabilities) + v2 = probs + + # Plot average graph + if plot: + draw_bfs_multipartite(adj_matrices, nodeNames, start_arr, "child") + return v2, cosine_similarity(v1, v2) # Return average probabilities and similarity of average and conditional probabilities + + + + +''' single_conditional_prob Documentation + --------------------------------------------- + This method inputs the probability of first failure, probability of second failure, and boolean for using the midpoint calculation + or not. We use these probabilities to estimate the conditional probability between the two failures. We return this conditional probability.''' + +def single_conditional_prob(pa, pb, mid_point = True): + lb = pb * pa # Lower bound for conditional probability + ub = min(pa, pb) # Upper bound for conditional probability + if mid_point: + overlap = (lb + ub) / 2 # Find midpoint between the lower and upper bounds + else: + rand_val = np.random.rand() + overlap = rand_val * (ub - lb) + lb # Find a random value between the lower and upper bounds + return overlap/pa # Return the conditional probability + + + + +''' multi_cond_prob Documentation + --------------------------------------------- + This method inputs array of parent nodes, current nodes, array of probabilities, and boolean for using a midpoint calculation or not. We + calculate the conditional probability when the node has multiple parents (i.e. probability of current event given the parent events). This + method outputs the conditional probability.''' + +def mult_cond_prob(parents, current, probabilities, midpoint = True): + if len(parents) < 1: + return probabilities[current - 1] # If no parents, return the probability of the current node + + # Create list of parent node probabilities, with the probability of the current node appended to the end + parents_sub = [int(parent - 1) for parent in parents] + parents_sub.append((current - 1)) + parents_sub = np.array(parents_sub) + parent_probs = probabilities[parents_sub] + # Initialize denomenator + denomenator = 1 + + # Calculate the single conditional probability for pairs of events until there are no events left + while len(parent_probs) > 1: + if (not isinstance(parent_probs[1][0], float)) and len(parent_probs[1][0]) > 1: + parent_probs[1][0] = parent_probs[1][0][1] + lb = parent_probs[0][0] * parent_probs[1][0] # Lower bound for conditional probability + ub = min(parent_probs[0][0], parent_probs[1][0]) # Upper bound for conditional probability + if midpoint: + val = (lb + ub) / 2 # Find midpoint between the lower and upper bounds + else: + rand_val = np.random.rand() + val = rand_val * (ub - lb) + lb + parent_probs = np.vstack(([val], parent_probs[2:])) + if len(parent_probs) == 2: + denomenator = parent_probs[0][0] + # print(parent_probs[0][0], denomenator, parent_probs[0][0]/denomenator) + return parent_probs[0][0]/denomenator # Return the conditional probability + + + + +''' bayesian_table Documentation + --------------------------------------------- + This method inputs adjacency matrix, current node, boolean for midpoint calculation, list of node names, boolean for alternate probabilties, + array of new probabilities, and boolean for calculations among multiple turbines. We calculate the probability distribution and format it + into a table. This method outputs the list of parents and the probability distribution table.''' + +def bayesian_table(adj, current, midpoint, nodeNames, pvs = False, prob_vals=None, mult_turbine = False): + nodes = diagonal_nodes(adj) # Diagonal matrix of node's numerical names +1 + adj = make_binary(adj, 0.5) # Binarize adjacency matrix + + parents_bool = nodes @ adj[:, current-1] # vector of zeros and parent names (numerical names) + parents = list(parents_bool[np.nonzero(parents_bool)]) # list of just the parent names (numerical names) + prob_table = np.zeros((2 ** len(parents), len(parents) + 2)) # Initialize the array for the node's conditional probability distribution + + # *** Code in development for multiple turbine bayesian tables *** + arr_nonbinary = adj.copy() + num_parents = len(parents) + + '''if mult_turbine: + for p in range(num_parents): + if arr_nonbinary[parents[p] - 1][current - 1] > 1: + print("Unlocked!", parents[p]) + parents.append(parents[p])''' + # *** End of code in development ********************************* + + for iteration in range(2 ** len(parents)): # For each combination of parents having either failed or not + if pvs: # Determine the probabilities being used + probabilities = prob_vals.copy() + else: + probabilities = np.array([0.0195, 0.0195, 0.013625, 0.0055, 0.0175, 0.2075, 0.001, 0.001, 0.001, 0.093185, 0.001, 0.001, + 0.027310938, 0.033968125, 0.033968125, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, + 0.0205, 0.0205, 0.02, 0.01, 0.01, 0.233, 0.288, 0.543374, 0.1285, 0.01, 0.01, 0.01, 0.015, 0.0155, + 0.015, 0.0155, 0.015, 0.0155, 0.015, 0.33, 0.025, 0.025, 0.025, 0.025, 0.025, 0.105]) #0.01375, + probabilities = np.reshape(probabilities, (len(probabilities), 1)) + # print(probabilities) + + true_false_array = [] # Iniitalize to record which parents have failed + + for p in range(len(parents)): + probabilities[int(parents[p]-1)] = abs((int(iteration / (2 ** p)) % 2) - probabilities[int(parents[p]-1)]) # Determine parent probability (given if the parent has failed or not) + true_false_array.append((int(iteration / (2 ** p)) % 2)) # Append whether or not the parent node has failed + prob_table[iteration][p] = int(iteration / (2 ** p)) % 2 # Append whether or not the parent node has failed + + # if mult_turbine and nodeNames[int(current - 1)][:3] != nodeNames[int(parents[p] - 1)][:3]: #and p > 0) and parents[p] <= parents[p-1]: + # probabilities[int(parents[p]-1)] *= 0.7 # If there is more than one turbine, then decrease the probability of failure by 30% + prob = mult_cond_prob(parents, current, probabilities, midpoint) # Calculate the conditional probability of the node given if the parents have failed or not + # print(true_false_array, prob) + # print(probabilities) + prob_table[iteration][-2] = prob # Add calculated probability to the array + prob_table[iteration][-1] = 1 - prob # Add the probability of the node not failing to the array + #print(prob_table) + return parents, prob_table # Return a list of the parents and the probability distribution array + + + + +''' write_bayesian_probabilities Documentation + ---------------------------------------------- + This method inputs adjacency matrix, list of node names, probabilties, boolean for calculations among multiple turbines, + boolean for midpoint calculation, and number of iterations. We calculate the probability distribution for each node in the graph and format it + into a table. This method writes the probability distribution tables to an Excel file (nothing returned).''' + +def write_bayesian_probabilities(adjacency_matrix, nodeNames, probabilities, mult_turbine, midpoint=True, num_iterations=1, twoTurbines = False, name=""): + ps = probabilities.copy() + with pd.ExcelWriter("bayesianProbabilities"+name+".xlsx") as writer: # Write to file + for current in range(1, adjacency_matrix.shape[0]+1): # Repeat for each node in the graph + if any(probabilities != ps): + print("Unequal probabilities", current-1) + break + adjacency_matrix2 = adjacency_matrix.copy() + parents, our_table = bayesian_table(adjacency_matrix2, current, midpoint, nodeNames, True, probabilities, mult_turbine) # Probability distrubution table + if twoTurbines: + parents, our_table = twoTurbine_bayesian_table(adjacency_matrix, adjacency_matrix, current, nodeNames, nodeNames) # Probability distrubution table + parents = [int(parent) for parent in parents] # Format parent list + + # Find average probability distribution table for the number of iterations + for i in range(num_iterations - 1): + parents, our_table2 = bayesian_table(adjacency_matrix, current, midpoint, nodeNames = nodeNames, pvs=True, prob_vals=probabilities, mult_turbine=mult_turbine) + our_table += our_table2 + + # Update column titles for the probability distribution table + parents = [nodeNames[parent - 1].replace("\n", " ") for parent in parents] + parents = np.append(parents, "P(" + nodeNames[current - 1].replace("\n", " ") + " = True)") + parents = np.append(parents, "P(" + nodeNames[current - 1].replace("\n", " ") + " = False)") + df = pd.DataFrame(our_table, columns = parents) + + df.to_excel(writer, sheet_name=nodeNames[current - 1].replace("\n", " ").replace("/", " or ").replace(":", "")[:31]) # Write to file + print(current, nodeNames[current - 1].replace("\n", " ")[:31]) # Print current node to keep track of which nodes have a probability distribution table + + + + +''' probability_over_time Documentation + ---------------------------------------------- + This method inputs a failure rate, lamda, and a time, t. We return the probability of a failure at time t.''' + +def probability_over_time(lamda, t): + return 1 - math.exp((np.log(1-lamda)) * t) # Return the updated probability of failure given a certain time + + + + +''' bayesian_inference Documentation + ---------------------------------------------- + This method inputs adjacency matrix, list of node names, array of nodes to start with for generating the graph of the Bayesian network, array of + evidence nodes, array of hypothesis nodes, probabilties, boolean for finding hte probability of failure of the hypothesis, boolean for printing + information about the calculations, boolean for multiple turbines, parent or child calculation (string), and boolean for using the twoTurbine case + study method. We calculate Bayesian inference given the evidence and hypothesis variables. This method outputs an array of inference probabilities.''' + +def bayesian_inference(arr, nodeNames, indicator, num_evidence, num_hypothesis, probabilities, tf = True, printing = False, multi= False, poc="parent", twoTurbine = False): + # print("E", num_evidence) + # print("H", num_hypothesis) + a = arr.copy() + non = nodeNames + prblts = probabilities + evidence = num_evidence + hypothesis = num_hypothesis + if not multi: + K, a, g, e, m, non = breadth_first_multi(a, nodeNames, indicator, poc) # Generate tree for Bayesian network + # draw_bfs_multipartite(arr, nodeNames, indicator, type = "multi-"+poc, multi_turbine = False) + prblts = [] # Initialize array of node probabilities (in order of appearance in graph) + for node in non: + node_index = np.where(nodeNames == node)[0][0] + prblts.append(probabilities[node_index]) # Add nodes to array of node probabilities + prblts = np.array(prblts) + + evidence = [] + hypothesis = [] + for hypothesis_node in num_hypothesis: # Adjust hypothesis node so that index in tree matches up with index in original adjacency matrix + if twoTurbine: + non = np.array(non) + if nodeNames[hypothesis_node - 1] not in non: # If node is not in tree, then there is a 0% probability of failure + print("Probability table of", nodeNames[hypothesis_node - 1], "...", np.reshape(np.array([0,1]), (1,2))) + return np.reshape(np.array([0,1]), (1,2)), np.reshape(np.array([0,1]), (1,2)) + hypothesis.append(np.where(non == nodeNames[hypothesis_node - 1])[0][0]) + + for evidence_node in num_evidence: # Adjust evidence node so that index in tree matches up with index in original adjacency matrix + if twoTurbine: + non = np.array(non) + if nodeNames[evidence_node - 1] not in non: # If node is not in tree, then this is an error! + print("ERROR - evidence node, " + nodeNames[evidence_node - 1] + ", not in graph!") + if evidence_node in num_hypothesis:# If node is in hypothesis, then probability of failure is 100% + print("Probability table of", nodeNames[evidence_node - 1], "...", np.reshape(np.array([1,0]), (1,2))) + return np.reshape(np.array([1,0]), (1,2)), np.reshape(np.array([1,0]), (1,2)) + evidence.append(np.where(non == nodeNames[evidence_node - 1])[0][0]) + + probabilitiy_table = np.zeros((2, a.shape[0])) # Initialize table of inference probabilities + nodes = diagonal_nodes(a) # Diagonal matrix of node names (numerical +1) + a = make_binary(a, 0.5) # Binarize adjacency table + hypothesis_nodes_array = np.zeros((len(hypothesis), 2)) + + # Depending on tree (forward or backward propagation), either iterate through nodes forward or backwards + if poc == "parent": + this_range = reversed(range(a.shape[0])) + elif poc == "child": + this_range = range(a.shape[0]) + + for node in this_range: + pts_bool = nodes @ a[:, node] # vector of zeros and child names (numerical names) + pts = pts_bool[np.nonzero(pts_bool)] #list of just the child names (numerical names) + + if len(pts) < 1: # If no parents, the probability is the initial probability + probabilitiy_table[0][node] = prblts[node] + probabilitiy_table[1][node] = 1 - prblts[node] + continue + + parents, our_table = bayesian_table(a, node+1, True, nodeNames, True, prblts) # Calculate the probability distribution table + + # If only using two turbines (specific to case study), calculate using twoTurbine method + if twoTurbine: + parents, our_table = twoTurbine_bayesian_table(a, arr, node + 1, nodeNames, non) # Calculate the probability distribution table + mlt_table = np.ones((our_table.shape[0],2)) # Initialize table for multiplying across rows of probability distribution table + + for i in range(our_table.shape[0]): + for j in range(our_table.shape[1] - 2): + parent = int(parents[j]) + + # Replace boolean in probability distribution table with probability of this event + if our_table[i,j] == 0: + # print(a.shape, probabilitiy_table.shape) + our_table[i,j] = probabilitiy_table[0][parent - 1] + + # If the node is in the hypothesis or evidence array, do not include this + if probabilitiy_table[0][parent - 1] == 0: + # print("indexing_error!!", parent, node) + # print(non[parent], non[node]) + break + if (parent-1 in hypothesis and tf): # or (parent in evidence): + our_table[i,j] = 0 + # print("in hypto and tf or in evidence", parent) + else: + our_table[i,j] = probabilitiy_table[1][parent - 1] + + # If the node is in the hypothesis or evidence array, do not include this + if (parent-1 in hypothesis and not tf) or (parent-1 in evidence): + our_table[i,j] = 0 + # print("in hypto and not tf or in evidence", parent) + + mlt_table[i,0] *= our_table[i,j] # Multiply the probabilities across the probability distribution table + mlt_table[i,1] = mlt_table[i,0] * our_table[i, -1] # Multiple by the probability of event not failing given combination of parent failure + mlt_table[i,0] *= our_table[i, -2] # Multiple by the probability of event failing given combination of parent failure + + sm_table = np.sum(mlt_table, axis = 0) #/np.sum(mlt_table) # Sum the products of probabilities across the columns + + if node in hypothesis: + print("Probability table of", non[node].replace("\n", " "), "...", sm_table) + hypothesis_nodes_array[np.where(np.array(hypothesis) == node)[0][0]][0] = sm_table[0] + hypothesis_nodes_array[np.where(np.array(hypothesis) == node)[0][0]][1] = sm_table[1] + if all(hypothesis_nodes_array != 0): + return probabilitiy_table, hypothesis_nodes_array + + probabilitiy_table[0][node] = sm_table[0] # Update the inference probability table with the probabilites just calculated + probabilitiy_table[1][node] = sm_table[1] + + if printing: # Print the inference probability, along with the indicators, evidence, and hypothesis conditionals + print("Indicator:", nodeNames[np.array(indicator)]) + print("Evidence:", nodeNames[np.array(evidence)]) + print("Hypothesis:", nodeNames[np.array(hypothesis)]) + print("Probability of Failure:", probabilitiy_table[:, 0][0], probabilitiy_table[:, 0][0]/np.sum(probabilitiy_table[:, 0])) + print("Probability of No Failure:", probabilitiy_table[:, 0][1], probabilitiy_table[:, 0][1]/np.sum(probabilitiy_table[:, 0])) + return probabilitiy_table, hypothesis_nodes_array # Return array or inference probabilities + + + + +''' backward_bayesian_inference Documentation + ---------------------------------------------- + This method inputs adjacency matrix, list of node names, array of nodes to start with for generating the graph of the Bayesian network, array of + evidence nodes, array of hypothesis nodes, probabilties, boolean for indexing into the right values of the array, boolean for multiple turbines, parent or child + calculation type (string), and boolean for using twoTurbine case study method. We calculate Bayesian inference given the evidence and hypothesis variables. + This method outputs an array of inference probabilities (normalized and not).''' + +def backward_bayesian_inference(adjacency_matrix, nodeNames, indicator, evidence, hypothesis, probabilities, start_bool = True, multi = False, poc="parent", twoTurbine = False): + # Calculate Bayesian inference for hypothesis = True and hypothesis = False + #print("--- First Run of Bayesian Inference ----------------") + # print("Before bn", probabilities.shape) + printing = False + if len(evidence) > 0 and len(hypothesis)>0: + printing = False + + pt1, hnd1 = bayesian_inference(adjacency_matrix, nodeNames, indicator, evidence, hypothesis, probabilities, True, printing=printing, multi = multi, poc=poc, twoTurbine=twoTurbine) + #print() + #print("--- Second Run of Bayesian Inference ---------------") + if poc == "parent": + pt2, hnd2 = bayesian_inference(adjacency_matrix, nodeNames, indicator, evidence, hypothesis, probabilities, False, printing=printing, multi = multi, poc=poc, twoTurbine=twoTurbine) + # print("pt1", pt1[:, 0]) + # print("pt2", pt2[:, 0]) + # Choose correct indexing value + index = 1 + if start_bool: + index = 0 + + if multi: + return [hnd1[0][0]/(hnd1[0][0]+hnd1[0][1]), hnd1[0][1]/(hnd1[0][0]+hnd1[0][1])], [0,0] + + # Depending on what our evidence and hypothesis nodes are, index into the correct values + if poc == "parent" and 0 in evidence: + p_true = pt1[:, 0][index] + p_false = pt2[:, 0][index] + + elif poc == "parent" and 0 in hypothesis: + p_true = pt1[:, 0][0] + pt2[:, 0][0] + p_false = pt1[:, 0][1] + pt2[:, 0][1] + + elif poc == "child": + if hnd1.shape[0] == 1: + p_true = hnd1[0][0] + p_false = hnd1[0][1] + + else: # If multiple nodes in hypothesis, multiply all possibilities together to get normailized value + print("Starting hypothesis for-loops...") + p_false = 0 + for iteration in range(2 ** hnd1.shape[0]): + p_false_mult = 1 + for node in range(hnd1.shape[0]): + p_false_mult *= hnd1[node][int(iteration / (2 ** node)) % 2] + if iteration == 0: + p_true = p_false_mult + else: + p_false += p_false_mult + if (iteration) % 1000000000000 == 0: + print("Percent done:", iteration/(2 ** hnd1.shape[0]) * 100) + print("Percent done:", 100) + else: + p_true = np.sum(pt1[:, 0]) + p_false = np.sum(pt2[:, 0]) + + probability_distribution = [p_true/(p_true + p_false), p_false/(p_true + p_false)] # Normalize the probability distribution of the event happening + + return probability_distribution, [p_true, p_false] # Return the normalized probability distribution and unnormalized distribution + + + +''' +# ----------- For running the code: feel free to uncomment ------------------- +arr, nodeNames = excel2Matrix("failureData.xlsx", "bigMatrix") +#random.seed(18) +start = 30 #np.random.randint(0, arr.shape[0]) +probabilities = np.array([0.0195, 0.0195, 0.013625, 0.0055, 0.0175, 0.2075, 0.001, 0.001, 0.001, 0.093185, 0.001, 0.001, + 0.027310938, 0.033968125, 0.033968125, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, + 0.0205, 0.0205, 0.02, 0.01, 0.01, 0.233, 0.288, 0.543374, 0.1285, 0.01, 0.01, 0.01, 0.015, 0.0155, + 0.015, 0.0155, 0.015, 0.0155, 0.015, 0.33, 0.025, 0.025, 0.025, 0.025, 0.025, 0.105]) #0.01375, +probabilities = np.reshape(probabilities, (arr.shape[0], 1)) +start = 44 #np.random.randint(1, arr.shape[0]+1) +single_simulation(start, arr, nodeNames) +single_simulation(start, arr, nodeNames, update=True) + +num_iterations = 100 +plot = True +start = 11 #np.random.randint(1, arr.shape[0]+1) +monte_carlo_sim(num_iterations, plot, start, adjacency_matrix, nodeNames, rand_seed=False, mid_point=False)''' + + + + +'''# ----------- For running the code: feel free to uncomment ------------------- +adjacency_matrix, nodeNames = excel2Matrix("ExcelFiles/failureData.xlsx", "bigMatrix") +probabilities = np.array([0.0195, 0.0195, 0.013625, 0.0055, 0.0175, 0.2075, 0.001, 0.001, 0.001, 0.093185, 0.001, 0.001, + 0.027310938, 0.033968125, 0.033968125, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, + 0.0205, 0.0205, 0.02, 0.01, 0.01, 0.233, 0.288, 0.543374, 0.1285, 0.01, 0.01, 0.01, 0.015, 0.0155, + 0.015, 0.0155, 0.015, 0.0155, 0.015, 0.33, 0.025, 0.025, 0.025, 0.025, 0.025, 0.105]) #0.01375, +probabilities = np.reshape(probabilities, (adjacency_matrix.shape[0], 1)) +targets = [44] +starts = [16, 33, 35, 36] + +# C, D = bayesian_table(adjacency_matrix, 16, True, pvs = True, prob_vals=probabilities, mult_turbine = False) +# A, B = backward_bayesian_inference(adjacency_matrix, nodeNames, [0], [0], [44], probabilities, start_bool = True) +# print(B) +write_bayesian_probabilities(adjacency_matrix, nodeNames, probabilities, mult_turbine = True, midpoint = True, num_iterations=1)''' \ No newline at end of file diff --git a/failure/failureProbabilities.xlsx b/failure/failureProbabilities.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..0b960e89958fd8be2f6d1da59b295bd0a4e48a5c GIT binary patch literal 73055 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

8y+4x}vQRnm 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 NF|dGGCIQUo3; output adjacency matrix and name array + + 2) matrix2Graph: inputs adjacency matrix, name of nodes, boolean of nodeLabels --> output graph and adjacency + matrix + + 3) export2graphml: inputs G, string (file name) --> writes graphML file with inputted file name (no return) + + 4) get_node_dict: inputs adjacency matrix and name of nodes --> outputs array containing dictionaries with each + entry containing the node's name, number, and whether it is an effect or mode. + + 5) get_node_array: inputs adjacency matrix and name of nodes --> outputs array with dictionary entries of the + names of the nodes + + 6) get_graph_edges: inputs adjacency matrix, string indicating type of output desired, and threshold --> outputs + an array of edges + + 7) make_binary: inputs adjacency matrix and float type --> outputs binarized adjacency matrix + + 8) plot_graph: inputs graph and type (str) --> no output (prints graph) + + 9) max_degrees: inputs adjacency matrix, names of nodes, threshold for binarization of adjacency matrix, boolean + for showing names --> outputs tuples of max out degree, max in degree, and max overall degree with the nodes that + have these max degrees + +10) getProbabilities: inputs fileName, sheetName, boolean of nodeLabels --> output probabilities and name array + +11) diagonal_nodes: inputs adjacency matrix --> outputs nodes diagonal matrix + +12) cosine_similarity: inputs vector one and vector two --> outputs cosine similarity of vector one and two + +13) make_undirected_unweighted: inputs adjacency matrix and (optional) threshold --> outputs undirected, unweighted + version of the adjacency matrix + +------------------------------------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------------------------------------ + + + excel2Matrix Documentation + ------------------------------- + This code is meant to take in an excel file (already properly formatted by theh user) and output the corresponding + graph. This assumes that the contents of the excel file is an adjacency matrix. The sheetName input identifies which + sheet in the excel file the adjacency matrix is in. The nodeLabels input lets the user choose if they would like + words (input TRUE) or numeric (input FALSE) labels on the graph. The plot input lets the user toggle on/off plotting + of the graph. Finally, the graph is returned.''' + +def excel2Matrix(fileName, sheetName = None): + # Read in excel file as a dataframe + df = pd.read_excel(fileName, sheet_name=sheetName) + + # Convert the data frame to a numpy array + arr = df.to_numpy() + + # Create an array of the names of the nodes for the graph. + nodeNames = arr[:, 0].flatten() + + # return the adjacency matrix without labels and an array of the names of the nodes + return arr[:, 1:], nodeNames + + + +''' matrix2Graph Documentation + ------------------------------- + This method takes in an adjacency matrix, the names of the nodes, whether or not to display the node names, and + whether or not to display the plot of the graph once constructed. The output is the graph and adjacency matrix.''' + +def matrix2Graph(arr, nodeNames, nodeLabels = False): + # Create the graph (DiGraph because the graph is directed and weighted) and add the nodes selected above + G = nx.DiGraph() + if not nodeLabels: + nodeNames = range(arr.shape[0]) + G.add_nodes_from(nodeNames) + + # The following nested for loops find the edges of the graph and add them to the graph using the 'add_weighted_edges_from()' + # command. 'i' indexes through the rows and 'j' indexes through the columns. If there is no edge (i.e. 0 in the adjacency + # matrix), then move on to the next set of nodes. Otherwise, add the edge to the graph. + for i in range(arr.shape[0]): + for j in range(arr.shape[1]): + if arr[i,j] == 0: + continue + else: + if nodeLabels: G.add_weighted_edges_from([(nodeNames[i], nodeNames[j], arr[i,j])]) + else: G.add_weighted_edges_from([(i, j, arr[i,j])]) + + # Return the graph and adjacency matrix + return G, arr + + + +''' export2graphML Documentation + ------------------------------- + This method takes in a networkx graph and ouputs a graphML file.''' + +def export2graphML(G, name): + + # create a name for the graphML file that you are going to write + filename = name + "_FOWT_graphML.graphml" + + # wrtie the graphML file using networkx methods + nx.write_graphml(G, filename, encoding='utf-8', prettyprint=True, infer_numeric_types=True, named_key_ids=True, edge_id_from_attribute=None) + return + + + +''' get_node_dict Documentation + ------------------------------- + This method takes in the adjacency matrix and the names of all the nodes. It outputs an array of node dictionaries. + Each key/value pair contains the node's number, name, and 'class_id' (which refers to if the node represents a + failure effect or failure mode).''' + +def get_node_dict(arr, nodeNames): + # Initialize array for the nodes + nodes = [] + + # Iterate through each node number + for index in range(arr.shape[0]): + + # If the number is less than 26 (value chosen based on excel sheets), then the node is a failure effect. + if index < 26: + class_id = 'effect' + + # Otherwise, the node must be a failure mode. + else: + class_id = 'mode' + + # Add a dictionary with the node's number, name, selection criteria (in this case they can all be selected), + # and class_id (whether they are an effect or mode). + nodes.append({ + 'data': {'id': str(index), 'label': nodeNames[index]}, + 'selectable': True, + 'classes': class_id + }) + + # Return the array of dictionaries for the nodes + return nodes + + + +''' get_node_array Documentation + ------------------------------- + This method takes in an adjacency matrix and list of node names. It outputs an array with each entry a + key/value pair. Each key is the string 'label', but the values are the string names of the nodes.''' + +def get_node_array(arr, nodeNames): + # Initialize an array for the nodes + nodes = [] + + # Iterate through each node number + for index in range(arr.shape[0]): + + # For every node, locate its name and append a dictionary with 'label' as the only key and + # the name just acquired as the only value. + nodes.append({'label': nodeNames[index]}) + + # Return the array of nodes + return nodes + + +''' get_grpah_edges Documentation + ------------------------------- + This method takes in an adjacency matrix (array type) and a string indicating the type of edge list + desired. This method outputs an array of all the edges. If the 'dictionary' type is indicated, then each + entry in the array is formatted as + {'data': {'id': name_of_source_and_target, 'source' name_of_source, 'target', name_of_target}}. + If the 'array' type is indicated, then each entry in the array is formatted as + [source, target]). + Lastly, if the 'tuple' type is indicated, then each entry in the array is formatted as + (source, target).''' + +def get_graph_edges(arr, type, threshold): + # Initialize an array for the edges + edges = [] + + # Iterate through each row of the adjacency matrix + for i in range(arr.shape[0]): + + # For each row, iterate through each column of the matrix + for j in range(arr.shape[1]): + + # If the entry in the adjacency matrix is greater than 0,5 (can change this threshold), then + # add an edge to the array of edges. Each edge is added by appending an array containing the + # source and target. + if arr[i, j] > threshold: + if type == 'dict': + edges.append({'data': {'id': str(i)+str(j), 'source': str(i), 'target': str(j)}, + 'classes': 'red'}) + elif type == 'array': + edges.append([i,j]) + elif type == 'tuple': + edges.append((i,j)) + + # Otherwise, if the input does not indicate an edge, move on to the nect entry in the + # adjacency matrix + else: + continue + + # Return the array of edges + return edges + + + +''' make_binary Documentation + ------------------------------- + This method takes in an adjacency matrix (array typle) and a threshold value (float type). It outputs a + new adjacency matrix such that all the entries are either 0 or 1.''' + +def make_binary(arr, threshold = 0): + # Initialize the new adjacency matrix by setting it equal to the original adjacency matrix + arr_altered = arr + + # Iterate through each row of the matrix + for i in range(0, arr_altered.shape[0]): + + # In each row, iterate through each column of the matrix + for j in range(arr_altered.shape[0]): + + # If the entry is greater than the indicated threshold, set the entry of the new adjacency + # matrix equal to 1 + if arr[i,j] > threshold: + arr_altered[i,j] = 1 + + # Otherwise, set the entry equal to 0 + else: + arr_altered[i,j] = 0 + + # Return the new adjacency matrix + return arr_altered + + + +''' plot_graph Documentation + ------------------------------- + This method takes in a plot and type of plot desired, then prints a plot of the graph.''' + +def plot_graph(G, type, effects_mark, nodeNames): + + # This type places the failure effects on one side and the failure modes on the other in vertical columns + if type == "bipartite": + pos = nx.bipartite_layout(G, nodeNames[:effects_mark]) # positions for all nodes + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98", edgecolors="#c89679", node_shape="s") + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed", edgecolors="#799dbd", node_shape="s") + + # This for loop places labels outside of the nodes for easier visualization + for i in pos: + x, y = pos[i] + if x <= 0: + plt.text(x-0.075,y,s=i, horizontalalignment='right') + + else: + plt.text(x+0.075,y,s=i, horizontalalignment='left') + + #nx.draw_networkx_labels(G, pos, horizontalalignment='center') + nx.draw_networkx_edges(G, pos) + plt.box(False) + plt.show() + return + + # This type draws all the nodes in a circle + elif type == "circular": + pos = nx.circular_layout(G) # positions for all nodes + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") + nx.draw_networkx_labels(G, pos) + nx.draw_networkx_edges(G, pos) + plt.box(False) + plt.show() + return + + # This type attempts to have all the edges the same length + elif type == "kamada_kawai": + pos = nx.kamada_kawai_layout(G) # positions for all nodes + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") + nx.draw_networkx_labels(G, pos) + nx.draw_networkx_edges(G, pos) + plt.box(False) + plt.show() + return + + # This type attempts to have non-intersecting edges. This only works for planar graphs. + elif type == "planar": + pos = nx.planar_layout(G) # positions for all nodes + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") + nx.draw_networkx_labels(G, pos) + nx.draw_networkx_edges(G, pos) + plt.box(False) + plt.show() + return + + # This type plots the graph in a random configuration + elif type == "random": + pos = nx.random_layout(G) # positions for all nodes + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") + nx.draw_networkx_labels(G, pos) + nx.draw_networkx_edges(G, pos) + plt.box(False) + plt.show() + return + + # This type uses the eigenvectrs of the graph Laplacian in order to show possible clusters of nodes + elif type == "spectral": + pos = nx.spectral_layout(G) # positions for all nodes + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") + nx.draw_networkx_labels(G, pos) + nx.draw_networkx_edges(G, pos) + plt.box(False) + plt.show() + return + + # This type treats nodes as repelling objects and edges as springs (causing them to moving in simulation) + elif type == "spring": + pos = nx.spring_layout(G) # positions for all nodes + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") + nx.draw_networkx_labels(G, pos) + nx.draw_networkx_edges(G, pos) + plt.box(False) + plt.show() + return + + # This type places nodes in concentric circles + elif type == "shell": + pos = nx.shell_layout(G) # positions for all nodes + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") + nx.draw_networkx_labels(G, pos) + nx.draw_networkx_edges(G, pos) + plt.box(False) + plt.show() + return + + # This type places nodes in spiral layout + elif type == "spiral": + pos = nx.spiral_layout(G) # positions for all nodes + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") + nx.draw_networkx_labels(G, pos) + nx.draw_networkx_edges(G, pos) + plt.box(False) + plt.show() + return + + # This type places nodes in spiral layout + elif type == "multipartite": + + pos = nx.multipartite_layout(G) # positions for all nodes + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") + nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") + nx.draw_networkx_labels(G, pos) + nx.draw_networkx_edges(G, pos) + plt.box(False) + plt.show() + return + + # Plot the grpah with networkx and matplotlib using regular algorithm + nx.draw(G, with_labels=True, font_weight='bold') + plt.show() + return + + + +''' max_degrees Documentation + -------------------------------- + This method takes in an adjacency matrix, the names of the nodes, a threshold for binarization + of the adjacency matrix, and a boolean for labeling the nodes with names (True) or numbers (False). + The output consists of three tuples: + 1. (the nodes with the highest out degree, value of the highest out degree) + 1. (the nodes with the highest in degree, value of the highest in degree) + 1. (the nodes with the highest overall degree, value of the highest overall degree)''' + +def max_degrees(arr, nodeNames, threshold = 0, name = False): + + # Create copy of the adjacency matrix so that we can alter it without losing information about + # our original adjacency matrix. + arr_altered = arr + + # Binarization of adjacency matrix. The threshold determines the cutoff for what will be labeled + # as a 1 versus a 0. Anything above the threshold will be a 1, and anything below the threshold + # will be set to 0. + for i in range(0, arr_altered.shape[0]): + for j in range(arr_altered.shape[0]): + if arr[i,j] > threshold: + arr_altered[i,j] = 1 + else: + arr_altered[i,j] = 0 + + # Calculating out degrees and the maximum of the out degrees + out_degrees = np.sum(arr_altered, axis=1) + max_out = np.where(out_degrees == max(out_degrees)) + + # Calculating in degrees and the maximum of the in degrees + in_degrees = np.sum(arr_altered, axis=0) + max_in = np.where(in_degrees == max(in_degrees)) + + # Calculating overall degrees and the maximum of the overall degrees + degrees = out_degrees + in_degrees + max_deg = np.where(degrees == max(degrees)) + + # If the user chooses to list the nodes by their proper name (rather than with numbers), then + # we index into the array of node names, nodeNames, to find the names of these nodes. We then + # return the three tuples about maximum out degree, in degree, and overall degree. + if name: + out_name = nodeNames[max_out[0].tolist()] + in_name = nodeNames[max_in[0].tolist()] + deg_name = nodeNames[max_deg[0].tolist()] + return (out_name, max(out_degrees)), (in_name, max(in_degrees)), (deg_name, max(degrees)) + + # Otherwise, if the user chooses not to label nodes with their proper names, then we return the + # three tuples about maximum out degree, in degree, and overall degree. Rather than listing the + # names of the nodes, their corresponding numbers are listed. + return (max_out, max(out_degrees)), (max_in, max(in_degrees)), (max_deg, max(degrees)) + + + +''' getProbabilities Documentation + ------------------------------- + This method inputs an Excel file and reads the probabilities from the file. We return an array of these + probabilities and an array of the node names (indexed the same as the probability array).''' + +def getProbabilities(fileName, sheetName = None): + df = pd.read_excel(fileName, sheet_name=sheetName) + + # Convert the data frame to a numpy array + arr = df.to_numpy() + + # Create an array of the names of the nodes for the graph. + nodeNames = arr[:, 0].flatten() + + # return the adjacency matrix without labels and an array of the names of the nodes + return arr[:, 1], nodeNames + + +'''diagonal_nodes Documentation + -------------------------------- + This method takes in an adjacency matrix and outputs a diagonal matrix. For an adjacency matrix of length k, the + outputted diagonal matrix places values 1-k on the diagonal.''' + +def diagonal_nodes(arr): + # Return the diagonal matrix with i+1 in the i,i spot and zero elsewhere + return np.diag([i+1 for i in range(arr.shape[0])]) + +'''cosine_similarity Documentation + ----------------------------------- + This method takes in two vectors and outputs the cosine similarity between them. In linear algebra, cosine + similarity is defined as the measure of how much two vectors are pointing in the same direction. It can also + be thought of cosine of the angle between the two vectors.''' + +def cosine_similarity(v1, v2): + # Find the dot product between the two vectors inputted + dot_prod = np.transpose(v1) @ v2 + + # Calculate and return the cosine similarity of the two vectors + return (dot_prod) / ((np.sqrt(np.sum(np.square(v1))) * np.sqrt(np.sum(np.square(v2))))) + + + +''' make_undirected_unweighted Documentation + --------------------------------------------- + This method takes in an adjacency matrix and threshold. It outputs an adjusted adjacency matrix that + corresponds to the undirected and unweighted graph of the one inputted.''' + +def make_undirected_unweighted(arr, threshold = 0): + # Make sure that the array is a numpy array + arr = np.array(arr) + + # Add the array to the transpose of itself. This makes the graph undirected. + arr = arr + arr.T + + # Use the method from graphBuilder.py to binarize the matrix. This makes the graph unweighted. + make_binary(arr, threshold) + + # Since the array now only consists of zeros and ones, make sure that it is of integer type + arr = arr.astype(int) + + # Return the adjusted matrix + return arr \ No newline at end of file diff --git a/failure/searchMethods.py b/failure/searchMethods.py new file mode 100644 index 00000000..b4fbb357 --- /dev/null +++ b/failure/searchMethods.py @@ -0,0 +1,429 @@ +import numpy as np +import networkx as nx +import matplotlib.pyplot as plt +from failure.graphBuilder import * + +''' searchMethods-------------------------------------------------------------------------------------------------- + + ****************************** Last Updated: 10 April 2024 ****************************** + + Methods: + 1) breadth_first_child: inputs adjacency matrix, names of nodes, start node --> outputs tree, adjacency matrix, + dictionary of nodes and generations, list of 'effect' nodes, list of 'mode' nodes + + 2) breadth_first_parent: inputs adjacency matrix, names of nodes, start node --> outputs tree, adjacency matrix, + dictionary of nodes and generations, list of 'effect' nodes, list of 'mode' nodes + + 3) draw_bfs_multipartite: nputs adjacency matrix, list of node names, start node --> plots breadth first tree. + Nothing is returned. + + 4) breadth_first_multi: adjacency matrix, list of node names, starting node, type of breadth first search --> + outputs tree, adjacency matrix, dictionary of nodes, arrays for effects and modes, and array of nodes + +-------------------------------------------------------------------------------------------------------------------- +-------------------------------------------------------------------------------------------------------------------- + + + breadth_first_child Documentation + ----------------------------------- + This method takes in an adjacency matrix, list of node names, and an integer indicating te starting node. We + traverse through the graph. For each generation, starting from the source node, we find and add all the children + of the nodes in this generation, unless the node has aldready been placed in the tree. As such, we create a tree. + This method returns this tree, the tree's adjacency matrix, a dictionary of nodes, and arrays for effects and modes. + + --> Note: a tree is a graph with no cycles or loops. It does not have to be directed, but it is acyclic.''' + +def breadth_first_child(arr, nodeNames, source): + # Initialize a new digraph for the tree we are going to make and add all the nodes (we will determine edges later) + G = nx.DiGraph() + + # Initialize effects and modes arrays for plotting purposes + effects = [] + modes = [] + + # Add the source node to the graph + G.add_node(nodeNames[source - 1]) + + # Determine if the source node is an effect or a mode, and add it to the correct array + if source < 27: effects.append(nodeNames[source - 1]) + elif source <= 47: modes.append(nodeNames[source - 1]) + + '''if source < 49: modes.append(nodeNames[source - 1]) + else: effects.append(nodeNames[source - 1])''' + + # Note that we are changing the numerical names of the nodes so that the values of nodes are from 1 to 47 rather + # than from 0 to 46. This is so we can find all the children of the nodes. Using the 0-46 range poses a problem + # since we do not know if a zero indicates no relation or that the 0 node is a child of the node we are looking at. + # We binarize the adjacency matrix so that relationships either exist or not (rather than having an intensity) + adj = make_binary(arr, 0.5).astype(int) + + # Create an array of booleans such that the ith entry indicates if the node has already been visited. Nodes + # that have been visited are set to TRUE. + nodeList = np.reshape(np.repeat(False, arr.shape[0]), (arr.shape[0], 1)) + + # Create a diagonal matrix such that the value of the i,i entry is 1+1, referencing the node with name i+1 + nodes = diagonal_nodes(adj) + # print("nodes", nodes.shape) + + # Visit the source node + nodeList[source-1] = True + + # Add the source node to the queue. We use a queue to iterate through the nodes. This allows us to search through + # the graph generation by generation rather than following one specific path at a time. + queue = [[source, 0]] + + # Initialize a dictionary that will tell us what generation each node is in. Label the generation of the source node + # as zero. + gens = {nodeNames[source-1]: {"layer": 0}} + + # Continue while there are still nodes in the queue (reference algorithms for a breadth first search for more info) + while len(queue) > 0: + # Set the node we are looking at equal to the node next in line in the queue + current = queue[0] + + # Determine the children of the current node. + # Proof/Reason Why This Works --> Using the unweighted adjacency matrix, we can find the children + # by multiplying the row of the adjacency matrix corresponding to the current node by the diagonal matrix of + # node names. If the jth node is a child of the current node, then there is a 1 in the j-1 column of the + # current row. When multiplied by the diagonal matrix, this 1 will be multiplied by the j-1 column of the. + # since the only non zero entry in the j-1 column of the diagonal matrix is j (located on the diagonal), then + # the entry in the j-1 column of the returned vector will be j. However, if the jth node is not a child of + # the current node, then there is a zero in the j-1 column of the current row of the adjacency matrix. When + # multiplied by the diagonal matrix, this zero is multiplied by every entry in the j-1 column of the diagonal + # matrix. So, the j-1 column of the returned vector is zero. + + children_bool = adj[current[0]-1] @ nodes # vector of zeros and child names (numerical names) + children = children_bool[np.nonzero(children_bool)] #list of just the child names (numerical names) + + for child in children: # For every child of the current node that was found above... + # Check if the child has been visited or if it is in the same generation as child. If either not visited or + # in the same generation, continue with the following: + if nodeList[child - 1] == False or gens[nodeNames[child - 1]]['layer'] > current[1]: + + # Add the child to the graph + G.add_node(nodeNames[child-1]) + + # Determine if the child is an effect or mode and add it to the correct array + if child < 27: effects.append(nodeNames[child - 1]) + elif child <= 47: modes.append(nodeNames[child - 1]) + '''if child < 49: modes.append(nodeNames[child - 1]) + else: effects.append(nodeNames[child - 1])''' + + # Add an edge between the current node and child in the tree we are building + G.add_edge(nodeNames[current[0]-1], nodeNames[child-1]) + + queue.append([child, current[1]+1]) # Append the child to the queue + nodeList[child - 1] = True # Change the status of the child to say we have visited it + gens.update({nodeNames[child-1]: {"layer": current[1]+1}}) # Add child to dictionary of nodes + + # Remove the current node from the queue + queue = queue[1:] + + # Return the tree we made, its adjacency matrix, the dictionary of nodes, the effects array, and the modes arrat + return G, nx.to_numpy_array(G), gens, effects, modes + + + +''' breadth_first_parent Documentation + --------------------------------------- + This method takes in an adjacency matrix, list of node names, and an integer indicating te starting node. We + traverse through the graph. For each generation, starting from the source node, we find and add all the parents + of the nodes in this generation, unless the node has aldready been placed in the tree. As such, we create a tree. + This method returns this tree, the tree's adjacency matrix, a dictionary of nodes, and arrays for effects and modes.''' + +def breadth_first_parent(arr, nodeNames, source): + # Initialize a new digraph for the tree we are going to make and add all the nodes (we will determine edges later) + # Initialize a graph in Networkx + G = nx.DiGraph() + + # Initialize effects and modes arrays for plotting purposes + effects = [] + modes = [] + names_of_nodes = [] + + # Add the source node to the graph + G.add_node(nodeNames[source - 1]) + names_of_nodes.append(nodeNames[source - 1]) + + # Determine if the source node is an effect or a mode, and add it to the correct array + '''if source < 27: effects.append(nodeNames[source - 1]) + else: modes.append(nodeNames[source - 1])''' + if source < 49: modes.append(nodeNames[source - 1]) + else: effects.append(nodeNames[source - 1]) + + # Binarize the adjacency matrix + arr2 = arr.copy() + adj = make_binary(arr2).astype(int) + + # Create an array of booleans such that the ith entry indicates if the node has already been visited. Nodes + # that have been visited are set to TRUE. + nodeList = np.reshape(np.repeat(False, arr.shape[0]), (arr.shape[0], 1)) + + # Create a diagonal matrix such that the value of the i,i entry is 1+1, referencing the node with name i+1 + nodes = diagonal_nodes(adj) + + # Visit the source node + nodeList[source-1] = True + + # Add the source node to the queue. We use a queue to iterate through the nodes. This allows us to search through + # the graph generation by generation rather than following one specific path at a time. + queue = [[source, 100]] + + # Initialize a dictionary that will tell us what generation each node is in. Label the generation of the source node + # as zero. + gens = {nodeNames[source-1]: {"layer": 100}} + + # Continue while there are still nodes in the queue (reference algorithms for a breadth first search for more info) + while len(queue) > 0: + # Set the node we are looking at equal to the node next in line in the queue + current = queue[0] + + # Determine the parents of the current node. + parents_bool = nodes @ adj[:, current[0]-1] # vector of zeros and parent names (numerical names) + parents = parents_bool[np.nonzero(parents_bool)] #list of just the parent names (numerical names) + + for parent in parents: # For every child of the current node that was found above... + # Check if the parent has been visited or if it is in the same generation as parent. If either not visited or + # in the same generation, continue with the following: + if nodeList[parent - 1] == False or gens[nodeNames[parent - 1]]['layer'] < current[1]: + + # Add the parent to the graph + G.add_node(nodeNames[parent-1]) + + # Determine if the parent is an effect or mode and add it to the correct array + if parent < 27: effects.append(nodeNames[parent- 1]) + else: modes.append(nodeNames[parent - 1]) + '''if parent < 49: modes.append(nodeNames[parent- 1]) + else: effects.append(nodeNames[parent - 1])''' + + # Add an edge between the current node and parent in the tree we are building + G.add_edge(nodeNames[parent-1], nodeNames[current[0]-1]) + + queue.append([parent, current[1]-1]) # Append the parent to the queue + nodeList[parent - 1] = True # Change the status of the parent to say we have visited it + gens.update({nodeNames[parent-1]: {"layer": current[1]-1}}) # Add parent to dictionary of nodes + + # Remove the current node from the queue + queue = queue[1:] + + # Return the tree we made, its adjacency matrix, the dictionary of nodes, the effects array, and the modes arrat + return G, nx.to_numpy_array(G), gens, effects, modes + + + +''' draw_bfs_multipartite Documentation + --------------------------------------- + This method inputs an adjacency matrix, list of node names, and a start node. We use the breadth first searh to + find generations of nodes starting from the start node. This method plots the resulting graph from the breadth + first search. Nothing is returned.''' + +def draw_bfs_multipartite(arr, nodeNames, start, type = "child", multi_turbine = False): + # Obtain the graph, adjacency matrix, dictionary, effects, and modes from breadth_first_tree + if type == "child": + G, arr, gens, effects, modes = breadth_first_child(arr, nodeNames, start) + elif type == "parent": + G, arr, gens, effects, modes = breadth_first_parent(arr, nodeNames, start) + elif type == "multi-child": + G, arr, gens, effects, modes, non = breadth_first_multi(arr, nodeNames, start, "child") + else: + G, arr, gens, effects, modes, non = breadth_first_multi(arr, nodeNames, start, "parent") + + # Give each node the attribute of their generation + nx.set_node_attributes(G, gens) + # print(effects) + + if multi_turbine: + effect_colors = ["#ffd6ed", "#ffb3ba", "#ffdfba", "#ffffba", "#baffc9", "#bae1ff", "#b1adff", "#e4adff", "#e5e5e5", "#e8d9c5"] + mode_colors = ["#e5c0d5", "#e5a1a7", "#e5c8a7", "#e5e5a7", "#a7e5b4", "#a7cae5", "#9f9be5", "#cd9be5", "#cecece", "#d0c3b1"] + pos = nx.multipartite_layout(G, subset_key='layer') + for node in G.nodes: + # print(int(str(node)[0])) + if str(node) in effects: + nx.draw_networkx_nodes(G, pos, nodelist=[node], node_color = effect_colors[int(str(node)[0])], node_size=750, node_shape="s") + else: + nx.draw_networkx_nodes(G, pos, nodelist=[node], node_color = effect_colors[int(str(node)[0])], node_size=750) + nx.draw_networkx_labels(G, pos, font_size=5, verticalalignment='center_baseline') + nx.draw_networkx_edges(G, pos, arrowsize=20) + plt.box(False) + plt.show() + else: + # Plot the graph + pos = nx.multipartite_layout(G, subset_key='layer') + nx.draw_networkx_nodes(G, pos, nodelist=effects, node_color="#98c5ed", node_size=2700, edgecolors="#799dbd", node_shape="s") + nx.draw_networkx_nodes(G, pos, nodelist=modes, node_color="#fabc98", node_size=2700, edgecolors="#c89679", node_shape="s") + nx.draw_networkx_labels(G, pos, font_size=10, verticalalignment='center_baseline') + nx.draw_networkx_edges(G, pos, arrowsize=60) + plt.box(False) + plt.show() + + # Nothing is returned + return + + + +'''breadth_first_multi Documentation + ----------------------------------- + This method takes in an adjacency matrix, list of node names, an integer indicating te starting node, and a string + that indicates which type of breadth first search we are conducting (child or parent). We traverse through the graph. + For each generation, starting from the source nodes (handles multiple start nodes), we find and add all the children/parents + of the nodes in this generation, unless the node has aldready been placed in the tree. As such, we create a tree. + This method returns this tree, the tree's adjacency matrix, a dictionary of nodes, arrays for effects and modes, and array + of nodes (in the order they are added).''' + +def breadth_first_multi(arr, nodeNames, sources, type_poc): + # Function that determines how value of the layer changes, depending on the type of calculation + def layer_fcn(layer, type_poc): + if type_poc == "child": + return layer + 1 + elif type_poc == "parent": + return layer - 1 + # Determining if the current generation is after the former node's generation, depending on the type of calculation + def same_layer(layer1, layer2, type_poc): + if type_poc == "child": + if layer1 > layer2: + return True + else: + return False + elif type_poc == "parent": + if layer1 < layer2: + return True + else: + return False + + # Initialize a new digraph for the tree we are going to make and add all the nodes (we will determine edges later) + G = nx.DiGraph() + + # Initialize arrays for tracking nodes + effects = [] + modes = [] + names_of_nodes = [] + adj = make_binary(arr).astype(int) + nodeList = np.reshape(np.repeat(False, arr.shape[0]), (arr.shape[0], 1)) + nodes = diagonal_nodes(adj) + queue = [] + gens = {} + + # Initialize value for the first layer created + if type_poc == "child": + layer_val = 0 + else: + layer_val = 100 + + # Add the source node to the graph + for start in sources: + G.add_node(nodeNames[start - 1]) + + # Determine if the source node is an effect or a mode, and add it to the correct array + if start < 27: effects.append(nodeNames[start - 1]) + else: modes.append(nodeNames[start - 1]) + + '''if start < 49: modes.append(nodeNames[start - 1]) + else: effects.append(nodeNames[start - 1])''' + + # Visit the source node + nodeList[start-1] = True + names_of_nodes.append(nodeNames[start - 1]) + + # Add the source node to the queue. We use a queue to iterate through the nodes. This allows us to search through + # the graph generation by generation rather than following one specific path at a time. + queue.append([start, 0]) + + # Initialize a dictionary that will tell us what generation each node is in. Label the generation of the source node + # as zero. + gens.update({nodeNames[start-1]: {"layer": layer_val}}) + # print("non", names_of_nodes) + + # Continue while there are still nodes in the queue (reference algorithms for a breadth first search for more info) + while len(queue) > 0: + # Set the node we are looking at equal to the node next in line in the queue + current = queue[0] + + if type_poc == "child": + # Determine the children of the current node. + children_bool = adj[current[0]-1] @ nodes # vector of zeros and child names (numerical names) + children = children_bool[np.nonzero(children_bool)] #list of just the child names (numerical names) + else: + children_bool = nodes @ adj[:, current[0]-1] # vector of zeros and parent names (numerical names) + children = children_bool[np.nonzero(children_bool)] #list of just the parent names (numerical names) + + + for child in children: # For every child of the current node that was found above... + # Check if the child has been visited or if it is in the same generation as child. If either not visited or + # in the same generation, continue with the following: + # print("child", child) + + if nodeList[child - 1] == False or same_layer(gens[nodeNames[child - 1]]['layer'], current[1], type_poc): + + # Add the child to the graph, and to the array of node names + G.add_node(nodeNames[child-1]) + if nodeNames[child - 1] in names_of_nodes: + x = 14 + else: + names_of_nodes.append(nodeNames[child - 1]) + + # Determine if the child is an effect or mode and add it to the correct array + '''if child < 27: effects.append(nodeNames[child - 1]) + else: modes.append(nodeNames[child - 1])''' + if child < 49: modes.append(nodeNames[child - 1]) + else: effects.append(nodeNames[child - 1]) + + # Add an edge between the current node and child in the tree we are building + if type_poc == "child": + G.add_edge(nodeNames[current[0]-1], nodeNames[child-1]) + else: + G.add_edge(nodeNames[child-1], nodeNames[current[0]-1]) + + queue.append([child, layer_fcn(current[1], type_poc)]) # Append the child to the queue + nodeList[child - 1] = True # Change the status of the child to say we have visited it + gens.update({nodeNames[child-1]: {"layer": layer_fcn(current[1], type_poc)}}) # Add child to dictionary of nodes + + # Remove the current node from the queue + queue = queue[1:] + + # Return the tree we made, its adjacency matrix, the dictionary of nodes, the effects array, and the modes arrat + return G, nx.to_numpy_array(G), gens, effects, modes, names_of_nodes + + +'''# **** Below code is still being worked on ***** + +arr, nodeNames = excel2Matrix("failureData.xlsx", "bigMatrix") +source = 11 +G = nx.DiGraph() + +effects = [] +modes = [] + +G.add_node(nodeNames[source - 1]) + +if source < 49: modes.append(nodeNames[source - 1]) +else: effects.append(nodeNames[source - 1]) + +adj = make_binary(arr).astype(int) + +nodeList = np.reshape(np.repeat(False, arr.shape[0]), (arr.shape[0], 1)) +nodes = diagonal_nodes(adj) + +nodeList[source-1] = True +queue = [[source, 0]] +gens = {nodeNames[source-1]: {"layer": 0}} + +while len(queue) > 0: + current = queue[0] + + children_bool = adj[current[0]-1] @ nodes # vector of zeros and child names (numerical names) + children = children_bool[np.nonzero(children_bool)] #list of just the child names (numerical names) + + for child in children: + if nodeList[child - 1] == False or gens[nodeNames[child - 1]]['layer'] > current[1]: + G.add_node(nodeNames[child-1]) + + if child < 27: effects.append(nodeNames[child - 1]) + else: modes.append(nodeNames[child - 1]) + + G.add_edge(nodeNames[current[0]-1], nodeNames[child-1]) + + queue.append([child, current[1]+1]) # Append the child to the queue + nodeList[child - 1] = True # Change the status of the child to say we have visited it + gens.update({nodeNames[child-1]: {"layer": current[1]+1}}) # Add child to dictionary of nodes + + queue = queue[1:]''' \ No newline at end of file diff --git a/failure/twoTurbineCaseStudy.py b/failure/twoTurbineCaseStudy.py new file mode 100644 index 00000000..82189fed --- /dev/null +++ b/failure/twoTurbineCaseStudy.py @@ -0,0 +1,76 @@ +import numpy as np +from failure.graphBuilder import * +from failure.searchMethods import * + +''' twoTurbineCaseStudy.py ----------------------------------------------------------------------------------------------- + + ****************************** Last Updated: 18 April 2024 ****************************** + + Methods: + 1) twoTurbine_bayesian_table: input adjusted adjacency matrix, adjacency matrix, current node, list of node names, + adjusted list of node names --> outputs list of parents, probability table + +----------------------------------------------------------------------------------------------------------------------- +----------------------------------------------------------------------------------------------------------------------- + + + twoTurbine_bayesian_table Documentation + ------------------------------------------ + This method inputs an adjusted adjacency matrix, regular adjacency matrix, current node (integer), array of node names (strings), + and adjusted node names (same indexing as adjusted adjacency matrix). We then calculate the probability table for the current node + given its parents in the adjusted adjacency matrix. We use the weights from the regular adjacency matrix to determine the transitional + probabilities between nodes. We output an array of the current node's parents and its probability table.''' + + +def twoTurbine_bayesian_table(a, arr, current, nodeNames, non): + # arr, nodeNames = excel2Matrix("ExcelFiles/failureData.xlsx", "twoTurbines_simplified") # For debugging, feel free to uncomment + adj = a.copy() + nodes = diagonal_nodes(adj) # Diagonal matrix of node's numerical names +1 + adj = make_binary(adj, 0) # Binarize adjacency matrix + parents_bool = nodes @ adj[:, current-1] # vector of zeros and parent names (numerical names) + parents = list(parents_bool[np.nonzero(parents_bool)]) # list of just the parent names (numerical names) + prob_table = np.zeros((2 ** len(parents), len(parents) + 2)) # Initialize the array for the node's conditional probability distribution + + current_index = np.where(nodeNames == non[int(current - 1)])[0][0] + + for iteration in range(2 ** len(parents)): # For each combination of parents having either failed or not + true_false_array = [] # Iniitalize to record which parents have failed + + for p in range(len(parents)): + parent_index = np.where(nodeNames == non[int(parents[p] - 1)])[0][0] + prob_table[iteration][p] = int(iteration / (2 ** p)) % 2 # Append whether or not the parent node has failed + prob_table[iteration][-2] += (1- int(iteration / (2 ** p)) % 2) * arr[parent_index][current_index] # Determine parent probability (given if the parent has failed or not) + true_false_array.append((int(iteration / (2 ** p)) % 2)) # Append whether or not the parent node has failed + + if prob_table[iteration][-2] > 1: # If the value is greater than 1, set the probability to 1 + prob_table[iteration][-2] = 1 + prob_table[iteration][-1] = 1 - prob_table[iteration][-2] # Add the probability of the node not failing to the array + # print(prob_table) + return parents, prob_table + + + +''' +# The following code is for writing excel file with all pair-wise probabilities. To run, copy and paste the code below into main.py and hit 'Run.' + +adjacency_matrix, nodeNames = excel2Matrix("ExcelFiles/failureData.xlsx", "twoTurbines_simplified") +current = 1 +midpoint = True +num_iterations = 1 +probabilities, nodeNames = getProbabilities("ExcelFiles/failureProbabilities.xlsx", sheetName = "Conditional Probabilities (2)") +all_probs = np.zeros(adjacency_matrix.shape) +with pd.ExcelWriter("bayesian_simulations3.xlsx") as writer: + for i in range(1, len(nodeNames) + 1): + print("----------------------------------", i, "----------------------------------") + array_of_probs = np.zeros((len(nodeNames), 2)) + for j in range(1,len(nodeNames)+1): + adjacency_matrix2 = adjacency_matrix.copy() + probabilities = np.array(probabilities).copy() + A, B = backward_bayesian_inference(adjacency_matrix2, nodeNames, [i], [i], [j], probabilities, start_bool = True, multi = False, poc="child", twoTurbine = True) + array_of_probs[j-1] = A + df = pd.DataFrame(np.array(array_of_probs)) + df.to_excel(writer, sheet_name="Probabilities given "+str(i)) + all_probs[:,i-1] = array_of_probs[:,0] + df2 = pd.DataFrame(np.array(all_probs)) + df2.to_excel(writer, sheet_name="allProbs") +''' \ No newline at end of file diff --git a/failure/zzz_failureGraphs.py b/failure/zzz_failureGraphs.py new file mode 100644 index 00000000..d31fdf92 --- /dev/null +++ b/failure/zzz_failureGraphs.py @@ -0,0 +1,526 @@ +import numpy as np +import networkx as nx +import matplotlib.pyplot as plt +import pandas as pd +from failure.failureProbabilities import * +from failure.twoTurbineCaseStudy import * +from famodel.project import Project + + +def addMoreEdges(nearby_platforms, G, Array, node, node_id, failures_c, failures_p, ids): + node_index = np.where(nodeNames == node)[0][0] + platforms = list(Array.platformList.keys()) + if len(nearby_platforms) > 1: + platforms = nearby_platforms + cables = list(Array.cableList.keys()) + all_ids = [] + combos = [] + for p in platforms: + for c in cables: + if c in p: + combos.append(str(p) + ',' + str(c)) + all_ids.append(c) + for m in Array.platformList[p]: + combos.append(str(m) + ',' + str(c)) + all_ids.append(str(m)) + print("ATTACHMENTS: ", Array.platformList[p].attachments) + for a in Array.platformList[p].anchorList: + all_ids.append(a) + all_ids = platforms+all_ids+list(combos) + for child in failures_c[node]: + child_index = np.where(nodeNames == child)[0][0] + original_ids = ids + if arr[node_index, child_index] > 1.5: + ids = all_ids + for id in ids: + if child + "\n" + str(id) in G.nodes: + G.add_edge(node + "\n" + str(node_id), child + "\n" + str(id), weight=0.001) + ids = original_ids + for parent in failures_p[node]: + parent_index = np.where(nodeNames == parent)[0][0] + original_ids = ids + if arr[parent_index, node_index] > 1.5: + ids = all_ids + for id in ids: + if parent + "\n" + str(id) in G.nodes: + G.add_edge(parent + "\n" + str(id), node + "\n" + str(node_id), weight=0.001) + ids = original_ids + return G + +def edgeReweight(G, edge): + new_weight = input("Enter weight for (" + str(edge[0].replace("\n", " ")) + ", " + str(edge[1].replace("\n", " ")) + ") (press \'Enter\' for default value)") + if new_weight=='': + new_weight=0.01 + G[edge[0]][edge[1]]['weight']=float(new_weight) + return G + +def get_y_Value(point1, point2, x): + return ((point2[1]-point1[1])/(point2[0]-point1[0]))*(x-point1[0])+point1[1] + +def get_min_max_vals(pnt1, pnt2, angle_radians): + vector = pnt2 - pnt1 + length = math.sqrt(vector[0]**2 + vector[1]**2) + if vector[0] == 0.0 and vector[1] > 0: angle = math.pi/2 + elif vector[0] == 0.0 and vector[1] < 0: angle = 3*math.pi/2 + elif vector[0] == 0.0 and vector[1] == 0: angle = 0.0 + else: angle = math.atan(vector[1]/vector[0]) + if angle == 0 and vector[0] < 0: + angle = math.pi + new_vector1 = np.array([length * math.cos(angle - angle_radians), length * math.sin(angle - angle_radians)]) + np.array(pnt1) + new_vector2 = np.array([length * math.cos(angle + angle_radians), length * math.sin(angle + angle_radians)]) + np.array(pnt1) + if angle == math.pi and angle_radians==0: + new_vector1 = np.array([length * math.cos(angle - angle_radians), 0]) + np.array(pnt1) + new_vector2 = np.array([length * math.cos(angle + angle_radians), 0]) + np.array(pnt1) + max_x = max([new_vector1[0], new_vector2[0], pnt1[0], pnt2[0]]) + min_x = min([new_vector1[0], new_vector2[0], pnt1[0], pnt2[0]]) + max_y = max([new_vector1[1], new_vector2[1], pnt1[1], pnt2[1]]) + min_y = min([new_vector1[1], new_vector2[1], pnt1[1], pnt2[1]]) + if max_x == min_x: + if max_x < 0: max_x = 0 + elif min_x > 0: min_x = 0 + return max_x, min_x, max_y, min_y + +# Create project class instance from yaml file +Array = Project(file='famodel/OntologySample600m.yaml') + +# Create adjacency matrix from failure matrix +df = pd.read_excel("/Users/eslack/Documents/Code/ExcelFiles/failureData.xlsx", sheet_name="bMMatch") +arr = df.to_numpy()[:,1:] +nodeNames = df.to_numpy()[:, 0].flatten() + +# Get initial failure probabilities for each failure mode and effect +probabilities, array_of_probs = getProbabilities("failureProbabilities.xlsx", "Sheet3") + + + +# Determine if using the twoTurbine case study model +tT_input = input("Would you like to calculate for the twoTurbine case study? ") +if 'y' in tT_input: twoTurbine = True +else: twoTurbine = False + +# Determine angle of clashing we are interested in +angle_degree = float(input("What angle do you want to use? (in degrees) ")) +angle_radians = angle_degree/360 * math.pi * 2 + +# Determine what the nearby platforms are (True ==> all other platforms, False ==> physically close turbines) +nba_input = input("Would you like one turbine to directly affect all turbines? ") +nba = False +if 'y' in nba_input.lower(): + nba = True + + + +# Initialize and create the dictionaries of the children, parents, and probabilities for each failure +failures_c = {} +failures_p = {} +probability_dict = {} + +for i in range(arr.shape[0]): + node_children = [] + node_parents = [] + for j in range(arr.shape[1]): + if arr[i,j] > 0: + node_children.append(nodeNames[j]) + if arr[j,i] > 0: + node_parents.append(nodeNames[j]) + failures_c.update({nodeNames[i]: node_children}) + failures_p.update({nodeNames[i]: node_parents}) + probability_dict.update({nodeNames[i]: probabilities[i]}) + +# Create dictionary for each subsystem of failures +turbine = [0,1,2,3,4,5,26,27,28,29] +platform = [6,7,8,9,10,11,30,31,32] +clashing = [12,13,14] +mooring_materials = [33,34,35] +mooring = [15,16,17, 18] +connector = [36] +clump_weight = [37] +anchor = [19,20,38,39] +cable = [21,22,23,41,42, 45,46] +cable_mat = [43,44] +grid = [24,25] +buoyancy = [40] + +systems = {'turbine':nodeNames[turbine], 'platform':nodeNames[platform], 'clashing':nodeNames[clashing], 'moor_mat':nodeNames[mooring_materials], + 'mooring':nodeNames[mooring], 'connector':nodeNames[connector], 'weight':nodeNames[clump_weight], + 'anchor':nodeNames[anchor], 'cable':nodeNames[cable], 'grid':nodeNames[grid], 'cable_mat':nodeNames[cable_mat], + 'buoyancy':nodeNames[buoyancy]} + + +# Initialize graph, boolean for plotting, and list of probabilities +G = nx.DiGraph() +plot = False +probabilities = [] + +for platform in Array.platformList: + # Initialize list of mooring-mooring clashing nodea and list of mooring-cable and anchor-cable clashing nodes + mooring_clashes = [] + cable_clashes = [] + + # Determine the nearby platforms + nearby_platforms = [] + for platform2 in Array.platformList: + platform_xy = Array.platformList[platform].r + platform2_xy = Array.platformList[platform2].r + dist_btwn_turbines = math.sqrt((platform_xy[0] - platform2_xy[0])**2 + (platform_xy[1] - platform2_xy[1])**2) + if dist_btwn_turbines <= (1600 * math.sqrt(2)): + nearby_platforms.append(platform2) + if 'y' in nba_input: + nearby_platforms = [] + + # Create platform nodes + for platform_failure in systems['platform']: + if not(platform_failure + "\n" + str(platform) in list(G.nodes)): + probabilities.append(probability_dict[platform_failure]) + G.add_node(platform_failure + "\n" + str(platform)) + G = addMoreEdges(nearby_platforms, G, Array, platform_failure, platform, failures_c, failures_p, [platform]) + + # Create turbine nodes + for turbine_failure in systems['turbine']: + if not(turbine_failure + "\n" + str(platform) in list(G.nodes)): probabilities.append(probability_dict[turbine_failure]) + G.add_node(turbine_failure + "\n" + str(platform)) + G = addMoreEdges(nearby_platforms, G, Array, turbine_failure, platform, failures_c, failures_p, [platform]) + + print(Array.platformList[platform].getTopLevelEdge) + for anchor in Array.platformList[platform].anchorList: + # Create anchor nodes + for anchor_failure in systems['anchor']: + if len(Array.platformList[platform].anchorList[anchor].mooringList) > 1 and "ingle" in anchor_failure: continue + elif len(Array.platformList[platform].anchorList[anchor].mooringList) <= 1 and "hared" in anchor_failure: continue + if not(anchor_failure + "\n" + str(anchor) in list(G.nodes)): probabilities.append(probability_dict[anchor_failure]) + G.add_node(anchor_failure + "\n" + str(anchor)) + G = addMoreEdges(nearby_platforms, G, Array, anchor_failure, anchor, failures_c, failures_p, [platform, anchor]) + + for mooring in Array.platformList[platform].anchorList[anchor].mooringList: + # Create mooring nodes based on specific materials + for section in Array.platformList[platform].anchorList[anchor].mooringList[mooring].dd["sections"]: + for material in systems['moor_mat']: + if ((("ope" in section["type"]["material"]) and ("ire" in material)) or (("oly" in section["type"]["material"]) and ("ynth" in material)) )or (("hain" in section["type"]["material"]) and ("hain" in material)): + if not(material + "\n" + str(mooring) in list(G.nodes)): probabilities.append(probability_dict[material]) + G.add_node(material + "\n" + str(mooring)) + G = addMoreEdges(nearby_platforms, G, Array, material, mooring, failures_c, failures_p, [platform, anchor, mooring]) + + # Create other mooring nodes + for mooring_failure in systems['mooring']: + if (Array.platformList[platform].anchorList[anchor].mooringList[mooring].shared) and ("ing line non" in mooring_failure.replace("\n", " ")): continue + elif (not Array.platformList[platform].anchorList[anchor].mooringList[mooring].shared) and ("ared line" in mooring_failure.replace("\n", " ")): continue + else: + if not(mooring_failure + "\n" + str(mooring) in list(G.nodes)): probabilities.append(probability_dict[mooring_failure]) + G.add_node(mooring_failure + "\n" + str(mooring)) + G = addMoreEdges(nearby_platforms, G, Array, mooring_failure, mooring, failures_c, failures_p, [platform, anchor, mooring]) + + # Create mooring-clashing nodes + for mooring2 in Array.platformList[platform].anchorList[anchor].mooringList: + if not(mooring == mooring2): + print('not shared', mooring, mooring2) + mooring_pnt1 = Array.platformList[platform].anchorList[anchor].mooringList[mooring].rA[:2] + mooring_pnt2 = Array.platformList[platform].anchorList[anchor].mooringList[mooring].rB[:2] + mooring2_pnt1 = Array.platformList[platform].anchorList[anchor].mooringList[mooring2].rA[:2] + mooring2_pnt2 = Array.platformList[platform].anchorList[anchor].mooringList[mooring2].rB[:2] + mMaxX, mMinX, mMaxY, mMinY = get_min_max_vals(mooring_pnt1, mooring_pnt2, angle_radians) + cMaxX, cMinX, cMaxY, cMinY = get_min_max_vals(mooring2_pnt1, mooring2_pnt2, angle_radians) + x_overlap = (cMaxX < mMaxX and cMaxX > mMinX) or (cMinX > mMinX and cMinX < mMaxX) + y_overlap = (cMaxY < mMaxY and cMaxY > mMinY) or (cMinY > mMinY and cMinY < mMaxY) + x_on_top = (cMaxX <= mMaxX and cMaxX >= mMinX) and (cMinX >= mMinX and cMinX <= mMaxX) + y_on_top = (cMaxY <= mMaxY and cMaxY >= mMinY) and (cMinY >= mMinY and cMinY <= mMaxY) + if (x_overlap and y_overlap) or (x_on_top and y_on_top): + if not(systems['clashing'][0] + "\n" + str(mooring) + "," + str(mooring2) in list(G.nodes)): + probabilities.append(probability_dict[systems['clashing'][0]]) + G.add_node(systems['clashing'][0] + "\n" + str(mooring) + "," + str(mooring2)) + G = addMoreEdges(nearby_platforms, G, Array, systems['clashing'][0], str(mooring) + str(mooring2), failures_c, failures_p, [platform, anchor, mooring, mooring2]) + mooring_clashes.append(str(mooring) + str(mooring2)) + + # Assign connector failures + for connector in Array.platformList[platform].anchorList[anchor].mooringList[mooring].dd['connectors']: + for connector_failure in systems['connector']: + if not(connector_failure + "\n" + str(mooring) in list(G.nodes)): probabilities.append(probability_dict[connector_failure]) + G.add_node(connector_failure + "\n" + str(mooring)) + G = addMoreEdges(nearby_platforms, G, Array, connector_failure, mooring, failures_c, failures_p, [platform, anchor, mooring, connector]) + + # Create cable-clashing nodes + for cable in Array.cableList: + if platform in Array.cableList[cable].dd['platforms']: + + # Cable failures + for cable_failure in systems['cable']: + if 'disconnect' in cable_failure.replace('\n',' ').lower(): + if not(cable_failure + "\n" + str(cable) + ' ' + str(platform) in list(G.nodes)): + probabilities.append(probability_dict[cable_failure]) + G.add_node(cable_failure + "\n" + str(platform)+','+str(cable)) + G = addMoreEdges(nearby_platforms, G, Array, cable_failure, str(platform)+','+str(cable), failures_c, failures_p, [platform, anchor, mooring, cable]) + else: + if not(cable_failure + "\n" + str(cable) in list(G.nodes)): + probabilities.append(probability_dict[cable_failure]) + G.add_node(cable_failure + "\n" + str(cable)) + G = addMoreEdges(nearby_platforms, G, Array, cable_failure, cable, failures_c, failures_p, [platform, anchor, mooring, cable]) + + # Cable buoyancy modules and materials + for cable_section in Array.cableList[cable].dd['cables']: + # Create buoyancy module nodes (one node per cable) + if 'buoyancy_sections' in cable_section.dd: + n_modules = cable_section.dd['buoyancy_sections'][0]['N_modules'] + for buoy_failure in systems['buoyancy']: + if not(buoy_failure + "\n" + str(cable) in list(G.nodes)): + probabilities.append(probability_dict[buoy_failure]) + G.add_node(buoy_failure + "\n" + str(cable)) + G = addMoreEdges(nearby_platforms, G, Array, buoy_failure, cable, failures_c, failures_p, [platform, anchor, mooring, cable]) + + # Create either dynamic or static cable failure node (one of each per cable at a maximum) + if cable_section.dd['cable_type']['dynamic']: + if not(systems['cable_mat'][0] + "\n" + str(cable) in list(G.nodes)): + probabilities.append(probability_dict[systems['cable_mat'][0]]) + G.add_node(systems['cable_mat'][0] + "\n" + str(cable)) + G = addMoreEdges(nearby_platforms, G, Array, systems['cable_mat'][0], cable, failures_c, failures_p, [platform, anchor, mooring, cable]) + else: + if not(systems['cable_mat'][1] + "\n" + str(cable) in list(G.nodes)): + probabilities.append(probability_dict[systems['cable_mat'][1]]) + G.add_node(systems['cable_mat'][1] + "\n" + str(cable)) + G = addMoreEdges(nearby_platforms, G, Array, systems['cable_mat'][1], cable, failures_c, failures_p, [platform, anchor, mooring, cable]) + + # Cable clashing failures + cable_pnt1 = np.array(Array.platformList[Array.cableList[cable].dd['platforms'][0]].r) + if not ('substation' in Array.cableList[cable].dd['platforms'][1].lower()): + cable_pnt2 = np.array(Array.platformList[Array.cableList[cable].dd['platforms'][1]].r) + else: cable_pnt2 = np.array(Array.substationList[Array.cableList[cable].dd['platforms'][1]].r) + mooring_pnt1 = Array.platformList[platform].anchorList[anchor].mooringList[mooring].rA[:2] + mooring_pnt2 = Array.platformList[platform].anchorList[anchor].mooringList[mooring].rB[:2] + cMaxX, cMinX, cMaxY, cMinY = get_min_max_vals(cable_pnt1, cable_pnt2, angle_radians) + mMaxX, mMinX, mMaxY, mMinY = get_min_max_vals(mooring_pnt1, mooring_pnt2, angle_radians) + x_overlap = (cMaxX < mMaxX and cMaxX > mMinX) or (cMinX > mMinX and cMinX < mMaxX) + y_overlap = (cMaxY < mMaxY and cMaxY > mMinY) or (cMinY > mMinY and cMinY < mMaxY) + x_on_top = (cMaxX <= mMaxX and cMaxX >= mMinX) and (cMinX >= mMinX and cMinX <= mMaxX) + y_on_top = (cMaxY <= mMaxY and cMaxY >= mMinY) and (cMinY >= mMinY and cMinY <= mMaxY) + if (x_overlap and y_overlap) or ((cMaxX == mMaxX and cMinX == mMinX)and(cMaxY == mMaxY and cMinY == mMinY)): + for cable_clashing_failure in systems['clashing'][1:]: + if not(cable_clashing_failure + "\n" + str(cable)+str(mooring) in list(G.nodes)): + probabilities.append(probability_dict[cable_clashing_failure]) + G.add_node(cable_clashing_failure + "\n" + str(cable)+str(mooring)) + G = addMoreEdges(nearby_platforms, G, Array, cable_clashing_failure, str(cable)+str(mooring), failures_c, failures_p, [platform, cable, anchor, mooring, str(cable)+str(mooring)]) + cable_clashes.append(str(cable)+str(mooring)) + if len(cable_clashes) < 1: + if not(systems['clashing'][1] + "\n" + str(platform) + "," + str(cable) in list(G.nodes)): + probabilities.append(probability_dict[systems['clashing'][1]]) + G.add_node(systems['clashing'][1] + "\n" + str(platform) + "," + str(cable)) + G = addMoreEdges(nearby_platforms, G, Array, systems['clashing'][1], str(platform) + ',' + str(cable), failures_c, failures_p, [platform, anchor, cable, mooring, str(cable) + ' ' + str(platform)]) + if not(systems['clashing'][2] + "\n" + str(platform) + ',' + str(cable) in list(G.nodes)): + probabilities.append(probability_dict[systems['clashing'][2]]) + G.add_node(systems['clashing'][2] + "\n" + str(platform) + ',' + str(cable)) + G = addMoreEdges(nearby_platforms, G, Array, systems['clashing'][2], str(platform) + ',' + str(cable), failures_c, failures_p, [platform, anchor, cable, mooring, str(cable) + ' ' + str(platform)]) + + # If there are no specific mooring-mooring clashing, create a generic mooring-mooring clashing node for the platform + if len(mooring_clashes) < 1: + fail_list = [platform, anchor] + for a_variable in Array.platformList[platform].mooringList.keys(): + fail_list.append(a_variable) + if not(systems['clashing'][0] + "\n" + str(platform) in list(G.nodes)): + probabilities.append(probability_dict[systems['clashing'][0]]) + G.add_node(systems['clashing'][0] + "\n" + str(platform)) + G = addMoreEdges(nearby_platforms, G, Array, systems['clashing'][0], platform, failures_c, failures_p, fail_list) + + # Shared mooring line failures + for mooring in Array.platformList[platform].mooringList: + if Array.platformList[platform].mooringList[mooring].shared: + for mooring_failure in systems['mooring']: + if not twoTurbine or 'shared line' in mooring_failure.replace('\n', ' ').lower(): + if not(mooring_failure + "\n" + str(mooring) in list(G.nodes)): + probabilities.append(probability_dict[mooring_failure]) + G.add_node(mooring_failure + "\n" + str(mooring)) + G = addMoreEdges(nearby_platforms, G, Array, mooring_failure, mooring, failures_c, failures_p, [platform, mooring]) + + # Create mooring nodes based on specific materials + for section in Array.platformList[platform].mooringList[mooring].dd["sections"]: + for material in systems['moor_mat']: + if ((("ope" in section["type"]["material"]) and ("ire" in material)) or (("oly" in section["type"]["material"]) and ("ynth" in material)) )or (("hain" in section["type"]["material"]) and ("hain" in material)): + if not(material + "\n" + str(mooring) in list(G.nodes)): probabilities.append(probability_dict[material]) + G.add_node(material + "\n" + str(mooring)) + G = addMoreEdges(nearby_platforms, G, Array, material, mooring, failures_c, failures_p, [platform, mooring]) + + # Create mooring clashing nodes + for mooring2 in Array.platformList[platform].anchorList[anchor].mooringList: + if not(mooring == mooring2): + mooring_pnt1 = np.array(Array.platformList[platform].mooringList[mooring].rA[:2]) + mooring_pnt2 = np.array(Array.platformList[platform].mooringList[mooring].rB[:2]) + mooring2_pnt1 = np.array(Array.platformList[platform].mooringList[mooring2].rA[:2]) + mooring2_pnt2 = np.array(Array.platformList[platform].mooringList[mooring2].rB[:2]) + mMaxX, mMinX, mMaxY, mMinY = get_min_max_vals(mooring_pnt1, mooring_pnt2, angle_radians) + cMaxX, cMinX, cMaxY, cMinY = get_min_max_vals(mooring2_pnt1, mooring2_pnt2, angle_radians) + x_overlap = (cMaxX < mMaxX and cMaxX > mMinX) or (cMinX > mMinX and cMinX < mMaxX) + y_overlap = (cMaxY < mMaxY and cMaxY > mMinY) or (cMinY > mMinY and cMinY < mMaxY) + x_on_top = (cMaxX <= mMaxX and cMaxX >= mMinX) and (cMinX >= mMinX and cMinX <= mMaxX) + y_on_top = (cMaxY <= mMaxY and cMaxY >= mMinY) and (cMinY >= mMinY and cMinY <= mMaxY) + if (x_overlap and y_overlap) or (x_on_top or y_on_top): + if not(systems['clashing'][0] in list(G.nodes)): probabilities.append(probability_dict[systems['clashing'][0]]) + G.add_node(systems['clashing'][0] + "\n" + str(mooring) + "," + str(mooring2)) + G = addMoreEdges(nearby_platforms, G, Array, systems['clashing'][0], str(mooring) + "," + str(mooring2), failures_c, failures_p, [platform, mooring, mooring2]) + mooring_clashes.append(str(mooring) + str(mooring2)) + + # Cable clashing failures + for cable in Array.cableList: + if platform in Array.cableList[cable].dd['platforms']: + cable_pnt1 = np.array(Array.platformList[Array.cableList[cable].dd['platforms'][0]].r) + if not ('substation' in Array.cableList[cable].dd['platforms'][1].lower()): + cable_pnt2 = np.array(Array.platformList[Array.cableList[cable].dd['platforms'][1]].r) + else: cable_pnt2 = np.array(Array.substationList[Array.cableList[cable].dd['platforms'][1]].r) + + if not ('substation' in mooring[0].lower()):mooring_pnt1 = np.array(Array.platformList[mooring[0]].r) + else: mooring_pnt1 = np.array(Array.substationList[mooring[0]].r) + if not ('substation' in mooring[1].lower()): mooring_pnt2 = np.array(Array.platformList[mooring[1]].r) + else: mooring_pnt2 = np.array(Array.substationList[mooring[1]].r) + + x_overlap = (cMaxX < mMaxX and cMaxX > mMinX) or (cMinX > mMinX and cMinX < mMaxX) + y_overlap = (cMaxY < mMaxY and cMaxY > mMinY) or (cMinY > mMinY and cMinY < mMaxY) + cMaxX, cMinX, cMaxY, cMinY = get_min_max_vals(cable_pnt1, cable_pnt2, angle_radians) + mMaxX, mMinX, mMaxY, mMinY = get_min_max_vals(mooring_pnt1, mooring_pnt2, angle_radians) + if (x_overlap and y_overlap) or ((cMaxX == mMaxX and cMinX == mMinX)and(cMaxY == mMaxY and cMinY == mMinY)): + for cable_clashing_failure in systems['clashing'][1:]: + if not (systems['clashing'][1] in list(G.nodes)): probabilities.append(probability_dict[systems['clashing'][1]]) + G.add_node(systems['clashing'][1] + "\n" + str(mooring) + "," + str(cable)) + G = addMoreEdges(nearby_platforms, G, Array, systems['clashing'][1], str(mooring) + "," + str(cable), failures_c, failures_p, [platform, cable, mooring, str(mooring) + "," + str(cable)]) + G = addMoreEdges(nearby_platforms, G, Array, systems['clashing'][1], str(platform) + "," + str(cable), failures_c, failures_p, [platform, cable, mooring, str(platform) + "," + str(cable)]) + cable_clashes.append(str(cable)+str(mooring)) + + # Grid/substation failures + for grid_failure in systems['grid']: + if not(grid_failure in list(G.nodes)): + probabilities.append(probability_dict[grid_failure]) + G.add_node(grid_failure + "\n" + "") + G = addMoreEdges(nearby_platforms, G, Array, grid_failure, "", failures_c, failures_p, [platform]) + + + +# If interested in the twoTurbine case study, alter a few of the failure nodes +if twoTurbine: + list_of_failures_to_rid = ['Tether & anchor systems array_cable10', 'Cable protection system array_cable10', + 'Terminations array_cable10', 'Offshore joints array_cable10'] + for failure in list(G.nodes): + if 'connector' in failure.replace('\n', ' ').lower(): + G.remove_node(failure) + if 'buoyancy modules' in failure.replace('\n', ' ').lower(): + G.remove_node(failure) + if failure.replace('\n', ' ') in list_of_failures_to_rid: + G.remove_node(failure) + + + +# Print the number of nodes and (if the user wants) the list of nodes +print('\nNumber of nodes -',len(G.nodes),'\n') +user_input3 = input("Would you like to see the list of failures? ") +if 'y' in user_input3.lower() or 'rue' in user_input3.lower(): + for edge in list(G.nodes): + print(edge.replace("\n", " ")) + +# Print the number of nodes and (if the user wants) the list of edges +print('\nNumber of edges -',len(G.edges),'\n') +user_input4 = input("Would you like to see the list of edges? ") +if 'y' in user_input4.lower() or 'rue' in user_input4.lower(): + itervar = 0 + for edge in list(G.edges): + print(edge) + itervar += 1 + if (itervar + 1) % 1000 == 0: user_input45 = input("Continue? ") + + + +# If the user wants to input probabilities for specific edges, reweight edges based on the user's inputs +user_inputs = input("Would you like to input probabilities into adjacency matrix? ") +twoTurbine_calculationType = False +if (user_inputs == 'y' or user_inputs == 'yes') or user_inputs == 'True': + twoTurbine_calculationType = True + for i in range(len(G.edges)): + edge = list(G.edges)[i] + if ('rift off' in edge[0].replace("\n", " ") or 'ncreased' in edge[0].replace("\n", " ")) or 'ynamics' in edge[0].replace("\n", " "): + G = edgeReweight(G, edge) + elif ('apsize' in edge[0].replace("\n", " ") or '-cable' in edge[0].replace("\n", " ")) or 'ing line non' in edge[0].replace("\n", " "): + G = edgeReweight(G, edge) + elif ('ragging' in edge[0].replace("\n", " ") or 'hain' in edge[0].replace("\n", " ")) or 'ire rope' in edge[0].replace("\n", " "): + G = edgeReweight(G, edge) + elif ('ynthetic' in edge[0].replace("\n", " ") or 'able profile' in edge[0].replace("\n", " ")) or 'ared line' in edge[0].replace("\n", " "): + G = edgeReweight(G, edge) + elif ('load on cable' in edge[0].replace("\n", " ") or 'eight' in edge[0].replace("\n", " ")): + G = edgeReweight(G, edge) + + + +# Ask user if they are ready to continue to Bayesian network calculations (if not, quit) +continue_input = input("Ready to continue? ") +if 'n' in continue_input.lower(): + quit() + + +# Bayesian network calculation +arr = nx.to_numpy_array(G) +nodeNames = np.reshape(np.array(list(G.nodes)), (len(list(G.nodes)), )) + +poc = "child" +filename = "turbineInference_" + poc + "FAModelTwoTurbine" + "_" + str(nba_input) + ".xlsx" +for start_component in range(1,arr.shape[0]+1): # Iterate through each failure mode/effect in turbine + print(start_component) + +with pd.ExcelWriter(filename) as writer: + all_probabilities = np.zeros(arr.shape) # Initialize a large array to put all the pairwise probabilities in + for start_component in range(1,arr.shape[0]+1): # Iterate through each failure mode/effect in turbine + a = arr.copy() + non = nodeNames + K, a, g, e, m, non = breadth_first_multi(a, nodeNames, [start_component], poc) # Generate tree for Bayesian network + prblts = [] # Initialize array of node probabilities (in order of appearance in graph) + for node in non: + node_index = np.where(nodeNames == node)[0][0] + prblts.append(probabilities[node_index]) # Add nodes to array of node probabilities + prblts = np.array(prblts) + + probabilitiy_table = np.zeros((2, a.shape[0])) # Initialize table of inference probabilities + nodes = diagonal_nodes(a) # Diagonal matrix of node names (numerical +1) + a = make_binary(a, 0.5) # Binarize adjacency table + + nodeNamesArray = np.array(list(K.nodes)) # Initialize array of names of nodes + nodeNamesArray = np.reshape(nodeNamesArray, (len(nodeNamesArray),)) # Make numpy array + + # Interence----------------------------------------------------------------------- + for node in range(a.shape[0]): + pts_bool = nodes @ a[:, node] # vector of zeros and child names (numerical names) + pts = pts_bool[np.nonzero(pts_bool)] #list of just the child names (numerical names) + + if len(pts) < 1: # If no parents, add probability of failure happening to the probability table + probabilitiy_table[0][node] = probabilities[np.where(nodeNames == nodeNamesArray[int(node)])[0][0]] + probabilitiy_table[1][node] = 1 - probabilities[np.where(nodeNames == nodeNamesArray[int(node)])[0][0]] + continue + + if twoTurbine_calculationType: + parents, our_table = twoTurbine_bayesian_table(a, arr, node + 1, nodeNames, non) # Calculate the probability distribution table + else: + parents, our_table = bayesian_table(a, node+1, True, nodeNames, True, prblts) + mlt_table = np.ones((our_table.shape[0],2)) # Initialize table for multiplying across rows of probability distribution table + + # Calculate Probability Table ------------------------------------------------------------ + for i in range(our_table.shape[0]): + for j in range(our_table.shape[1] - 2): + parent = int(parents[j]) + if our_table[i,j] == 0: + our_table[i,j] = probabilitiy_table[0][parent - 1] + if probabilitiy_table[0][parent - 1] == 0: + break + else: + our_table[i,j] = probabilitiy_table[1][parent - 1] + if (parent-1 == 0): # If the node's parent is the evidence, zero out the non-failing possibility + our_table[i,j] = 0 + mlt_table[i,0] *= our_table[i,j] # Multiply the probabilities across the probability distribution table + mlt_table[i,1] = mlt_table[i,0] * our_table[i, -1] # Multiple by the probability of event not failing given combination of parent failure + mlt_table[i,0] *= our_table[i, -2] # Multiple by the probability of event failing given combination of parent failure + sm_table = np.sum(mlt_table, axis = 0) #/np.sum(mlt_table) # Sum the products of probabilities across the columns + probabilitiy_table[0][node] = sm_table[0] # Update the inference probability table with the probabilites just calculated + probabilitiy_table[1][node] = sm_table[1] + + # Print and add probability of node to table + print(start_component, node, " --> Probability of ", nodeNamesArray[node].replace("\n", " "), "=", sm_table) + index2 = np.where(nodeNames == nodeNamesArray[node])[0][0] + all_probabilities[0 * arr.shape[0] + start_component - 1][0 * arr.shape[0] + index2] = sm_table[0]/np.sum(sm_table) + + # Write array to dataframe + df3 = pd.DataFrame(all_probabilities) + df3.to_excel(writer, sheet_name="allProbs") + + +# If we want to plot the network, plot it now +if plot: + nx.draw_networkx(G) + plt.show() \ No newline at end of file From 08a57d71bb2a5e18ce9a6e513ee8639e1e5da1d2 Mon Sep 17 00:00:00 2001 From: EmmaSlackGraph Date: Thu, 6 Jun 2024 10:27:34 -0600 Subject: [PATCH 04/24] Add subnode and subedge failures + fix clashing failure nodes --- failureGraphs.py | 604 +++++++++++++++-------------------------------- 1 file changed, 186 insertions(+), 418 deletions(-) diff --git a/failureGraphs.py b/failureGraphs.py index ef9c78da..4e3b5b0c 100644 --- a/failureGraphs.py +++ b/failureGraphs.py @@ -2,48 +2,42 @@ import networkx as nx import matplotlib.pyplot as plt import pandas as pd -from failureProbabilities import * -from twoTurbineCaseStudy import * +from failure.failureProbabilities import * +from failure.twoTurbineCaseStudy import * from famodel.project import Project +def couldClash(G, failure, a1, a2, reverse): + if a1 == a2 or (failure + "\n" + str(a1.id) + str(a2.id) in list(G.nodes) or failure + "\n" + str(a2.id) + str(a1.id) in list(G.nodes)): + return False + a1_pnt1 = np.array(a1.rA[:2]) + a1_pnt2 = np.array(a1.rB[:2]) + a2_pnt1 = np.array(a2.rA[:2]) + a2_pnt2 = np.array(a2.rB[:2]) + # print('\n', a1_pnt1, a1_pnt2) + # print(a2_pnt1, a2_pnt2) + a1MaxX, a1MinX, a1MaxY, a1MinY = get_min_max_vals(a1_pnt1, a1_pnt2, angle_radians, reverse) + a2MaxX, a2MinX, a2MaxY, a2MinY = get_min_max_vals(a2_pnt1, a2_pnt2, angle_radians, False) + + overlap = False + for corner_x in [a1MaxX,a1MinX,]: + for corner_y in [a1MaxY, a1MinY]: + if (corner_x <= a2MaxX and corner_x >= a2MinX) and (corner_y <= a2MaxY and corner_y >= a2MinY): + overlap = True + # print("TRUE!!!!! ------------->") + # print(corner_x < a2MaxX, corner_x > a2MinX) + # print([corner_x, corner_y], a2MaxX, a2MinX, a2MaxY, a2MinY) + # print(a1MaxX, a1MinX, a1MaxY, a1MinY, '\n') + return overlap def addMoreEdges(nearby_platforms, G, Array, node, node_id, failures_c, failures_p, ids): - node_index = np.where(nodeNames == node)[0][0] - platforms = list(Array.platformList.keys()) - if len(nearby_platforms) > 1: - platforms = nearby_platforms - cables = list(Array.cableList.keys()) - all_ids = [] - combos = [] - for p in platforms: - for c in cables: - if c in p: - combos.append(str(p) + ',' + str(c)) - all_ids.append(c) - for m in Array.platformList[p]: - combos.append(str(m) + ',' + str(c)) - all_ids.append(str(m)) - for a in Array.platformList[p].anchorList: - all_ids.append(a) - all_ids = platforms+all_ids+list(combos) for child in failures_c[node]: - child_index = np.where(nodeNames == child)[0][0] - original_ids = ids - if arr[node_index, child_index] > 1.5: - ids = all_ids for id in ids: if child + "\n" + str(id) in G.nodes: G.add_edge(node + "\n" + str(node_id), child + "\n" + str(id), weight=0.001) - ids = original_ids for parent in failures_p[node]: - parent_index = np.where(nodeNames == parent)[0][0] - original_ids = ids - if arr[parent_index, node_index] > 1.5: - ids = all_ids for id in ids: if parent + "\n" + str(id) in G.nodes: G.add_edge(parent + "\n" + str(id), node + "\n" + str(node_id), weight=0.001) - ids = original_ids return G def edgeReweight(G, edge): @@ -56,8 +50,13 @@ def edgeReweight(G, edge): def get_y_Value(point1, point2, x): return ((point2[1]-point1[1])/(point2[0]-point1[0]))*(x-point1[0])+point1[1] -def get_min_max_vals(pnt1, pnt2, angle_radians): +def get_min_max_vals(pnt2, pnt1, angle_radians, reverse): + if reverse: + pnt_hold = pnt1 + pnt1 = pnt2 + pnt2 = pnt_hold vector = pnt2 - pnt1 + # print('vect', vector) length = math.sqrt(vector[0]**2 + vector[1]**2) if vector[0] == 0.0 and vector[1] > 0: angle = math.pi/2 elif vector[0] == 0.0 and vector[1] < 0: angle = 3*math.pi/2 @@ -65,6 +64,9 @@ def get_min_max_vals(pnt1, pnt2, angle_radians): else: angle = math.atan(vector[1]/vector[0]) if angle == 0 and vector[0] < 0: angle = math.pi + if (angle > -math.pi*0.5 and angle < math.pi*0.5) and vector[0] < 0: + angle += math.pi + # print('angle', angle) new_vector1 = np.array([length * math.cos(angle - angle_radians), length * math.sin(angle - angle_radians)]) + np.array(pnt1) new_vector2 = np.array([length * math.cos(angle + angle_radians), length * math.sin(angle + angle_radians)]) + np.array(pnt1) if angle == math.pi and angle_radians==0: @@ -81,34 +83,20 @@ def get_min_max_vals(pnt1, pnt2, angle_radians): # Create project class instance from yaml file Array = Project(file='famodel/OntologySample600m.yaml') -print() # Create adjacency matrix from failure matrix -df = pd.read_excel("/Users/eslack/Documents/Code/ExcelFiles/failureData.xlsx", sheet_name="bMMatch") +df = pd.read_excel("failure/failureData.xlsx", sheet_name="bMMatch") arr = df.to_numpy()[:,1:] nodeNames = df.to_numpy()[:, 0].flatten() # Get initial failure probabilities for each failure mode and effect -probabilities, array_of_probs = getProbabilities("failureProbabilities.xlsx", "Sheet3") +probabilities, array_of_probs = getProbabilities("failure/failureProbabilities.xlsx", "Sheet3") - -# Determine if using the twoTurbine case study model -tT_input = input("Would you like to calculate for the twoTurbine case study? ") -if 'y' in tT_input: twoTurbine = True -else: twoTurbine = False - # Determine angle of clashing we are interested in -angle_degree = float(input("What angle do you want to use? (in degrees) ")) -angle_radians = angle_degree/360 * math.pi * 2 - -# Determine what the nearby platforms are (True ==> all other platforms, False ==> physically close turbines) -nba_input = input("Would you like one turbine to directly affect all turbines? ") -nba = False -if 'y' in nba_input.lower(): - nba = True - - +# angle_degree = float(input("What angle do you want to use? (in degrees) ")) +# angle_radians = angle_degree/360 * math.pi * 2 +angle_radians = 30/360 * math.pi * 2 # Initialize and create the dictionaries of the children, parents, and probabilities for each failure failures_c = {} @@ -130,396 +118,176 @@ def get_min_max_vals(pnt1, pnt2, angle_radians): # Create dictionary for each subsystem of failures turbine = [0,1,2,3,4,5,26,27,28,29] platform = [6,7,8,9,10,11,30,31,32] -clashing = [12,13,14] -mooring_materials = [33,34,35] -mooring = [15,16,17, 18] +mooringmooring = [12] +mooringcable = [13,14] +rope = [35] +polyester = [34] +chain = [33] +mooring = [15,16,17] connector = [36] clump_weight = [37] -anchor = [19,20,38,39] -cable = [21,22,23,41,42, 45,46] -cable_mat = [43,44] +anchor = [19,20,38] +cable = [21,22,23,40, 41,42, 45] +dynamic = [43] +static = [44] grid = [24,25] +joints = [46] buoyancy = [40] - -systems = {'turbine':nodeNames[turbine], 'platform':nodeNames[platform], 'clashing':nodeNames[clashing], 'moor_mat':nodeNames[mooring_materials], - 'mooring':nodeNames[mooring], 'connector':nodeNames[connector], 'weight':nodeNames[clump_weight], - 'anchor':nodeNames[anchor], 'cable':nodeNames[cable], 'grid':nodeNames[grid], 'cable_mat':nodeNames[cable_mat], - 'buoyancy':nodeNames[buoyancy]} +sharedmooring = [15,16,18] +sharedanchor = [19,20,39] +systems = {'turbine':nodeNames[turbine], 'platform':nodeNames[platform], 'mooringmooring':nodeNames[mooringmooring], + 'rope':nodeNames[rope], 'chain':nodeNames[chain],'polyester':nodeNames[polyester],'mooringcable':nodeNames[mooringcable], + 'cablemooring':nodeNames[mooringcable], 'mooring':nodeNames[mooring], 'connector':nodeNames[connector], + 'weight':nodeNames[clump_weight], 'anchor':nodeNames[anchor], 'cable':nodeNames[cable], 'grid':nodeNames[grid], + 'dynamic':nodeNames[dynamic], 'static':nodeNames[static], 'buoyancy':nodeNames[buoyancy], 'cablecable': [], + 'sharedmooring':nodeNames[sharedmooring], 'sharedmooringcable':nodeNames[mooringcable], 'joints': nodeNames[joints], + 'cablesharedmooring':nodeNames[mooringcable], 'sharedmooringmooring':nodeNames[mooringmooring], + 'mooringsharedmooring':nodeNames[mooringmooring], 'sharedanchor': nodeNames[sharedanchor]} # Initialize graph, boolean for plotting, and list of probabilities G = nx.DiGraph() plot = False -probabilities = [] +# FIRST DEGREE NODES ------------------------------------------------------------------------------------------- for platform in Array.platformList: - # Initialize list of mooring-mooring clashing nodea and list of mooring-cable and anchor-cable clashing nodes + # print(platform, Array.platformList[platform].r) + attachments = Array.platformList[platform].attachments + nearby_platforms = [] mooring_clashes = [] cable_clashes = [] + num_cables = [] - # Determine the nearby platforms - nearby_platforms = [] - for platform2 in Array.platformList: - platform_xy = Array.platformList[platform].r - platform2_xy = Array.platformList[platform2].r - dist_btwn_turbines = math.sqrt((platform_xy[0] - platform2_xy[0])**2 + (platform_xy[1] - platform2_xy[1])**2) - if dist_btwn_turbines <= (1600 * math.sqrt(2)): - nearby_platforms.append(platform2) - if 'y' in nba_input: - nearby_platforms = [] - - # Create platform nodes + # Create platform failure nodes for platform_failure in systems['platform']: - if not(platform_failure + "\n" + str(platform) in list(G.nodes)): - probabilities.append(probability_dict[platform_failure]) G.add_node(platform_failure + "\n" + str(platform)) G = addMoreEdges(nearby_platforms, G, Array, platform_failure, platform, failures_c, failures_p, [platform]) - # Create turbine nodes + # Create failure nodes for turbine_failure in systems['turbine']: - if not(turbine_failure + "\n" + str(platform) in list(G.nodes)): probabilities.append(probability_dict[turbine_failure]) G.add_node(turbine_failure + "\n" + str(platform)) G = addMoreEdges(nearby_platforms, G, Array, turbine_failure, platform, failures_c, failures_p, [platform]) - for anchor in Array.platformList[platform].anchorList: - # Create anchor nodes - for anchor_failure in systems['anchor']: - if len(Array.platformList[platform].anchorList[anchor].mooringList) > 1 and "ingle" in anchor_failure: continue - elif len(Array.platformList[platform].anchorList[anchor].mooringList) <= 1 and "hared" in anchor_failure: continue - if not(anchor_failure + "\n" + str(anchor) in list(G.nodes)): probabilities.append(probability_dict[anchor_failure]) - G.add_node(anchor_failure + "\n" + str(anchor)) - G = addMoreEdges(nearby_platforms, G, Array, anchor_failure, anchor, failures_c, failures_p, [platform, anchor]) - - for mooring in Array.platformList[platform].anchorList[anchor].mooringList: - # Create mooring nodes based on specific materials - for section in Array.platformList[platform].anchorList[anchor].mooringList[mooring].dd["sections"]: - for material in systems['moor_mat']: - if ((("ope" in section["type"]["material"]) and ("ire" in material)) or (("oly" in section["type"]["material"]) and ("ynth" in material)) )or (("hain" in section["type"]["material"]) and ("hain" in material)): - if not(material + "\n" + str(mooring) in list(G.nodes)): probabilities.append(probability_dict[material]) - G.add_node(material + "\n" + str(mooring)) - G = addMoreEdges(nearby_platforms, G, Array, material, mooring, failures_c, failures_p, [platform, anchor, mooring]) - - # Create other mooring nodes - for mooring_failure in systems['mooring']: - if (Array.platformList[platform].anchorList[anchor].mooringList[mooring].shared) and ("ing line non" in mooring_failure.replace("\n", " ")): continue - elif (not Array.platformList[platform].anchorList[anchor].mooringList[mooring].shared) and ("ared line" in mooring_failure.replace("\n", " ")): continue + # FIRST DEGREE EDGES ------------------------------------------------------------------------------------------- + for attach1 in attachments.keys(): + attach1_name = str(attachments[attach1]['id']) + attach1_type = '' + if 'mooring' in str(type(attachments[attach1]['obj'])): + if attachments[attach1]['obj'].shared: attach1_type = 'sharedmooring' + else: attach1_type = 'mooring' + elif 'cable' in str(type(attachments[attach1]['obj'])): + attach1_type = 'cable' + num_cables.append(attachments[attach1]['obj'].id) + + # Create moroing/cable failure nodes + for attach1_failure in systems[attach1_type]: + original_name = attach1_name + if 'connect' in attach1_failure: attach1_name = platform + attach1_name + G.add_node(attach1_failure + "\n" + attach1_name) + G = addMoreEdges(nearby_platforms, G, Array, attach1_failure, attach1_name, failures_c, failures_p, [platform, attach1_name]) + attach1_name = original_name + + # Create clashing failure nodes + for attach2 in attachments.keys(): + attach2_name = str(attachments[attach2]['id']) + attach2_type = '' + clash_name = str(attach1_name)+str(attach2_name) + if 'mooring' in str(type(attachments[attach2]['obj'])): attach2_type = 'mooring' + elif 'cable' in str(type(attachments[attach2]['obj'])): attach2_type = 'cable' + for clash_failure in systems[(str(attach1_type)+str(attach2_type))]: + if 'shared' in attach1_type and all(abs(np.array(attachments[attach1]['obj'].rB[:2]) - np.array(attachments[attach2]['obj'].rB[:2])) < 100): reverse = True + else: reverse = False + # print('could clash --', couldClash(G, clash_failure, attachments[attach1]['obj'], attachments[attach2]['obj']), '--', attach1_name, attach2_name) + if couldClash(G, clash_failure, attachments[attach1]['obj'], attachments[attach2]['obj'], reverse): + G.add_node(clash_failure + "\n" + clash_name) + G = addMoreEdges([attach1_name, attach2_name], G, Array, clash_failure, clash_name, failures_c, failures_p, [platform, attach1_name, attach2_name, clash_name]) + + if attach1_type == 'mooring' and attach2_type == attach1_type: mooring_clashes.append(clash_failure + "\n" + clash_name) + elif ('shared' not in attach1_type) and ('shared' not in attach2_type): cable_clashes.append(clash_failure + "\n" + clash_name) + + # SUBNODES AND SUBEDGES ------------------------------------------------------------------------------------ + subcomponents = attachments[attach1]['obj'].subcomponents + component_num = 0 + for component in subcomponents: + component_num += 1 + if 'mooring' in attach1_type: + if 'type' in component.keys(): + # Create clump weight failure nodes + if 'str' in str(type(component['type'])) and 'weight' in component['type']: + component_type = 'weight' + component_name = str(attach1_name + ' ' + component['type'] + ' ' + str(component_num)) + + # Create mooring material failure nodes + if 'dict' in str(type(component['type'])): + if 'polyester' in component['type']['material']: component_type = 'polyester' + elif 'chain' in component['type']['material']: component_type = 'chain' + elif 'rope' in component['type']['material']: component_type = 'rope' + component_name = attach1_name + ' ' + str(component['type']['name']) + + # Create connector failure nodes else: - if not(mooring_failure + "\n" + str(mooring) in list(G.nodes)): probabilities.append(probability_dict[mooring_failure]) - G.add_node(mooring_failure + "\n" + str(mooring)) - G = addMoreEdges(nearby_platforms, G, Array, mooring_failure, mooring, failures_c, failures_p, [platform, anchor, mooring]) - - # Create mooring-clashing nodes - for mooring2 in Array.platformList[platform].anchorList[anchor].mooringList: - if not(mooring == mooring2): - print('not shared', mooring, mooring2) - mooring_pnt1 = Array.platformList[platform].anchorList[anchor].mooringList[mooring].rA[:2] - mooring_pnt2 = Array.platformList[platform].anchorList[anchor].mooringList[mooring].rB[:2] - mooring2_pnt1 = Array.platformList[platform].anchorList[anchor].mooringList[mooring2].rA[:2] - mooring2_pnt2 = Array.platformList[platform].anchorList[anchor].mooringList[mooring2].rB[:2] - mMaxX, mMinX, mMaxY, mMinY = get_min_max_vals(mooring_pnt1, mooring_pnt2, angle_radians) - cMaxX, cMinX, cMaxY, cMinY = get_min_max_vals(mooring2_pnt1, mooring2_pnt2, angle_radians) - x_overlap = (cMaxX < mMaxX and cMaxX > mMinX) or (cMinX > mMinX and cMinX < mMaxX) - y_overlap = (cMaxY < mMaxY and cMaxY > mMinY) or (cMinY > mMinY and cMinY < mMaxY) - x_on_top = (cMaxX <= mMaxX and cMaxX >= mMinX) and (cMinX >= mMinX and cMinX <= mMaxX) - y_on_top = (cMaxY <= mMaxY and cMaxY >= mMinY) and (cMinY >= mMinY and cMinY <= mMaxY) - if (x_overlap and y_overlap) or (x_on_top and y_on_top): - if not(systems['clashing'][0] + "\n" + str(mooring) + "," + str(mooring2) in list(G.nodes)): - probabilities.append(probability_dict[systems['clashing'][0]]) - G.add_node(systems['clashing'][0] + "\n" + str(mooring) + "," + str(mooring2)) - G = addMoreEdges(nearby_platforms, G, Array, systems['clashing'][0], str(mooring) + str(mooring2), failures_c, failures_p, [platform, anchor, mooring, mooring2]) - mooring_clashes.append(str(mooring) + str(mooring2)) + component_type = 'connector' + component_name = attach1_name + ' connector' - # Assign connector failures - for connector in Array.platformList[platform].anchorList[anchor].mooringList[mooring].dd['connectors']: - for connector_failure in systems['connector']: - if not(connector_failure + "\n" + str(mooring) in list(G.nodes)): probabilities.append(probability_dict[connector_failure]) - G.add_node(connector_failure + "\n" + str(mooring)) - G = addMoreEdges(nearby_platforms, G, Array, connector_failure, mooring, failures_c, failures_p, [platform, anchor, mooring, connector]) - - # Create cable-clashing nodes - for cable in Array.cableList: - if platform in Array.cableList[cable].dd['platforms']: - - # Cable failures - for cable_failure in systems['cable']: - if 'disconnect' in cable_failure.replace('\n',' ').lower(): - if not(cable_failure + "\n" + str(cable) + ' ' + str(platform) in list(G.nodes)): - probabilities.append(probability_dict[cable_failure]) - G.add_node(cable_failure + "\n" + str(platform)+','+str(cable)) - G = addMoreEdges(nearby_platforms, G, Array, cable_failure, str(platform)+','+str(cable), failures_c, failures_p, [platform, anchor, mooring, cable]) - else: - if not(cable_failure + "\n" + str(cable) in list(G.nodes)): - probabilities.append(probability_dict[cable_failure]) - G.add_node(cable_failure + "\n" + str(cable)) - G = addMoreEdges(nearby_platforms, G, Array, cable_failure, cable, failures_c, failures_p, [platform, anchor, mooring, cable]) - - # Cable buoyancy modules and materials - for cable_section in Array.cableList[cable].dd['cables']: - # Create buoyancy module nodes (one node per cable) - if 'buoyancy_sections' in cable_section.dd: - n_modules = cable_section.dd['buoyancy_sections'][0]['N_modules'] - for buoy_failure in systems['buoyancy']: - if not(buoy_failure + "\n" + str(cable) in list(G.nodes)): - probabilities.append(probability_dict[buoy_failure]) - G.add_node(buoy_failure + "\n" + str(cable)) - G = addMoreEdges(nearby_platforms, G, Array, buoy_failure, cable, failures_c, failures_p, [platform, anchor, mooring, cable]) - - # Create either dynamic or static cable failure node (one of each per cable at a maximum) - if cable_section.dd['cable_type']['dynamic']: - if not(systems['cable_mat'][0] + "\n" + str(cable) in list(G.nodes)): - probabilities.append(probability_dict[systems['cable_mat'][0]]) - G.add_node(systems['cable_mat'][0] + "\n" + str(cable)) - G = addMoreEdges(nearby_platforms, G, Array, systems['cable_mat'][0], cable, failures_c, failures_p, [platform, anchor, mooring, cable]) - else: - if not(systems['cable_mat'][1] + "\n" + str(cable) in list(G.nodes)): - probabilities.append(probability_dict[systems['cable_mat'][1]]) - G.add_node(systems['cable_mat'][1] + "\n" + str(cable)) - G = addMoreEdges(nearby_platforms, G, Array, systems['cable_mat'][1], cable, failures_c, failures_p, [platform, anchor, mooring, cable]) - - # Cable clashing failures - cable_pnt1 = np.array(Array.platformList[Array.cableList[cable].dd['platforms'][0]].r) - if not ('substation' in Array.cableList[cable].dd['platforms'][1].lower()): - cable_pnt2 = np.array(Array.platformList[Array.cableList[cable].dd['platforms'][1]].r) - else: cable_pnt2 = np.array(Array.substationList[Array.cableList[cable].dd['platforms'][1]].r) - mooring_pnt1 = Array.platformList[platform].anchorList[anchor].mooringList[mooring].rA[:2] - mooring_pnt2 = Array.platformList[platform].anchorList[anchor].mooringList[mooring].rB[:2] - cMaxX, cMinX, cMaxY, cMinY = get_min_max_vals(cable_pnt1, cable_pnt2, angle_radians) - mMaxX, mMinX, mMaxY, mMinY = get_min_max_vals(mooring_pnt1, mooring_pnt2, angle_radians) - x_overlap = (cMaxX < mMaxX and cMaxX > mMinX) or (cMinX > mMinX and cMinX < mMaxX) - y_overlap = (cMaxY < mMaxY and cMaxY > mMinY) or (cMinY > mMinY and cMinY < mMaxY) - x_on_top = (cMaxX <= mMaxX and cMaxX >= mMinX) and (cMinX >= mMinX and cMinX <= mMaxX) - y_on_top = (cMaxY <= mMaxY and cMaxY >= mMinY) and (cMinY >= mMinY and cMinY <= mMaxY) - if (x_overlap and y_overlap) or ((cMaxX == mMaxX and cMinX == mMinX)and(cMaxY == mMaxY and cMinY == mMinY)): - for cable_clashing_failure in systems['clashing'][1:]: - if not(cable_clashing_failure + "\n" + str(cable)+str(mooring) in list(G.nodes)): - probabilities.append(probability_dict[cable_clashing_failure]) - G.add_node(cable_clashing_failure + "\n" + str(cable)+str(mooring)) - G = addMoreEdges(nearby_platforms, G, Array, cable_clashing_failure, str(cable)+str(mooring), failures_c, failures_p, [platform, cable, anchor, mooring, str(cable)+str(mooring)]) - cable_clashes.append(str(cable)+str(mooring)) - if len(cable_clashes) < 1: - if not(systems['clashing'][1] + "\n" + str(platform) + "," + str(cable) in list(G.nodes)): - probabilities.append(probability_dict[systems['clashing'][1]]) - G.add_node(systems['clashing'][1] + "\n" + str(platform) + "," + str(cable)) - G = addMoreEdges(nearby_platforms, G, Array, systems['clashing'][1], str(platform) + ',' + str(cable), failures_c, failures_p, [platform, anchor, cable, mooring, str(cable) + ' ' + str(platform)]) - if not(systems['clashing'][2] + "\n" + str(platform) + ',' + str(cable) in list(G.nodes)): - probabilities.append(probability_dict[systems['clashing'][2]]) - G.add_node(systems['clashing'][2] + "\n" + str(platform) + ',' + str(cable)) - G = addMoreEdges(nearby_platforms, G, Array, systems['clashing'][2], str(platform) + ',' + str(cable), failures_c, failures_p, [platform, anchor, cable, mooring, str(cable) + ' ' + str(platform)]) - - # If there are no specific mooring-mooring clashing, create a generic mooring-mooring clashing node for the platform - if len(mooring_clashes) < 1: - fail_list = [platform, anchor] - for a_variable in Array.platformList[platform].mooringList.keys(): - fail_list.append(a_variable) - if not(systems['clashing'][0] + "\n" + str(platform) in list(G.nodes)): - probabilities.append(probability_dict[systems['clashing'][0]]) - G.add_node(systems['clashing'][0] + "\n" + str(platform)) - G = addMoreEdges(nearby_platforms, G, Array, systems['clashing'][0], platform, failures_c, failures_p, fail_list) - - # Shared mooring line failures - for mooring in Array.platformList[platform].mooringList: - if Array.platformList[platform].mooringList[mooring].shared: - for mooring_failure in systems['mooring']: - if not twoTurbine or 'shared line' in mooring_failure.replace('\n', ' ').lower(): - if not(mooring_failure + "\n" + str(mooring) in list(G.nodes)): - probabilities.append(probability_dict[mooring_failure]) - G.add_node(mooring_failure + "\n" + str(mooring)) - G = addMoreEdges(nearby_platforms, G, Array, mooring_failure, mooring, failures_c, failures_p, [platform, mooring]) - - # Create mooring nodes based on specific materials - for section in Array.platformList[platform].mooringList[mooring].dd["sections"]: - for material in systems['moor_mat']: - if ((("ope" in section["type"]["material"]) and ("ire" in material)) or (("oly" in section["type"]["material"]) and ("ynth" in material)) )or (("hain" in section["type"]["material"]) and ("hain" in material)): - if not(material + "\n" + str(mooring) in list(G.nodes)): probabilities.append(probability_dict[material]) - G.add_node(material + "\n" + str(mooring)) - G = addMoreEdges(nearby_platforms, G, Array, material, mooring, failures_c, failures_p, [platform, mooring]) - - # Create mooring clashing nodes - for mooring2 in Array.platformList[platform].anchorList[anchor].mooringList: - if not(mooring == mooring2): - mooring_pnt1 = np.array(Array.platformList[platform].mooringList[mooring].rA[:2]) - mooring_pnt2 = np.array(Array.platformList[platform].mooringList[mooring].rB[:2]) - mooring2_pnt1 = np.array(Array.platformList[platform].mooringList[mooring2].rA[:2]) - mooring2_pnt2 = np.array(Array.platformList[platform].mooringList[mooring2].rB[:2]) - mMaxX, mMinX, mMaxY, mMinY = get_min_max_vals(mooring_pnt1, mooring_pnt2, angle_radians) - cMaxX, cMinX, cMaxY, cMinY = get_min_max_vals(mooring2_pnt1, mooring2_pnt2, angle_radians) - x_overlap = (cMaxX < mMaxX and cMaxX > mMinX) or (cMinX > mMinX and cMinX < mMaxX) - y_overlap = (cMaxY < mMaxY and cMaxY > mMinY) or (cMinY > mMinY and cMinY < mMaxY) - x_on_top = (cMaxX <= mMaxX and cMaxX >= mMinX) and (cMinX >= mMinX and cMinX <= mMaxX) - y_on_top = (cMaxY <= mMaxY and cMaxY >= mMinY) and (cMinY >= mMinY and cMinY <= mMaxY) - if (x_overlap and y_overlap) or (x_on_top or y_on_top): - if not(systems['clashing'][0] in list(G.nodes)): probabilities.append(probability_dict[systems['clashing'][0]]) - G.add_node(systems['clashing'][0] + "\n" + str(mooring) + "," + str(mooring2)) - G = addMoreEdges(nearby_platforms, G, Array, systems['clashing'][0], str(mooring) + "," + str(mooring2), failures_c, failures_p, [platform, mooring, mooring2]) - mooring_clashes.append(str(mooring) + str(mooring2)) - - # Cable clashing failures - for cable in Array.cableList: - if platform in Array.cableList[cable].dd['platforms']: - cable_pnt1 = np.array(Array.platformList[Array.cableList[cable].dd['platforms'][0]].r) - if not ('substation' in Array.cableList[cable].dd['platforms'][1].lower()): - cable_pnt2 = np.array(Array.platformList[Array.cableList[cable].dd['platforms'][1]].r) - else: cable_pnt2 = np.array(Array.substationList[Array.cableList[cable].dd['platforms'][1]].r) - - if not ('substation' in mooring[0].lower()):mooring_pnt1 = np.array(Array.platformList[mooring[0]].r) - else: mooring_pnt1 = np.array(Array.substationList[mooring[0]].r) - if not ('substation' in mooring[1].lower()): mooring_pnt2 = np.array(Array.platformList[mooring[1]].r) - else: mooring_pnt2 = np.array(Array.substationList[mooring[1]].r) - - x_overlap = (cMaxX < mMaxX and cMaxX > mMinX) or (cMinX > mMinX and cMinX < mMaxX) - y_overlap = (cMaxY < mMaxY and cMaxY > mMinY) or (cMinY > mMinY and cMinY < mMaxY) - cMaxX, cMinX, cMaxY, cMinY = get_min_max_vals(cable_pnt1, cable_pnt2, angle_radians) - mMaxX, mMinX, mMaxY, mMinY = get_min_max_vals(mooring_pnt1, mooring_pnt2, angle_radians) - if (x_overlap and y_overlap) or ((cMaxX == mMaxX and cMinX == mMinX)and(cMaxY == mMaxY and cMinY == mMinY)): - for cable_clashing_failure in systems['clashing'][1:]: - if not (systems['clashing'][1] in list(G.nodes)): probabilities.append(probability_dict[systems['clashing'][1]]) - G.add_node(systems['clashing'][1] + "\n" + str(mooring) + "," + str(cable)) - G = addMoreEdges(nearby_platforms, G, Array, systems['clashing'][1], str(mooring) + "," + str(cable), failures_c, failures_p, [platform, cable, mooring, str(mooring) + "," + str(cable)]) - G = addMoreEdges(nearby_platforms, G, Array, systems['clashing'][1], str(platform) + "," + str(cable), failures_c, failures_p, [platform, cable, mooring, str(platform) + "," + str(cable)]) - cable_clashes.append(str(cable)+str(mooring)) - - # Grid/substation failures - for grid_failure in systems['grid']: - if not(grid_failure in list(G.nodes)): - probabilities.append(probability_dict[grid_failure]) - G.add_node(grid_failure + "\n" + "") - G = addMoreEdges(nearby_platforms, G, Array, grid_failure, "", failures_c, failures_p, [platform]) - - - -# If interested in the twoTurbine case study, alter a few of the failure nodes -if twoTurbine: - list_of_failures_to_rid = ['Tether & anchor systems array_cable10', 'Cable protection system array_cable10', - 'Terminations array_cable10', 'Offshore joints array_cable10'] - for failure in list(G.nodes): - if 'connector' in failure.replace('\n', ' ').lower(): - G.remove_node(failure) - if 'buoyancy modules' in failure.replace('\n', ' ').lower(): - G.remove_node(failure) - if failure.replace('\n', ' ') in list_of_failures_to_rid: - G.remove_node(failure) - - - -# Print the number of nodes and (if the user wants) the list of nodes -print('\nNumber of nodes -',len(G.nodes),'\n') -user_input3 = input("Would you like to see the list of failures? ") -if 'y' in user_input3.lower() or 'rue' in user_input3.lower(): - for edge in list(G.nodes): - print(edge.replace("\n", " ")) - -# Print the number of nodes and (if the user wants) the list of edges -print('\nNumber of edges -',len(G.edges),'\n') -user_input4 = input("Would you like to see the list of edges? ") -if 'y' in user_input4.lower() or 'rue' in user_input4.lower(): - itervar = 0 - for edge in list(G.edges): - print(edge) - itervar += 1 - if (itervar + 1) % 1000 == 0: user_input45 = input("Continue? ") - - - -# If the user wants to input probabilities for specific edges, reweight edges based on the user's inputs -user_inputs = input("Would you like to input probabilities into adjacency matrix? ") -twoTurbine_calculationType = False -if (user_inputs == 'y' or user_inputs == 'yes') or user_inputs == 'True': - twoTurbine_calculationType = True - for i in range(len(G.edges)): - edge = list(G.edges)[i] - if ('rift off' in edge[0].replace("\n", " ") or 'ncreased' in edge[0].replace("\n", " ")) or 'ynamics' in edge[0].replace("\n", " "): - G = edgeReweight(G, edge) - elif ('apsize' in edge[0].replace("\n", " ") or '-cable' in edge[0].replace("\n", " ")) or 'ing line non' in edge[0].replace("\n", " "): - G = edgeReweight(G, edge) - elif ('ragging' in edge[0].replace("\n", " ") or 'hain' in edge[0].replace("\n", " ")) or 'ire rope' in edge[0].replace("\n", " "): - G = edgeReweight(G, edge) - elif ('ynthetic' in edge[0].replace("\n", " ") or 'able profile' in edge[0].replace("\n", " ")) or 'ared line' in edge[0].replace("\n", " "): - G = edgeReweight(G, edge) - elif ('load on cable' in edge[0].replace("\n", " ") or 'eight' in edge[0].replace("\n", " ")): - G = edgeReweight(G, edge) - - - -# Ask user if they are ready to continue to Bayesian network calculations (if not, quit) -continue_input = input("Ready to continue? ") -if 'n' in continue_input.lower(): - quit() - - -# Bayesian network calculation -arr = nx.to_numpy_array(G) -nodeNames = np.reshape(np.array(list(G.nodes)), (len(list(G.nodes)), )) - -poc = "child" -filename = "turbineInference_" + poc + "FAModelTwoTurbine" + "_" + str(nba_input) + ".xlsx" -for start_component in range(1,arr.shape[0]+1): # Iterate through each failure mode/effect in turbine - print(start_component) - -with pd.ExcelWriter(filename) as writer: - all_probabilities = np.zeros(arr.shape) # Initialize a large array to put all the pairwise probabilities in - for start_component in range(1,arr.shape[0]+1): # Iterate through each failure mode/effect in turbine - a = arr.copy() - non = nodeNames - K, a, g, e, m, non = breadth_first_multi(a, nodeNames, [start_component], poc) # Generate tree for Bayesian network - prblts = [] # Initialize array of node probabilities (in order of appearance in graph) - for node in non: - node_index = np.where(nodeNames == node)[0][0] - prblts.append(probabilities[node_index]) # Add nodes to array of node probabilities - prblts = np.array(prblts) - - probabilitiy_table = np.zeros((2, a.shape[0])) # Initialize table of inference probabilities - nodes = diagonal_nodes(a) # Diagonal matrix of node names (numerical +1) - a = make_binary(a, 0.5) # Binarize adjacency table - - nodeNamesArray = np.array(list(K.nodes)) # Initialize array of names of nodes - nodeNamesArray = np.reshape(nodeNamesArray, (len(nodeNamesArray),)) # Make numpy array - - # Interence----------------------------------------------------------------------- - for node in range(a.shape[0]): - pts_bool = nodes @ a[:, node] # vector of zeros and child names (numerical names) - pts = pts_bool[np.nonzero(pts_bool)] #list of just the child names (numerical names) - - if len(pts) < 1: # If no parents, add probability of failure happening to the probability table - probabilitiy_table[0][node] = probabilities[np.where(nodeNames == nodeNamesArray[int(node)])[0][0]] - probabilitiy_table[1][node] = 1 - probabilities[np.where(nodeNames == nodeNamesArray[int(node)])[0][0]] - continue + elif 'cable' in attach1_type: + # Create dynamic cable section failure nodes + if 'dynamic' in str(type(component)): + component_type = 'dynamic' + component_name = str(component.id) + + # Create static cable section failure nodes + elif 'static' in str(type(component)): + component_type = 'static' + component_name = str(component.id) + + # Create offshore Joints failure nodes + elif 'joint' in str(type(component)).lower(): + component_type = 'joints' + component_name = attach1_name + ' ' + str(component.id) + + for component_failure in systems[component_type]: + G.add_node(component_failure + "\n" + component_name) + G = addMoreEdges(nearby_platforms, G, Array, component_failure, component_name, failures_c, failures_p, [platform, attach1]) + + + # SECOND ORDER NODES ------------------------------------------------------------------------------------------- + attached_to = attachments[attach1]['obj'].attached_to + for attach1A in attached_to: + attach1A_name = str(attach1A.id) + + # Create anchor failure nodes + if 'anchor' in str(type(attach1A)): + attach1A_type = 'anchor' + if len(attach1A.attachments) > 1: attach1A_type = 'sharedanchor' + for anchor_failure in systems[attach1A_type]: + G.add_node(anchor_failure + "\n" + attach1A_name) + G = addMoreEdges(nearby_platforms, G, Array, anchor_failure, attach1A_name, failures_c, failures_p, [platform, attach1_name, attach1A_name]) + + # Create edges between platforms + elif 'platform' in str(type(attach1A)): + attach1A_type = 'platform' + attach1A_name = attach1A.id + for platform_failure in systems['platform']: + G = addMoreEdges(nearby_platforms, G, Array, platform_failure, platform, failures_c, failures_p, [platform, attach1A_name]) - if twoTurbine_calculationType: - parents, our_table = twoTurbine_bayesian_table(a, arr, node + 1, nodeNames, non) # Calculate the probability distribution table - else: - parents, our_table = bayesian_table(a, node+1, True, nodeNames, True, prblts) - mlt_table = np.ones((our_table.shape[0],2)) # Initialize table for multiplying across rows of probability distribution table - - # Calculate Probability Table ------------------------------------------------------------ - for i in range(our_table.shape[0]): - for j in range(our_table.shape[1] - 2): - parent = int(parents[j]) - if our_table[i,j] == 0: - our_table[i,j] = probabilitiy_table[0][parent - 1] - if probabilitiy_table[0][parent - 1] == 0: - break - else: - our_table[i,j] = probabilitiy_table[1][parent - 1] - if (parent-1 == 0): # If the node's parent is the evidence, zero out the non-failing possibility - our_table[i,j] = 0 - mlt_table[i,0] *= our_table[i,j] # Multiply the probabilities across the probability distribution table - mlt_table[i,1] = mlt_table[i,0] * our_table[i, -1] # Multiple by the probability of event not failing given combination of parent failure - mlt_table[i,0] *= our_table[i, -2] # Multiple by the probability of event failing given combination of parent failure - sm_table = np.sum(mlt_table, axis = 0) #/np.sum(mlt_table) # Sum the products of probabilities across the columns - probabilitiy_table[0][node] = sm_table[0] # Update the inference probability table with the probabilites just calculated - probabilitiy_table[1][node] = sm_table[1] - - # Print and add probability of node to table - print(start_component, node, " --> Probability of ", nodeNamesArray[node].replace("\n", " "), "=", sm_table) - index2 = np.where(nodeNames == nodeNamesArray[node])[0][0] - all_probabilities[0 * arr.shape[0] + start_component - 1][0 * arr.shape[0] + index2] = sm_table[0]/np.sum(sm_table) - - # Write array to dataframe - df3 = pd.DataFrame(all_probabilities) - df3.to_excel(writer, sheet_name="allProbs") - - -# If we want to plot the network, plot it now -if plot: - nx.draw_networkx(G) - plt.show() \ No newline at end of file + # Create substation/grid failure nodes + elif 'substation' in str(type(attach1A)): + attach1A_type = 'substation' + for grid_failure in systems['grid']: + G.add_node(grid_failure + "\n" + attach1A_name) + G = addMoreEdges(nearby_platforms, G, Array, anchor_failure, attach1A_name, failures_c, failures_p, [platform, attach1_name, attach1A_name]) + + if len(mooring_clashes) < 1: + G.add_node(systems['mooringmooring'][0] + "\n" + str(platform)) + G = addMoreEdges(nearby_platforms, G, Array, systems['mooringmooring'][0], str(platform), failures_c, failures_p, [platform]) + + if len(cable_clashes) < 1: + for cable_num in num_cables: + for clashing_failure in systems['cablemooring']: + G.add_node(clashing_failure + "\n" + str(platform) + ' ' + str(cable_num)) + G = addMoreEdges(nearby_platforms, G, Array, clashing_failure, str(platform) + ' ' + str(cable_num), failures_c, failures_p, [platform]) + + +print('\n') +for node in G.nodes: + print(node.replace('\n', ' ')) \ No newline at end of file From e04a1b35f709e5617652c8db3b8363a3dbe7d3f9 Mon Sep 17 00:00:00 2001 From: EmmaSlackGraph Date: Thu, 6 Jun 2024 10:28:42 -0600 Subject: [PATCH 05/24] Update failure files --- failure/failureGraphs.py | 232 ----------------------------------- failure/zzz_failureGraphs.py | 4 +- 2 files changed, 2 insertions(+), 234 deletions(-) delete mode 100644 failure/failureGraphs.py diff --git a/failure/failureGraphs.py b/failure/failureGraphs.py deleted file mode 100644 index 3341d6de..00000000 --- a/failure/failureGraphs.py +++ /dev/null @@ -1,232 +0,0 @@ -import numpy as np -import networkx as nx -import matplotlib.pyplot as plt -import pandas as pd -from failure.failureProbabilities import * -from failure.twoTurbineCaseStudy import * -from famodel.project import Project - -def couldClash(G, failure, a1, a2): - if a1 == a2 or (failure + "\n" + str(a1.id) + str(a2.id) in list(G.nodes) or failure + "\n" + str(a2.id) + str(a1.id) in list(G.nodes)): - return False - a1_pnt1 = np.array(a1.rA[:2]) - a1_pnt2 = np.array(a1.rB[:2]) - a2_pnt1 = np.array(a2.rA[:2]) - a2_pnt2 = np.array(a2.rB[:2]) - # print('\n', a1_pnt1, a1_pnt2) - # print(a2_pnt1, a2_pnt2) - a1MaxX, a1MinX, a1MaxY, a1MinY = get_min_max_vals(a1_pnt1, a1_pnt2, angle_radians) - a2MaxX, a2MinX, a2MaxY, a2MinY = get_min_max_vals(a2_pnt1, a2_pnt2, angle_radians) - - overlap = False - for corner_x in [a1MaxX,a1MinX,]: - for corner_y in [a1MaxY, a1MinY]: - if (corner_x <= a2MaxX and corner_x >= a2MinX) and (corner_y <= a2MaxY and corner_y >= a2MinY): - overlap = True - print("TRUE!!!!! ------------->") - # print(corner_x < a2MaxX, corner_x > a2MinX) - # print([corner_x, corner_y], a2MaxX, a2MinX, a2MaxY, a2MinY) - # print(a1MaxX, a1MinX, a1MaxY, a1MinY, '\n') - return overlap - -def addMoreEdges(nearby_platforms, G, Array, node, node_id, failures_c, failures_p, ids): - for child in failures_c[node]: - for id in ids: - if child + "\n" + str(id) in G.nodes: - G.add_edge(node + "\n" + str(node_id), child + "\n" + str(id), weight=0.001) - for parent in failures_p[node]: - for id in ids: - if parent + "\n" + str(id) in G.nodes: - G.add_edge(parent + "\n" + str(id), node + "\n" + str(node_id), weight=0.001) - return G - -def edgeReweight(G, edge): - new_weight = input("Enter weight for (" + str(edge[0].replace("\n", " ")) + ", " + str(edge[1].replace("\n", " ")) + ") (press \'Enter\' for default value)") - if new_weight=='': - new_weight=0.01 - G[edge[0]][edge[1]]['weight']=float(new_weight) - return G - -def get_y_Value(point1, point2, x): - return ((point2[1]-point1[1])/(point2[0]-point1[0]))*(x-point1[0])+point1[1] - -def get_min_max_vals(pnt2, pnt1, angle_radians): - vector = pnt2 - pnt1 - # print('vect', vector) - length = math.sqrt(vector[0]**2 + vector[1]**2) - if vector[0] == 0.0 and vector[1] > 0: angle = math.pi/2 - elif vector[0] == 0.0 and vector[1] < 0: angle = 3*math.pi/2 - elif vector[0] == 0.0 and vector[1] == 0: angle = 0.0 - else: angle = math.atan(vector[1]/vector[0]) - if angle == 0 and vector[0] < 0: - angle = math.pi - if (angle > -math.pi*0.5 and angle < math.pi*0.5) and vector[0] < 0: - angle += math.pi - # print('angle', angle) - new_vector1 = np.array([length * math.cos(angle - angle_radians), length * math.sin(angle - angle_radians)]) + np.array(pnt1) - new_vector2 = np.array([length * math.cos(angle + angle_radians), length * math.sin(angle + angle_radians)]) + np.array(pnt1) - if angle == math.pi and angle_radians==0: - new_vector1 = np.array([length * math.cos(angle - angle_radians), 0]) + np.array(pnt1) - new_vector2 = np.array([length * math.cos(angle + angle_radians), 0]) + np.array(pnt1) - max_x = max([new_vector1[0], new_vector2[0], pnt1[0], pnt2[0]]) - min_x = min([new_vector1[0], new_vector2[0], pnt1[0], pnt2[0]]) - max_y = max([new_vector1[1], new_vector2[1], pnt1[1], pnt2[1]]) - min_y = min([new_vector1[1], new_vector2[1], pnt1[1], pnt2[1]]) - if max_x == min_x: - if max_x < 0: max_x = 0 - elif min_x > 0: min_x = 0 - return max_x, min_x, max_y, min_y - -# Create project class instance from yaml file -Array = Project(file='famodel/OntologySample600m.yaml') - -# Create adjacency matrix from failure matrix -df = pd.read_excel("/Users/eslack/Documents/Code/ExcelFiles/failureData.xlsx", sheet_name="bMMatch") -arr = df.to_numpy()[:,1:] -nodeNames = df.to_numpy()[:, 0].flatten() - -# Get initial failure probabilities for each failure mode and effect -probabilities, array_of_probs = getProbabilities("failureProbabilities.xlsx", "Sheet3") - - -# Determine angle of clashing we are interested in -# angle_degree = float(input("What angle do you want to use? (in degrees) ")) -# angle_radians = angle_degree/360 * math.pi * 2 -angle_radians = 30/360 * math.pi * 2 - -# Initialize and create the dictionaries of the children, parents, and probabilities for each failure -failures_c = {} -failures_p = {} -probability_dict = {} - -for i in range(arr.shape[0]): - node_children = [] - node_parents = [] - for j in range(arr.shape[1]): - if arr[i,j] > 0: - node_children.append(nodeNames[j]) - if arr[j,i] > 0: - node_parents.append(nodeNames[j]) - failures_c.update({nodeNames[i]: node_children}) - failures_p.update({nodeNames[i]: node_parents}) - probability_dict.update({nodeNames[i]: probabilities[i]}) - -# Create dictionary for each subsystem of failures -turbine = [0,1,2,3,4,5,26,27,28,29] -platform = [6,7,8,9,10,11,30,31,32] -mooringmooring = [12] -mooringcable = [13,14] -mooring_materials = [33,34,35] -mooring = [15,16,17] -connector = [36] -clump_weight = [37] -anchor = [19,20,38] -cable = [21,22,23,41,42, 45,46] -cable_mat = [43,44] -grid = [24,25] -buoyancy = [40] -sharedmooring = [15,16,18] -sharedanchor = [19,20,39] -systems = {'turbine':nodeNames[turbine], 'platform':nodeNames[platform], 'mooringmooring':nodeNames[mooringmooring], - 'moor_mat':nodeNames[mooring_materials], 'mooringcable':nodeNames[mooringcable], 'cablemooring':nodeNames[mooringcable], - 'mooring':nodeNames[mooring], 'connector':nodeNames[connector], 'weight':nodeNames[clump_weight], - 'anchor':nodeNames[anchor], 'cable':nodeNames[cable], 'grid':nodeNames[grid], 'cable_mat':nodeNames[cable_mat], - 'buoyancy':nodeNames[buoyancy], 'cablecable': [], 'sharedmooring':nodeNames[sharedmooring], - 'sharedmooringcable':nodeNames[mooringcable], 'cablesharedmooring':nodeNames[mooringcable], 'sharedmooringmooring':nodeNames[mooringmooring], - 'mooringsharedmooring':nodeNames[mooringmooring], 'sharedanchor': nodeNames[sharedanchor]} - - -# Initialize graph, boolean for plotting, and list of probabilities -G = nx.DiGraph() -plot = False - -# FIRST DEGREE NODES ------------------------------------------------------------------------------------------- -for platform in Array.platformList: - # print(platform, Array.platformList[platform].r) - attachments = Array.platformList[platform].attachments - nearby_platforms = [] - mooring_clashes = [] - cable_clashes = [] - - # Create platform failure nodes - for platform_failure in systems['platform']: - G.add_node(platform_failure + "\n" + str(platform)) - G = addMoreEdges(nearby_platforms, G, Array, platform_failure, platform, failures_c, failures_p, [platform]) - - # Create failure nodes - for turbine_failure in systems['turbine']: - G.add_node(turbine_failure + "\n" + str(platform)) - G = addMoreEdges(nearby_platforms, G, Array, turbine_failure, platform, failures_c, failures_p, [platform]) - - # FIRST DEGREE EDGES ------------------------------------------------------------------------------------------- - for attach1 in attachments.keys(): - attach1_name = str(attachments[attach1]['id']) - attach1_type = '' - if 'mooring' in str(type(attachments[attach1]['obj'])): - if attachments[attach1]['obj'].shared: attach1_type = 'sharedmooring' - else: attach1_type = 'mooring' - elif 'cable' in str(type(attachments[attach1]['obj'])): attach1_type = 'cable' - - subcomponents = attachments[attach1]['obj'].subcomponents - print('subcomponents') - for component in subcomponents: - print(component) - if 'mooring' in attach1_type: - print(component.keys()) - - # Create moroing/cable failure nodes - for attach1_failure in systems[attach1_type]: - G.add_node(attach1_failure + "\n" + attach1_name) - G = addMoreEdges(nearby_platforms, G, Array, attach1_failure, attach1_name, failures_c, failures_p, [platform, attach1_name]) - - # Create clashing failure nodes - for attach2 in attachments.keys(): - attach2_name = str(attachments[attach2]['id']) - attach2_type = '' - clash_name = str(attach1_name)+str(attach2_name) - if 'mooring' in str(type(attachments[attach2]['obj'])): attach2_type = 'mooring' - elif 'cable' in str(type(attachments[attach2]['obj'])): attach2_type = 'cable' - for clash_failure in systems[(str(attach1_type)+str(attach2_type))]: - # print('could clash --', couldClash(G, clash_failure, attachments[attach1]['obj'], attachments[attach2]['obj']), '--', attach1_name, attach2_name) - if couldClash(G, clash_failure, attachments[attach1]['obj'], attachments[attach2]['obj']): - G.add_node(clash_failure + "\n" + clash_name) - G = addMoreEdges([attach1_name, attach2_name], G, Array, clash_failure, clash_name, failures_c, failures_p, [platform, attach1_name, attach2_name, clash_name]) - - if attach1_type == 'mooring' and attach2_type == attach1_type: mooring_clashes.append(clash_failure + "\n" + clash_name) - else: cable_clashes.append(clash_failure + "\n" + clash_name) - - # SECOND ORDER NODES ------------------------------------------------------------------------------------------- - attached_to = attachments[attach1]['obj'].attached_to - for attach1A in attached_to: - attach1A_name = str(attach1A.id) - - # Create anchor failure nodes - if 'anchor' in str(type(attach1A)): - attach1A_type = 'anchor' - if len(attach1A.attachments) > 1: attach1A_type = 'sharedanchor' - for anchor_failure in systems[attach1A_type]: - G.add_node(anchor_failure + "\n" + attach1A_name) - G = addMoreEdges(nearby_platforms, G, Array, anchor_failure, attach1A_name, failures_c, failures_p, [platform, attach1_name, attach1A_name]) - - # Create edges between platforms - elif 'platform' in str(type(attach1A)): - attach1A_type = 'platform' - attach1A_name = attach1A.id - for platform_failure in systems['platform']: - G = addMoreEdges(nearby_platforms, G, Array, platform_failure, platform, failures_c, failures_p, [platform, attach1A_name]) - - # Create substation/grid failure nodes - elif 'substation' in str(type(attach1A)): - attach1A_type = 'substation' - for grid_failure in systems['grid']: - G.add_node(grid_failure + "\n" + attach1A_name) - G = addMoreEdges(nearby_platforms, G, Array, anchor_failure, attach1A_name, failures_c, failures_p, [platform, attach1_name, attach1A_name]) - - if len(mooring_clashes) < 1: - G.add_node(systems['mooringmooring'] + "\n" + str(platform)) - G = addMoreEdges(nearby_platforms, G, Array, systems['mooringmooring'], str(platform), failures_c, failures_p, [platform]) - - -# print('\n') -# for node in G.nodes: -# print(node.replace('\n', ' ')) \ No newline at end of file diff --git a/failure/zzz_failureGraphs.py b/failure/zzz_failureGraphs.py index d31fdf92..15e0c58b 100644 --- a/failure/zzz_failureGraphs.py +++ b/failure/zzz_failureGraphs.py @@ -2,8 +2,8 @@ import networkx as nx import matplotlib.pyplot as plt import pandas as pd -from failure.failureProbabilities import * -from failure.twoTurbineCaseStudy import * +from failureProbabilities import * +from twoTurbineCaseStudy import * from famodel.project import Project From e21ee6898fe3e45736b263a732418176ae536522 Mon Sep 17 00:00:00 2001 From: EmmaSlackGraph Date: Thu, 6 Jun 2024 10:33:22 -0600 Subject: [PATCH 06/24] Remove files after putting into failure folder --- failureData.xlsx | Bin 193244 -> 0 bytes failureProbabilities.py | 741 -------------------------------------- failureProbabilities.xlsx | Bin 73055 -> 0 bytes graphBuilder.py | 488 ------------------------- searchMethods.py | 429 ---------------------- twoTurbineCaseStudy.py | 76 ---- 6 files changed, 1734 deletions(-) delete mode 100644 failureData.xlsx delete mode 100644 failureProbabilities.py delete mode 100644 failureProbabilities.xlsx delete mode 100644 graphBuilder.py delete mode 100644 searchMethods.py delete mode 100644 twoTurbineCaseStudy.py diff --git a/failureData.xlsx b/failureData.xlsx deleted file mode 100644 index 8d71b22ddaf613cdf7bf60ee74e16c8b1a0aac8b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 193244 zcmeFZXFwC}wl)lcA_CGxl&Vyb-a#oMASlw64x#rRdKIJy2nZ;>NC)Y?hR};t0SUbm zssKW)Jkm z&)H6L-Raz6{zXkUrx0RMCsPe@SHqDfWoo*x^Yd#@lH;p zHeURYWwEPUX6qSJ`QsV6|N%3=; zzBA%u(>AUE=5YqVKcu(u#72!8Voi8iYL30#J{d^q*vNxZCu`a{acODd_qB{rmD;Y7E{ zMS^K*GOV&1;x;8J&L;ItVdIV33nRMQvW68+P;H1lBQ;b?dP1rBEgxfseX%?CgSYzZ zEPMo(Vm%*=vX_S7L$XzI!mW=&P4!V@M4Jlz`bkW!wyI-u)D&t>x>LrQ`MW)N#}ri! z4=~d@xAUJl>Aabw(xp>i@jo&q<|%vE%e8lMOJ4V}Rktz6o17r}n^^7O51HHi=>`?y zVpvp0r#)w*B_W{FI&#)dv3O*&cY@~`&qhqpEemdM=UDM;PXxck9Q`=Bg@}xzb}zbL zt5`;8D!|p(xA}T{bEsCbqhYU7Julq*bi}Ve-lKoMV{R_hxe>LuB~AJK>>#4cSYL)o zOePkqjm_8KE5b_G7wfNRsm8pk1;{(bms=PZgaF?FTK*#;3)QA^qZ{*yy-y>RJfwVmOTxUu1Yir*^a|##aIR=_5n{d3B#d(|p^aAx=sosyUO+2BGd;_36*xhrPTf4?+gqr+W6&aoo5*k3TIq&7O?@!RZno%Z`= z3`zN<8Uot;mhY>QzGgEf)3r(Dd;{S-HJQG8;W!;@cwK6{|NYlZj~seNzk3K3A~hq4 z)||V|ZSO(iy>$aF8hp+6CND2-S?HBCnmKpLUL>Eo^_P@A`W*X(_bqCm#XV)0B$vDv*PHG=tV!d^Ed;xoi;rZ!owWbB5uy1GGjXnO_VB8O~tYu1{ z0BlTxa>)z5@Aw+7&fmS9j+1MH*5#+A@N&|#rBkV`W<|>R2Ic&_6S2EPKhn96J%RVpY-#t$^112SppR^@NnAs^aW6=NF7Eqw?3iXn zN`9=tdTK7l?+K?LpASBN z#K#w~_$1d;cxuwCDdpEZctCLc&0&UBt2W#_|Y4-Mpf&31+23NhR53g^q9>-M4 z$A-+ueR1k#z4$K$=iXAVe2sdp&Qo&duKi1`kL{4C+QTui9&q$6TN-#21-8LE6I7Dc zb^Krj@e{-F&-E14lA8f@bv-mJb+oew5j>ThnDFJI@^OKJ*dmLITMWBkr39a^j$Ia2 zEEWnc1ssX>+TK^gz8Iu!-HBsyVi$HkTz>e7o<+iCF*M9JC`f`a-`YrwTSS}Ju=mGS zzL}-!)HRifsQXW2ghH`o)Z)D|&rn^SMVs0_D0PVpHZD@u8w7Zl`fM+0^l!7LFQwgf z>(a+EqqukXStaRbbAf$)_M1A|;EZ-hrboAonB6g1X9mTQY6%-D0xcPV5v)lzHAgG) zR4&2&UQma;V)dC3&kiQEq|EGM2-TK;9pCalhuuA7i>jg{>Kw z)5XSG>JM9h1F*Lebt5wc?gZ_y9*S#hN!yZKM^Fn$X~|c)+GJO&aDMw4`i^&xWBz6` zeg(8?b=xo@);O});34LX~ z%tq|)*3G-LnwA*52-OPh4`OB;4IU9aBs(sBVvtbgzse@9VY5WtyJ%Jxw@g&7_2PzH z-J72pgCiodkZ%ty*p|BOKi2VZ`XvY;2UaS4$XYe z7hhS;KZwLSeAj&VQ>o*)`_oCY)Sc^c3`e{?uY{gPfPipRD;peUs zGt*OLhaPF-&dpcGo;_lVU-xUhmOI#tDDU5ZEBIL7${G*k&=K2vV=HO7h>YFqD#G`+ zJ~HYI`~36aTeaJbi&m))NGer#@k45Xf^EL2Tf@syOPyts|zT0p(-I8(q2j?$aayJTQ`{2D4 z$^C+}U@b=6@gix0iw^$egZ+%U*bxfnZw0B#S z9ciJ)tgmLc4HaHufKI?aISCGaxW*x#SPNc%pQbOBW{{(vwp3{Nt))zgu;Mm~NRgG) zYIj-V1Vdycki%}E>{e1rXD*79GJ=!aI*Ize%bgSysi9SX+Yk{}d9`zZkXv0XPFt$K z8p4aUt&+aY$zXOqRrD6-~gz*-aV@jEDmajC+4?ZNTOxCQe{Z^p5|k=}!JU6_LaR zT&b`gQnSr5>tAys4=gMf&hu11Ed5nv6r|tLIqIUIdpPs7nJMY9;Ez}DxVh#~k)mxU z3t!15{G5kJg-zN%k>mxk7()iuBX@dRs2K+k_4D6HKaJ1}opK>h3>pPpmL6j$eo|^k zR+|;nBob2ggWH-@SwyP4)Yf3awOT9M2(OOghd;dY2eeeDGOnea-?>PhB4ic{zeY@f8Mpu77$SwU04J7UM2>%ME%1)ISR$94~`(ScygRR1g0 zSCa#p_bV)*p3i59qHf>$P#uKZWq`x0bgKuNQGMZa%<~JI-@l@J<>ICN#NiaVoZ56t zUtU`IE}4M9kuxUa++)NIH?NyT2}i$XZerI|wmT$UyQ%H8d(Kgut7^(Xcu z1-CtolW%KM1Y3MoqN^UD^d9yg*uiqBseh*PV4ou78w^u|`2^^AQGe9B&ofgIOD|#r z7u2~oFs)Zxf^EmIONBbBK1)CsRaWA7UX+_&5k&393l?JHT z<^IeLxc|1L9tg(&!?Rz}b*k~Qlw(4&Cvr1dQe*$Tkwduky}OSltR38ck?DR!WB>fj zEmA1k?mYJp_T8Megef;M4R)5C*Xc=j>!eAq3ACfyD#od}nHXx>i15lOSxH&N`?Zv^ zng|?DD}|GX-?jAFZ90xf<=hcDKJa!QgguMwnp%CuKa1dRg?Sv(RA}o(AD1+%@8E(F zy$8tcm6aV{=&K#Mb6q(3x_8KNVFOay1GFf*ZtD}(Gz}evAsrE62ZzP&p5JHADIlol z;wO==KH;;zjGSb)J|VMqOQH$3K7q4wZiCrLps&Y_v#RTG*y_f?sbb6dsm{-nxe@eE zvGx445!K|oYXzLWVDZ^c%SRs8oodbYj`Wud{uH|e^Fzhd&rTHE(JvoF@6IpYc$tZh z+ltQ=njVhWAc7~~Sc{KtnWdfIKN5hw7RN=Mu8BH49lhxs>G4c7qcacJGBhzAp2s|T ztxBp$X;7Q_z+cXC^hTBRFD1`W%)`(p8Be;bHBTKAmiZyV(h?&p8^SU^{5U-h(Nr=$ z4zX0hJr0RfT|Ew|R5;%qGN@#}Yv#=VTo;m$SXvfRjKEs)mny3Nh0I4&R7uzQB!6inqs<%+RImn9SH^;F!$R<=~ji+~wz({HQC+F_vZPc^?&X zm%Ag?BXS1IpMhJGTbm6-Z?>EtqQXz6blP;+$Qp{nLIyKWhx zm7ZFouy#FeG>^bCY%={Q4{L+WS;{$LqMQ$+aqi0e6#l_|APU+;#gMkjfE1DGSrwKO z8#LH=SBMz|CwrwLmU*vyFin*|e?gtLBP{Q8NX48ctZ0inTI1vWDgQ0)=|YqkAK96@ zR(s3TnD81#!#IHGd_bnqlp0g{-jovzJbbiiRoAts*Nqh8ml?=5~ zNgn+-a9?aTr+6jwaA#_3)K8XLWB86WKhAfkMk4770+=;*>@4ok{q~nZ@HekiqZLFl zYnre|M9d1@9GRq1z5DG0G(}kLk|~+BtHn{GTy3U%JI?3g?TU_$3OI{A*_VFf93@yUDSlSk=LKXA^-e?$791r}@yuk>B znwWRB+u;)xbC1JkDzhGkc&gkUhd-vGV*c)sm27gWfrz*34}x9~(QO^@h*=U69*9|bCamyzNmh8^^OBOV9GMHf>Fv);Qo<3R@p!5>Bo*hx zhd(sEIZNqIa`w;;ttFdK7Q!M%q#~QWj3W|A;h%+#1`-teu7i7YxYGm&cLK?O_7Q-4 zr@4>&un=F|jjNH0ePQ8V({po8euglo_ z;YEDt6DEGbtgF|PVzJY9Xb;P1)Yt9{IH|2pI1_`#&ra;L>Z;1q15!-pFdaW)yU&bknwvw+3e)qY##U%_>QyJXZn5I8)HbJ@Ks~svOCmCT) zoSZF+@I!V}@iO0u9y3=eemcN!}iL9n^rbt7uBsixnZI<~<+OL_lUy*N8AqHP+>OKq(4@4|d$}Nkk z)5b^kaf4fvdCdBR5g~Q@90{y&1RM$3%}A^>uuXUhPj%OC;)(1VYiyrC*)l+oWG-d+ zywn(PY(JD?KlM5j;THWFQ_4Ck40Y@Tm8#_9HwOr1`;Z(>$(yqKIZM z6Pkn~n~*)Hg{KR2`eW5@zQo*C+B%%iO>N{p=Hh`I;WfE{C#DuJq(=*F_Le}cclE$~ zmvRHGHDNkXhW-+$G{LqL@@WwzOmb;MaVK-6KroE+vhh?WYnE+M*4j9%)Zrm*?dk8a zC0y=xIo=#DIc7y?$6441%_SfJJXSh7oQ9^fx7rTr%#ZdsbvQFj!^1YE$G1GJiV1zB z%NW?LbuBxht4rPYjI{QTTrcXKMfsL4`3AM;P_ug-XZneNS9HJ&R5kz@=idiYB=-^e z2beK5m`yYoU9jkR@!o~(iK%i?Z6N!&RZkK$?q)JS*CmJ4a-m;Hy4wPs?NLzFF|l8T z;76Z}dUsJi3UTj%&*H(Si-Vn<(i!dxe6|1*_uog-FZa6t49#`RslOS1pp&h7Za7oZOO}I_>?5ePjrC#JQfEa{n|}O&>LQP=pJVAY*!_p~&q3pb#P9Ac0G@ zZ?%ac_da}@=<;;DU^i`X9%i>#FyAOwKM7LyA|n<$%Ns;0Uw;}hG|}Z9>h8HfXMYru z))@IA*&Ch z!v(u#i}O!*8~ct9Px+8G!H=!d&+tlhA_g~6WrgokRYZn?h(Nj%tJsUvhCA)ZCZvb9is)crc@x^wP#^EA z!y&2*Ua##26rp_+yYyla-QbTYI49cW&>1} z)b!Qg)CpkClHzICeYm#-B3~rQ94E;TCduStEAUF5=jA-u$W)~{{J|1?y7pPY+v!%m z*b|etv?+82kqSLOWpXh#Gvy-QYENrD?aGk&w!Yj0w2&FLUL``zf| zFftweLv#u)_LlCjRG>%0zE0TjxYH8zpUE@9!b&H$;^W83Is8rK!;XoSD{69mF1}en zMEJ^3*{3kV0>#*)#4#PZ#8~QV(Dna7Kvx5xxL1i179dQ_&CCOr>Q0tTAms+rnJHlR zhhzy8^D~#jrN*Z}3p5UsaQMTehLb-e;#E}JY7zFYL;Jv|0wOMF=WkI)EU^@q^6#iS ziaU%>|2jebszrs77@huIGOOC-o9v@_HnkcfyAzxNTE46(zN#N%Rkc2^C|%hB|8Hs; zz6QC)S2@Ex%wMBcykDa(ReDiEp>V11_Ncbvi)7tF3@Y`91gv^>^*yT={>S99%RU& zEFac|upt)s51K8DkUg)ztE85p2neX1{Oq$b%YBJ=VPRQl1pEL3@$B%D%5gxtEdUKo zgoqz(!sIv8Th8O$;j@=uPv4Ub-{lPzhS>-E`T+T=f4_hOi5dU*<=e>N!>^WK&3FS5 z2yo8^fFjjzYvlmBnKUy7c!Rg$YRlJ9)NyDqHfDo{aGY>>g(#X%w9eX8>iLv*3|4#E9ApF*V+clc(f!rG-F+%>Cdqzmk9V6ti zd@t(kSDh3;JI(hR>G4XpM`^!5xzeALpGeep$6s7i#$UlB=dJ)f*cZEsbgiCL|5YGL zHAhHBa$B$bACm6104!bJDpBHM!&m8S#oO?($pYzHBN~^N(?O43edIFuFiO}|Azl^! zQP@n3o4k(^+~GAV0wvdb!zrNN!U6TeA*AK+c2YF1w{ zF!6HVw&j&D&-?@&&fidQrOYSAcN=ad@&h@cT+yHfNVA0~-yIam?T$0wD)jV{dsogU z?D!9+5B+b`GX^aNhQ&{O0ZNm`wpi_-{c=tUz6OsOxy3{ z|7NxKrjfn1xI=aCcSWz5F=z1~Mb9cEALy1%$Ew_D(Fy)oueLi8NM|u%*(zbW*!}gf z@uB22rZzvT^k@@9Q7VwsP~Ux&sb?{A0JPox8 zTV1R>{oRiVfHM5IHA}Y_+Jw{rD>fOcZr5o9h;5|}G@ zBzUici+l&gKd6(*QSHVxd-sR76dVijdo-($z4!6YIFl1s6f@pf3Jm+K2_t#Gcg1Nd zpzpH;hEY~GJ)koi3^<(GjM~hlv%u6;#TUsLb|FXdP+-?uf z+yCe5>#BC+#y@f#on{beboZev_qy|{`z=Oyzc8TR5CDXo=Za;{?A-U!EVB~jyN@~* zyAM76NP&tZTBQIK<~>02hP#BFzf1CeSUft%iqScS9tA3?t{gsqLbHNWyK(TdKvP-A z)fmu|jc#?uf}q=4ganR1FEzM&;TVHZ|vwP!B!yO;7-AwS&ys4lzj z?W4E>=kfvlXTk-E@<(9FFDpQFfXe+-UFG{*)cJs@hXV=r)vQ=qH_U9whS-HYHT-w` zJs^OTf0h*=A`TwF%YDF<+~`<;ou~p>mN(%I=$JvpnU=*gd#K0Hy zpbStRzw~^5bK6D7xA6}Cx=o+;7SF>VypN6H;s@3`76x}%ImlqzpSYsARo}>&7zuz> zFuS~K-C4}zoC>Wc%UoP(=GX(@&~lKm`C5Ey9aL~MW<35b)k~B~Mkc^vwArV?IP-FV zp6s>Fme2r(*s5`z@-Ha=Sx@;2JoQYOOpRdHU!C(QaifM+irWu!i&O0=_FsqiIW&x~ zd1Q_- zW*w%7krD46WF`4te}`#2*^jZS!qEHTC9bX=(V^FqpFWuqql2Emkd*D_!BW`avUsKI zPkj9Gzyvj?f*6S`I9fT)?yvCrnl)J+%{$FVrp+xgWwW2ou2N9Ukq_W~Ir8eB+Ke%s zJhY<+ec6wZ=E}kJ%zZrn;~Fm9>Omdjyj&|t>a`ClJh3qk&zO4J1G+4LwI53}` zsBFB(aADPWHuI|>V>4i@KmdoLBuMXjOi|SvP}qH=`ZI#7sQ&X;oYjA-tZ&g`V0e*X z{=2)!0_eNPZ_sy-y-P-YOV0;VN-+Zq$Bi?$<=!5ad%JwZRpZL323!5~-MQyjkns(; zcWl!q3?6U`>RHm`Gw=NjFYkAlKfPBL`#rj*fXer*-oEpmh2&&3U%82}7*{xL?#`UN zi%OAmf4oHV&;}K(( zbIb0N%O=m^Fx2aoN2IdEsKuM8m?CgwCgqGdIAn4FJB8c?n&s&ku#yxgP*`e@ZbM< z4-#_td_#ddf}WZEMLy#Eb02(9#E0WOg6y>ubB4M_j*g|mh|kowY15K z!2n+%6Bf6vd7IJ%2D3=i{E->v7ulnWTFOUDqw>+?imk*ioHE1jAZlGMbM(3O^ z6_SyM_hm6jS@&I<$BpWR1mqd+m)j$Y3lV{%G2sw8g>PBZPg&r?!@1%m|OI zr^t~V)S>v?#bs}BFyY+A-W>FDl@JB>6-6zzxk4|Hy}@0*sPmmRL<#8fa0iv-bFtJ5 zL)D(P9Y@lN`JOsjl*_@gkBvz5qIXAvcN+HzOU^bCZQ|nI=bMP!NRUdu)ToF!p;Znn zG0iU4{n9~$hm=vAKn}hX-PL9A@*z#%(nzUkl%*|gztPdVILnkR;FJh|EB*KA%MX=zam0sov`$(Qy%SI`%C*z30CzOQQHpoIJ-T^6$O!W9({1NPpb zwEOCh`&Z}9OqMJx#Bes3OWr09wmd=NMZv*JF;Egx!8^|)w z5S0ywp0&;yaKaM1g6*IS<>a=}^DD4rP#UigqY%9ivj&k4kq!wPZt=}xTs3S#Y%70l ze}We37SR^07W$Uhc@_zaQuPfH(yg0`4}Xbr+Sr(TNWv&5XNQi1It87Y(hmtBcIG~k z$CQ(EL#QAueId*sdVL|RAW40p>p@2PKdA7pP5Zh-#G5%gG~bDva-TaE;a(Gwpq|Gz z!(PS4^T+d-^Ji=!YvFGRoe$ENx0fq@SW92a?DoKoVVp#SF%K*G$sqPqe^Xh{K^)8o zh-#L(r{o+Z#oQ1wXiV^+DIG~LV`uIwiK4vyZUOsV#JdIjd$;`-?%a#;lLm9$wD* zi8XF2$tb2mikUyP1aB5(^*u({EQ?AdBz=b<+-}WFa+A_yZsn#%hN2RVP|N)m;THdS|M_VBm-f>3 z@}+dO4>d@qNvv;f-`vKfxk-atjjf2gfss4F+Y<5h{_re9;+@gX9hU#h-zd6+OV(XE z2b%ebIefiheu;O?fFI{S&L`_j*h|>IEM=&rt7Xz4o+c{9wZ#>{cEvu%Cd7`!*7ncE zc7CV2nxHQ;gm1Bfe9&Cx8hXdZZp~A2iSo(Z&}q=DARjdSjNq5unr|B>S29K$F;_BX z+e5D8Yi-=6=C^NPZl5X6($2>j67KrWo)h!V!S?^}#(PeA|0>?A>hlw}NpV*4MDu_* znSYPy)bpe6Oyk6Sl9QfdH|@7l=%lA8PiKctf(8UDo6?U7`t8M=j<`?nVapg z@y(j;Z{p`R+vBq(zL$7+_VeHj>s%!n$E})de?^NF+s*$;Gw#660o=RTErpJ<*?d^%=+?i=FfpvyOCR>p%i z&u@}c>-g(b7Jg2CP9c?2_0Q{HXg!>HP!wewB_Qi6dn`*R8!4-um7BVu-!;oSd^?|( zv;OrbT7uEN3jqR&dhqdQZo&XshSuOt`URX1rAfi9g9HYGk%4LCt+q7yOFdjP1WOZK zG`E-5cSoL`5H2CPXo&EbjpI+^B*8T}+DH+rCx)wbcjg8zMNC&C^k|Bul^2;Q7{q>P zLd&;O=-EGUeBy|Tl~tF8$+BiqX7LZR58qh7y-u@^xlU{mb}%~fV6R8rOs7tABScZ| zOj)I!v{SWHgedjoq{5v+WUmYWL`vJ>&UR(uv|X8e=uxyO@^KZ|lM|xxs9NtRo^K}9 zv{(#wZ9UuR#Y*W+I^dJ_dh$lFZB}^r+WNJ15{nS0K&SA^=k*d=^fPoepSC}3N6~zu ziK>=W6eS>`CG=bTPOYNoC5H1ozN;l?x*uyHB$6;(1`XlS)x2G3lk3WqK|>HikD|?I z=M>^=G)$K(_LFC%;?yRH8YW-Rl3Ci|?*6l`VqA3DFC1( z9(V|Pex|FLa$CjE@EY7X&UvAwNz83KOGbi&L33SQwyC$Fk&!bZv_9%M4Q;sh0l%D} zv1r)FpCB7vvF7@~Q;|4fF68O@Nh-)2Zm z$)5~?{=pDL3BVAMJqLgwS99f~r^UN6d(bAqmAQ^K2?6sqI%`PXud)USF<4wRGvqud zm(U!T&B~Qn9Kme4e>4zvSC(8hPF609HOnfCY?xq}V*Regd#7Nh$b)wWA1b9iHh>7a zy}7Ht{mEkyVWHV6avKPbek`|Q0J9j!051@n84-^etdEbvTsb7l4Km0u{*|TOZo_=`PzPeQPk@=%L z&G}~Xt{Fn=iU@-MBp1XRw25YlG1^2k#R+X*F(sIkNA|p{VPM*);HdYw#ryoc&jyrL zvgi4&WG`yq({FerU%_ijl>XPJQy9-)f_CR0dUiSU+6x}bJs99CO3E>yx&Rsqm=Phr zB$FTSk68c>0|%6s{J_D;0%)||%=SLw-H9vz^)NzGR55RjPp;{?4K32={LLNj1)9{l zU!I5jV#B{DX@k>$W-qwct<~+jTPHwOXapgx@Nc-oSC}@fE_|13Z|4bkla^-xS5W^i z{!6eAyqfb?@!tBIiOL;7(>zwkwIAp{(4}LKD*jX)r6wyVYn7#)MKDY~EIN!eOh0^g zJs%*{Qi3*Gb%>>{=0mI|H)S%IwH3(y{E1sb4A#+I+H%hg}M6vs{jBxva~4j?+FXUC32(h*BS-w z;B*e-dCiv-Z!YO4l22r`{!g62FLr62dK1tRaBuuo#Uo!W<4Of`_v%=t%}qdL5820V zL<7`cpcL@d5CdgK*UXl7B%!1QWGi#Lx7TL_l>$-`e)o4p3>@}{i1o2A;V!W(-8cHD z>92I+ssY%w`U6y(+mmyOw83c}IOZO7`o^EsjD+!m-ZwlM6!$tkfvqaiLcJ~zRvPAf zk-O2M84XXItgR=HZyfLCyGB5MOX2JORCsvoUI6#td(hGnnj^rHKe_`2*ujAYEod!h zbOg{&03%vh_W+n%0J3(%`rr^>Si)476WnSyZt5KDe3~&YCPqz}bOe!DRNbNS{4I(9 zNADP=0wRAtAwmpD8^9DoKs-1GjL;%>?@Gin(I(ZExr8#pg^WXGJKk`qe6vE`0 zvr1$KWq}%a(9^rG{tC&TE$h3KOHkyQeuy_#LSU- zD{8&W>`+T&;fg{pjjlR562|hV1j=>W8vcXv*Xob#!&!@o9rs8MU0Sz(L4u9bW}7B? zB^ZtmLYx9PaPS$?fZ?Pf#PNXxXCEWrpa~&+>8(w_AbXT>9 zzrNfpL8wuK8%p=u(T62(rA{wvX#Y89`KV+5%9mJ}R&izH567aFqF74yoytbozC^DK zdySTu1@djL8M~{N0Qh1GS^A*<-myS0y&1)MrRLXT1xjgkB>>yC3e9-|ye(6|IX153 z>k$>rj(6P#u8nBUKf$v$^V?zLN)1mD{8-$eKqZz<8r;OPy`xChb7}p>^1vicGC_Ag zyydm}V9a^jTDTQqP^5YKN_MOqdr4*Em}B-zc&rt>UDdR?d+rJ(mQYK~vhL^p%ia<# zF{`?f@?7FtS-Z0#<~LP}Y#^AyGCNk?BdRL4K<`q_bD2^XWaVD4@}l=C`%abD(7tkSQLLht&(D3$-jrC;#$vUw zS$>{W>;eTWNX?{Bqm#=G(~4#r8at~XG`yWVVu)b{IbP?!AQuu`6K=Uh31Y(oZLMf7~2%5kV7B4tx;-N1b z$pyJgT6sa7vx(J?5VjUSj31t{inxvdPt=|s3jx?ga$Za z)(!I~@*I1}w32`liLwE+1LQY%PgrKlf5l#MKxf6St~G03H@2_V8y@SbHEV_Zx{?{I z&7M|y{%xfq_9?rk8)Jd=!k@sJ&F>2Jx5-hAw8?EkCiXWAXKj`?IUx%3g1(5nO{^~D zVO%kx$SMp}FJ_+t@?Ae|cJ;Z)-s~En41(zfJEaykG~1Ox_X*WsGfFtUbl(u&Kb89q z*3~t4|0`-S2!~Cl6whvMNP<%4o{DUkX#FEB-J-iq?ouMSY6Quq6<@`66NBR_bAsc> zuo{rwp1QR621**8y!C~lb|srwBgijt#XB}}!PfIn2{|DHNr-aR_GUMql`kc{_$3Vk zNg}#}d~-qSm-p%N8E%%O9XQ8*Hf#FaxE<}7UH~}EvTk($RqsFeN~_Vi0n-)DAESNCxpf~ zFAUuWh_!kCT@JQ$4I-#V<6<{J85pPMyVYpmHskEHxLz-KEYP?g*(S><;dW8jCik5e z^a>yvo-Sl)U!@n|zLuU<-SGa4-m2IdE!$|3Tb#(`E$72@kSCR%V}hD^Y)mYu%4^Iq z*MzX7;Abl0qq6be0($f7-vFxY5v$*S zjt(N+j`9$`(`~jUV;wi9{S(rpF^WN8V%02O zMgEhXM;zq1`yhH>M;%S{{1ug6R`!xA9Sir&6|G)vcEOi4*|5X>@5>Oq<$tES%N~bk zC)zuM*F^0T&D2YK#fGpdkn8}(^#!3T0Gc-7K$liyz|MXBbk=D}8}M0a3aSj9f7*KP z7Y`azcP33xMIN=imk-4seztUS5q`rQP$@F9uhSbC`>$N8jzh2;$zOB#ST{MUKi#eN za9Qvg?XC1kh$;S4wEA^&i;g~`rIfJG)NhKdH0ioeMYH`~p@D0?n)AYVug&~6*-BF& zNR9^9pHsGcYD{TeP#9tpxBk z01mxdpIPvm6KVFJIr5{?7{Gd6LK+WjVK~(YjFCh6uTUfhUgNCG62{0Tpj8;Xo57ea zFxxY;vZ+g!r>vhX{m+M|enDj5|D~aF>~UIp7VbqWuX-ujVOq16bwBnMdOg{%z&IAH zs76=FdIGY}Iczae5hggrixze7yK8&j^7_S2F8r!ULc#@BLvo z-K`zCHl;cL6z}8e#24`a1DBbqiA(p!iA#gey0F9t4F|TKAa&EYS(}oAzjDt%mtsbD z;NQsEV+K8<6nay28+%jt7&M$I)+(N*;dtot`!3zvDhKAbx$IOfAwB7z6S`^=Qg!Ra zc9XB?AhvF~OyGFTKON+N3;@MttKKT^0^w|jn2&MTH;Tw->gIs-%z7HTtjiz$QC$9i z9V0X0?ET}#1S6|z{TT1BI6;Wxj~*$_ zj+dzbivc)*9{oD62Z~-NVzao>CtlaprrejtuH3ggEPr&%_X$ z)8W6WP-WxDzFsd5J8jjpC33WP&g{0^&%;#)vl7MJ0#S%toaoZaQpTcKuXj$g^VQ zIFnq$TVD|MK66%&=H9>#knYWnKED737(rG6+GM>715%$W732w=@bLNd1x5(8D>g@i zOEy4~JxqT?niV%|wj;^|IS}*I)n#!1tmhb&t(Wyrb>zR-To3nTz5V;*o3PxG0#A=U zvHubyZ<;m8MCW?;aTJaVx@TIra+5x~XNpJnOx5V&JrRh%_mQ92(JU8ad(sS*>>@bl zQ{67<^ZF6l^}TG@NzcfSoRAScb@G=q9dSCw`Q$ABQxBZ0N7EJO)j4>2xPGuf18N-k zM_$`iJ9w?9Ec?xfGyzbABGIzA1OzMxadJf=^sq=|iGHs4*GPzKcuA!1`O)EK+6V}B zeu%jG*_6M18$exGv6bu21*^O-hS7Cuk{9Lqa&Eg4^6jvC=gcx~GZqkPcIu(&bLGzW z^;>>ETS1<-gTfHXj&j*rc==bl=*PKQ2s%lGV228Au_3&F~I{h@~{(>O(egth_lWFGIManGHfD#&;Ud)~X< zJC?OXPOo1K*xqzk`}s6KH11b%hFMX@7tV_%S+L&L9z*V?IJ`^$9`*%hBFPCOc3_qu zzV*j4LuV-F)D4GwdBFmu%(qp@ab8n|VvTx5*#62bjZ4&4Bw*@N-JEptG_}+Y8huo; zGNi~fauLl2)So;1iQ@!AuU5Y^|58JtvYuWyZV}y6B1-(B>bu*9spFmR3yu>HWA9W~ zXtLC;>91-3>?U7Wv7b`?=~vwkZnf9ec$JFCcj-1{&RU?g>iTTlkK7a^&j$OIP=b+c z7MCr;bq*k- zsko9OZ_PxrJ%*RHzPXb#fYB^oE^eTv@$t~$&0M!sCQ{$>4?NJ1i+zeCg$EUawfHd% z?Tit==@XTUN}e2#6)WnWF%I;u3Q4zEPr{CP%{;;U$AufSWs?I5MAx+pmoJoF`Q)^x z%(g9CB3t)Lm+^R5?^K=Ha!J8Tc!!uhaM$blosSirx5t6#L+Sr$myRtuv@t zo_bn*o(Cc0wk~cL)SI|5Nm~y!ay+fumqMi=Y2et25s_eI$!cl)f4wc!hFx|jt(O^K`5Jp;2q%n>B&lY zx2~G*Skv)*|9jdwZ?hh8wqsasE=a5iwK>vt4DvF?On|}MnHo%e>ZgDYD@4izx+4*? zG%~4at5Mfz`VhS$75fQiG6*V>dH!FdB8vpxOXB^|vDhPA9!3+N)E!l2CO0}u;Q4c( z_??kv;Q4bzUj$Ffpt~Z7^+cG~JZ*k;T7~IE*!45+sL*q`2`|Kwqcr=;_!<3L+v1ib#imbPUaqqXLow zf`p_ZA=1)a0wN94&CogYP&41&sOLTReeb#VobQjBS^HVJf9t8W*6e5RinaMpMk2gA z1*RjX43hUJ_x<^hz%O(r1NCkzuWNYlqJC0mt+d&ZZ&wshY>y!M0`@S$q`|BKAihH+ z1?}$zukDR?2oz4*wgP_@$Ujjnob=jq9U7g}02+L0X8q!(Y{i{8;H(bZzqJjw1@@pg zfs?vl=X4gqMY8U9++{{sq*xfjL+>SSb8J51eT0tKqn5Mr7{B&);v;F{z0d~^*%8Ju z^lh_4Y%Stqd#wczLti^&zctRFSDYK-Xweqi`z~kHvE1@`e{^MmoOpRb?0Q@&O*SNz z4mkG#&o6<4IVT_BxaE#EQ_de}mpZ8ozz#A+FXIy3(sQRf%Ly6^Kf0VBsX;@k?>;)Q z8wsXNAHLSYA?DIrpcHE3kR559OiwsBbiL)in9KJ9wa^@g>?q?r-D?Bc-mBKDKYM2P zTsheb+V%OTcO7_G&U0}xfQ#1Sb;^EB!XC4q1xz)d`aaVhkKa%mr4j6LK(#W{p0r=A zpM&+=?fY`DIrK0}6)5#2K#NnvZPzO{>_;3`x4-h`0C<>cfEj29Frb%2XNE| z9+HayXTgfVQQPI;$81G%BTQ1vQcP=alHL&ClTYn*i@HO%nR3o};9G&bE&0;XV&T<^ zL=y+=NaIBMqgl=CEqBG#zZX0XO>?l0GR~p*nA7BHF)hEBqlGJVg1U!yl1R*XJyy%T zkd){ge#d7eMgyLm2s7s=hHEPby^5rGVOjyR|tMHi*fu| z)pd)AKnLX(G|*4HS6ipAPF!wxOJ%R_7lJH|Q+Kdsn)mXo(F0X_|K^1hDB_ZEDe zmCYA?UDw(4cLHqJfs8}obN;Tp5y1kH3mtw9WLQOI`Dl9jD6@ItY0B2xQs9+K_RUMI z$OQX(coVz>-Ux3id#1~uOD7WI8nPck5~9=0fMh{ZAd@!VZPvk`Srg5(J|>!%3Q{sq zx^h?EHUelr3HW@Ld+l6ERB?%+l;?5S0ULwUJ8j3uBo!gGHQikmA=@&yRs=dHDSde! zPc}WcvnWu^Hef$tLPBId3j2m8GUW5cHrL*z2@z~oY!*Yl+zi@`+kCYdZQojEq$_Ak zyFt4_2cazuk!Y50W^a~43L;-@ejBRblB!&bk?V_msq#{8JCYfCgF5;3CN27!)=i14 zd&xJ0)A8?klXJ&GNZmff?NVcHW1j_^UP$-+ArxuXL;nJPM`Z4OG1|<%Z{4=3s+ciu z#k)coDLtx%S*wUxc)B;+A%kWl|7YOjL@^%hwapBW%T6*w>8RHQZ%~&~Z$K4*?*;ab znSq((YuaPl2{fOztTvyYTmv3)lP1Xlo+}sN8Cy!HFQVRX>QgQmkVQ_Z@R2a;UpqEs zvgZCumk|@<*nAF2g%m;(A-Rze_DyA`V|)|*6N1uwwsfu`VIe9Z9?fRWc*r*e#p>>= zx?DOJc+M88)W8L_ubaj&IO^Xs-Jr@lKnY1&oQ|Xi7)jN`4((t_ZFt87OHiwQy&p{Z z!h)|okGidQ^ltYKnj$33UxW)@NV-Qg6Vmq&X62AWN|Ps=`z*~%ErU(X%7^WTLEAC{ zE1dkAJm^`iA;m~@dxK4kjL-_F_i?24rWOFt5_PTM?C~|-*jhULr4Zf|F- zz)`DS(~6=NwoKMs{|p5ob@%gnKT&9&$#nh@D@48E_iR8jfhKOaT3eL&PGRL2JN-|l zubbSM;u8%IJa_i}70SvAe*`I1log}-AB>ztCc6&E_ARM+r=u}>fxPu6MiX4ViGHHU zoe2BRvZqx>Ri;&jRVKOuxwL~JSDGc7J(}^GxsjopZ#Dz%yU^|Mr;-9Co%cy{gG~tQ z{hkhJNiOqH7Iw~5g|~~hEqp8tP$)usebBa#B*J*Z^(fX^eGBJjHe{}wbpT^Z9LEPx zlkk9jJJ76h1D=Wa;a#5gSoGc@xpJ}21s%eF%9Tm3G^bi5yBIEmNvCa>MpN+})ph98pVls`T1_`l>-dM zi+37b>6c$jYL=ic&`C!Vq7gC}A`(&>qS#D@jI(cqx4^s5b!8@F{L%u_{OfedbR;42 z%>&v?_3#2~rZUoY?O^|LKO>i1)>p`LG||I0Qf`pxcjI-D+YLA4MdzyQI@pxOc&!Tq zA9oT?t^$V}gveD2G#_*&t4;nf$(Z!Ec(Wu{k~1Ny^dlVa!9Qb;aZN!fPTo@%6*G*b z2pV1!aw`EuL}Y08_(37O#7Ucsp}dv4R)o?(?t+=f=uQ~Nh{8QHk+IngS#z_}vDqrp zF58OM1I=nTnAA-s>?Mk=ylKy9zx-0TOwvCQ6?aZ4CU6{GXK7w(!8R$&?Aq4}xzcCO z0bH8%!VSU{H%_mq)JVvqTX=-GE!`koDa*Iu2XhH~3c|rtZ|FUt5jLKFEdJi}-~l|I z-HRw=D%pO_rC!vy>REyy4{aW89vzGpMi&#p*(`*7wHdmZwE4omt<2c-nWxd1ptK+( zZCFT79wu2LcKc>2s*Rw2RT6m4G-%ij6c?AICbtjocBvNp%m*|QYBQ-56~?W79JwYG zY0VOw+@=$pfl|%`TEH;0NIL2aq`wA9zaYtzDI!fW-n=CFPqdaZAg}s>ye1LwTq<t7al315yf!hm5vw zL4QMkEi>~pmE>cj)3Hi3n`-*vHzmU-(MK+bdP&#1OEzVCt+K5o+z(mVa-exUk9L48vq5`NjSQy#{_W1UT*ky zgQ$lF?fk-m2FZguXk+z2BX(#Duwc32>{DXZ|AZTQDNshkGAJcG!`NLa6%zmgpkpqu zV-%>%8*YLf4}wJNQ{w4pL|3x-pc`5K>66KMKCRm-6F!2~A`tQ%=keQ48Z%P@#WqRe znyJLOs`!JMKJh>5ZI++D6W2^59?0~0usp@%Y?mw!zsF$x7{BsIz47wY4d;9n2FVO! z;!Gdo<@d#(l3N!F{Ze;q-;9YXGCpvJt;NAxhYSc+ewVzt@;TKC#FgC2vMF4eKobuZyc+rd-<|k zo!qXmFcpSgwysjj?eHm{E^~RKTYcYq&UtH*&CMxZQkyMIz4fE>mU*OPg(BlPdl+Mc z{OD{CkK2y9{B+0n8|9x=7^T?5h+97zZ+(iYsD1XrpKbyjUm#8+vP5&7B6a*QyXu|6 zos45S`x%<@K!YMSZ?ZVFoXHGLV4%Sct2bE;T25<*Mk~gy&h<)i3!zF%B1(*|tR`kBk$x z*#O4{_T|6ZN|#RfGBMU@cvfTRh74aR<&jd5-;q+NTq5fpS+v-PcN9rtOmP(HVjtot za>bb9DN4jX#8cFWF(ptmmur^0nMpYnHv~hjCF@aLjvf$dWY;diji#<4&~y;rNBb{6 zSay1f2~ht#MoL|0DVX{zpakAcMH(9tjbDqeoV{2O7T!+KmR*AO%0snV1_M%%#SKb9 z$yYhxzxLzVC2$B+LO=C_@!UKRy+MwUchGI?=y49}JiRotHlcrvAn+y@5-DQkb_eBC zTUoUaxVIuV=rkyZ@vm5Q{*|+x6IJ7M8}wr+3nRJhmtIaa2(aHQ93YS3xxk}TI56X7 zn|p4JLk-BBkpE`Nu9HAh0V=2qrzX1`a2%Y58`_*xj%$h=uGpai%(-iv>Ia@$F?EXR zff`W-eKeI4+_84+VYJS~C$)q744>RS{*{@i?2X|IIMqurFA4isLxOpUNWW;g+GE&s zy2Su>#A8q3GX?l{Jm65q0n-uhOtau;H=vgg78UP0C`-dzg)ZMZQkgYq9Bn%cH|MSK z1;*m>dn_LGYCnEq@*fp6ui;M)<~>If1bESDxH1A(+?^_Mej6&Fik=}qU+-r*0K^gq z2mIS@84)0pa5}qK6v8P2%kk$igqJJFOeWj=m6C3e^=JhM>i7Uo-GbAv>E$ zwJq7+_fIBabc_F=MG!DA;lJi(KTuMD{TeVARo#G7vn_M;=wZ^fjL_+~FxdGx&^aJK zfbajHd}>UU)(rC9|EOJOa_pIBXBNn@eNn#EU%< zxx1A5uCszEC2;A9fu}aVS@r+Ujg1~c4cFE!@?X9G-uqoGKP@>RF-?HNP13n%ng%*m zQNYZYt7{uc>)TDoB?8a9N7`&=0yLPg)ps|;1g~tWd9$?L!*0*x$)y9& z!?(>dLN@>H@}rW#|D%Bi;%YYcMEabQ>!_6I6X`8s_KEa`ozrFeYQpA^@G4+#bnngk znkbvk`f13BTQvNpYYook4?+aUJ(?zxR&FZM>&MZjM(InagR`GQ{_Bj3NCu$gaCbEIiL&jt0Ty-9Zd)Ec8?qZ4Wk>L>=_&n zxTeioj(!Jjj#4+-u@@b;rm=Y|f+=P2hrsOE|HbV5+0EM*UHk&<9f;7;=~$$-%z~cY zyD7zbKO=SRWA-yl@1mks( z9%0`cCmIBRAGc{>AhBUIx}Qwb0G>QYQ=(&Zz3zOMJqP!u6y%)E)d)>_?F+z-Ch>Lmm!2Ur!ng;68X?qm2 zw!Qm**hunjn8ZJyAO?lgOF~&Pa!P*UVD}-`>vPlgn2dmj&o@9qs&50!tPbgZaGwJF z4(|Z0u8at~XW__FI+9nrmKujr2II2&3O1xUe{MSUG+z~L_BX1qj`K0FLa zr`4VrpuaR`tTV0!C&C+;n`3n<%yt zcqBtD2}QKF+5URRk0R8H5HW)Xg|cke7Dpw8gQ8*flJ{KKxn!6be;P+kmUF{q^fdP-Tm&*C0PxnB#q}6u66DtT$&^;n7w{1 zARq}exx-5UCN?e=00i-gwjdgS+}zbiX;xUlsj|47af<1K?6pTPNnTn$ zGYMp!DtXfUV1c+?h+|7Ok3@Fk%GTw$OTgm=m-JsMR}yt5+CGzqR;t+$7-fX`^21%l zBOhnP)97ZiUQ^hb)pvO8ozTpCdn~HDZKE@*V)?oDxNU7fS;$FU>mt600I@^j*n;-* za*%N*F`~mp+WO6H+d@Qhj{3P84j!le3qIw}Y?sen)uf4+E>TPVEJ$kgCHlMc==<;8 zZl02HFTVv^CyVaWa;dp{nig+_2GWe^5#)V|B&n9#G%LJa)I;-c52V$p5K~rUI5;{E ze|aD+dU_xoIcob640x_F;(Duwq&YnNxy)x_{9@H&?X}Gjem2blek+NL+`VzvOAoYf zh&*Dg!qHpV7RFhpJ28`Jy{f94IJ3@$8|N3QTjcNZk9{_MGdp7c~zk*Ztlwmsvk z+Zju@<8yMvTyaS+>%1Q+7j7B)631yNJHB-uqc(DV`Kh_^P|2&lFNCV(Esx$B$;tV+ z%h=i)E`BBt;k`L}W1@`Fs4SIuXH>grPwDY0bwY}1r4_zX7B#6Alb?k1L(1#XP@Bs( zi`p{d@&c(`qL<-D@JPovTMS1edAoTQC(RkU{kXigF1uGZD#LSEY6)at4WX)0_~=(b zP`1Djo@_Kxv?pn_{!{Ntr5Xy87ns~$YlZnNYrb}7EFBO2us|rjLPRLd$DL_!$#=)H zg%&rae7&B$n11qNU7-ff_ACTtJ)*T3GlhSY3lSZc-u~WE{x<*k&8>61+F2VRhj{ca z3V9oOC!+F$NMm=E^{uLR>)0RC-ganXHJf;h>u1{Kv4|HaZ^wXfdRc)RP){SYab#wa z)rmEDke?piRea2SUqjaDo6c6ydg@U+Ntmv+C4S+J)kqO-{Uqu!0Xxxe1jYt}edMty zqPHj!s!R97^gI^dG;$?evw!P!dAL(Nr?XWbd30|mTDsTF<28fd!$Idu>k$~>#Z$iy zniCR-BgUG^$X1{rV^jwYJlBcipfLPp5zbHH)9VsmrSrb z>)W-p7|cpneO~R!@zTD+ZXY@NdGR`4lbTaRif>S->RW-HJ*^-C4poA#%zLa=S5qP{stLRfdR$PiuNG~|p{iy1 z>7GfZNlM~()s|45C#7bWg`&=->gdDuBThE zH><_E7)7_A{Tcf`#6x`!VbUy>rCWO$X{%9-&2~7cK<^b%XFjXFJQ;M0V>qw5ROVyE ziKR8?P4J>v=3}IdQssbklJl3>7HRj`M`P84cDh8;qnP!`klHtT6$DCvq_DBhjo!?g zU{V)M5{LlFM0=zT)5u~7Z}WK0Agh-*LNA;1$zeCm_Wh{bb|Zt#UM+)5Tu(SY#x&Kd zrhReN;sP%8VwxIdc2l;TwUoG?SO)1w&uM)Au~W$<9nB$B%+q!TD6$`8LJ-Xnb=3Cd zykgY%6B8|8Kg$dR^2cQTx#-CGhZkbqZsYALelP=IcvT4 zTh1=5;A+`gbj~)BisoRj(h5(zuVJngk#@h*$K4}i{C0J9-}?KB2`kr!u#MgJ4-*qv z;`*7T`=3KKme;DkpEz&sxp_s8W{nxpz`*XEH%rtq|IY*j(qyjLiq}<)4`z$hGlpcj}}a z*v$!b4lmtipe(O`Uh^nNbZ5+FiRzLk9W0A%_h&AlRaNzsyxtAhMs}fENu*7h>UNtT zQ0??0vsYs>39Lc(x-@P1)AAx6j5F$cwOLh;8pl4EktsF}w59Z^d>PF7{1MFA7q5XF zu}#LYBU#g}lRQgB93F?|yxkoqbdu5MEwn5>8My6M&Adf=5OC%WQ_6)$A=hueW&Tb& z7jW?oYs$q(uRq=X!Msem8}Leq+>deNEyG2x$Fd)GrG`w@17%qLW;I@}U)8v`{z)~R zF=D@(%n#iaM0W3?M3Od(+&}9puxxdWhkB8>tA8Oh;9+}@^ zWQII?e*F$5QxF+zfa)E(6#7RmKHX7c`b;JipnZoSg|R39Zc4YYM*W9w5g|%#=&iDU zP%XYdaQ5Mn0&&_v*bAP!Y?cpVBk zseGe+pZNCs3i%fMI{WVW61{9aoV?6SyqkP<^E2kq{=meCA&JwF(NNkD*O1AO%#h!Z z!0?74ouQcFIYU-MDnlVdB13LN216-BpQm3irQRX3CkuP8KSOU02@j#m$>+)DDX2-Q z$*IYxDWXZE$)m}lDbz{T$<@i!DF#Rf$Op&NaU#TX$sST)ju*YH$@N6ONj7*koN170U~VvKAa77@5NL2{xY%&Nfuq5) z;c|m^gLs2`!}N3eZin}F~FAwlqkU;@PpiUe&J+6Xu=a1vx)$Re=1U`4QUVTItz z*~q!tUBrYwrd%iWJBdxwC>BzAyZ7XqWXj*H?Cb|t_IDz5MZB0lrvn}Gs;~|Jdzb}o z!3J|`{n@b!ITDD&(a=qXIGu-c-VJ{jxWUBQmO1v<&=bH*^XlDaX=iC?=eo*ujfA1E%zQ+_OJSM$no4|t^L@5N&eeCR@cz69A{aEL?a+@Ai z4P`uOIZ&349PcjDAlFQhm*iEPcRW@=WJIx2iP0I)hn9@{23~ z_IdW=zmg6M#P9Q9P7{gyJm(X>if>tN^YO3j@en5fxei7X zm6~?svd`KGoHl0a6)wHmxN_#3bs5~QOA>9gIW^Jb6GrLtL) zxs}C$LgXvovbd-2oFn031X~kWAqEpb7apQ+?L3TRYs6Zx1*)W}W$MFrg{0Z)0=)SGfRdt;*BpfIFm?ssIKG+#%i4EI4*%STc8u zr^9I@CB`nBQ>|5U#ojZ(HVOTW`Q}!jDY&P|1a_r&63G6Bv%L(S6ZLn^|RqIlbZdjmd0b8>$xRb@PpGvxI)* z)q?wz?oV`dL|nJCSJCN>$yiGI+O*H{W+v?-p@!-TZ$9>~5OBzUPl3@lR`qbKJhlHZ zuk~*CwQ=k|+Z-)5%qA(yW>WN(n}o{zcLC#;lEMTPup}N^OMW&4Xk`v)WrE`}f_S_^ zqhI&kAyAP@J;c6b;J9nZW7~QnJlt4Q{cA^&Yg;MIDS4uL&6P`{<{Lkk6|#(G9e5wo zm#hF<z9n(_xVG*=4eYCXLg!Oj%b?~ zdLjEAu}`EhZ@Xe(fnY}K&Qj*l#(U|(g^2zA+D9utUzc^d|4+6QaFG8`gLax;aWa{b+yrbsKb%B#4Pt_WTc5NzpHlo_Tg5}zEu7e*FLL1cYfz}f$#dFXlcIX z?hA^q@EGww=v6uip64cq?wQhgY`C&1>k1-GG0XfRnib{|H{WH*m>(PQ(i%!DvJ3@E z=Z}2Abs>*yyj2a`UuNB zp(8q!W_@TM6>J&RlaVJ<&BWULg~KfE^VN^|d^?s~n~m^{o#t#@uG=yu0u?1C%2Hh@ zp?ju>pI==>+;|(^KJ!uEEq$PF>+Us0-kp=LkxFVstf`0OuQ)E&8-75m%b4f{yo%6j zpUjrC5s#aVnYz{&Z!?a(KMhUv`&P907>Jn>WX-ip&k_h%^ChTy9zJuroVuOS z6W|_cFL-}q>_Y}IvEh?rx?#Rgn2uNp@d%^Ay(#t-d@vKmYld$Dy)0!jM^v(pqN(zxe1h3&>Oms(bW z=GOf?UuBWMLo1}(Atd0VLp28z#oypRAa_4Ngc4_Ud`#BcLl4yhACFwO=lk|ZyiAD> z9qYWZoYGmEe61k4>_E(QH?HVUvimks&(T;U+5{WKGTEYRk`DdCvUcGNrGf&b!kd(P z%N^79N?o$958hf?nqp2?9tto{$$PZ z2@JbXS&KbfpC5Ncoa9Ynw|P(U5J$&a*o8q?C}wND5{lV3z@kSH?U=oNEcyguzQvC=4qfK2(VtQGl!{l)Ch0xc9az9~LCt z6p56+si8CE=bcMaQ5fl+$v!@+ki4QmV%zJ#znhwt_(O6!%tdwEH*ZW3RikBL!!D$P zZdSHTLa_@j#mo~-R)}>px!5W@=9q~rWE=*L8O$$23mg#^-le>@%G+5|WmZD9!yn&m zmmf4GjU~*NZZ6N?tQ}~T>%HIF)j=V3V`AChIeG~y(zTNcKiV8s=y(j!)H#K^c2nUx z4=j^b6*f!aen^f+&e#ZLx>c~tjltN*t6)he1^&{ce#yDW6kDk*FY2Jm-RwP$$Fn(R z#VYCrKn^BEZqjn#!I2;}0_t_N;drz$mU=TpaB>Ywa9 zwCl@#Qj}{Z6vj!F2T;3WgNAmq=MU~TtuFTvRWbTtO%;qTKfwwh67HsS2 z3WqH);K_$zi;&89@L~+}ry~#%C$|D8cllaKs(OvBh*mauHEG?T4Z%?l}TTk!8(ZRm5#^^|i z5*$8E0;rvvl($!^2X;P&VFZe1@0Ig{qcckZdrc0UagoY8Ap-6!xnSJjboANOKxM4E zI^v6YHvJ{)i~;!9P#%K^*%fpYn{N9&JT(dXX;3ZU(vFX zZlIxpF)lLHd%Pq8BH2Msqea?YkR}@=A0zW*D`Q8?fT8SAPA^>8tH~3T!Yz{#ULEr> zouNsy1q378RWD1RD%aVDAhxQKs3OfNi=Mi^nHL#yXH|j;lBlU9xq0B)&rn4}Kt_Gv zj8BH#1(jg3Bx+VkZe_T3Do|tkh9&=PUUK(0K{R_6nfInG_#66Cd@>egWN64M!n^%^ zd9FDzeIO*zyZ4_-&zzV(64L2?Ta>v=BTy0E;}^p7A4%)w5Bt~MPta?{M|=mLU0_#M zwo`4RlAskM-j?#PwZ-<(PWjx*{7eZ6cbGh6b+I>8dZZ_cL2tC@HG|iek2(?+g&(_I zoM7?>tBduajBATaTRH{%qZ%(G33{DAEc70n?5`<~kC;yWs2(o>4iYtT zoTy8;9t9C-zv&KA%U-1H-1(%+pYmALxMXOm@IS-yf51D1CFaOyyZ``eZUU_TUjZv$ zka;4ya#CAL)7dzi)llw*zY*!xGa9PAwzzg6Fy8AS+_1pn$=|TSF;R8h8%pj?JKaWx zxkqC_rb{tK>d9QjTE>D_0)is?O}&_UGqrTai%P{#;C38O5%vsv=*`MIl-v~*K{Vq8av|%!%<`@>1s^3oEcPTmJVtTLlYQ+o3doQnQwe5C8i*Lvt(eQE+V-s%E3n>g3e*;>oD!DJrUIE21=M-91U` z7!Zkx9&nA09w?294t=6qUXxL@Sus_CapfKS9!p^UAUB79Q=4AUaCJVS;9{GxYxEdr z(`V`1$w+sPWIm;uWr~q5BaHdcSqbstkEJe7rt%95{!U9b;-vEP-~0pK(kKfyHJ+-;A7MmV-8A8{r`)rBuU_F`D=F_*vVM zV-Z%%F}|Ysqo9O1q`aXaeSLMYn!$^x}1o zoU+t@FnuJZbN=>8_AZ0Ka`+?v5b@b0;wNj_ypA>1E{tpj{hezxO5GIiW5?t35G6N>ja5x;1)PWerU zmW`aImJa!+61ImnkKT`bzEy1U!gA$IKQ(~HB{iMPpGt%u+5jxIe5-g4$iRy_bL){G z$VdNM-D^*E1^+j$`5zncpFm027@OZF`x79~eqkiV@R?Z)5j(pkw*^>%d$w5R2q5$i zLt+xmYR#&r)#Ji9KRkVTIVL=tk+#=7Ri^QQn8smeHCVzdq#8tu(2t2~VeU!XNIjp? zTJKi43d!mp8jk2x!vRbqchbH6PnkaGvMLzlde@}i5HM3S2suC0Wkd=gvLYpndgm3`(I^{Qi35MdHE&8ex? zMN$3}L+OBQ*k7Bh%4$p9UwaMxNK&2~nTwgab(clp@|&K3kTliP)QWhk*V*`s!6B;8 z-ZInNSZxa}XP0}@N`f%Lj@FijA)-;o`{;P6;t+%SqBBKdcB^Wn}`kW*;Ft$!?>~AJ#wKr1-3_i#0d@IP7o_>jjeXru-?y$L%J1$~ z9Ivcq#SD!i@ou)A)T0#3us3XzD|Z_Op^|xdz20)Y{_Dr6YKV88B3FxdyL|2Pcappl zR5JpI@7+TdRDTD|D!TbyzRlW4= zYU>yi3#Jq=dF-#SEb1E#HA}L%IJv$a>K~_TEO}*SG&J$t--Z}Iv=01`z=tr;@riIq z#Bmsla3mLT;oWCZ?+Kp{m^Mr-re4$5mA}Sh3Uj(no<4kq!N+$AEkAKNd5AUv$}?(K|Lci^y&hdwdc`Cy zIyDFVJMr7V^8js2YxbE6h1UJv#4$8AwJ?3Z3zTW-^38?3WKLkbI z_Nrv#uGFENhBti8U&I@2F*`C!R%3P0PnOpK%b)h}G}R9WLjfmUT>EhxBfl1WNbt(p ze1&x!i@fns@Ks`m$##CLTXX)T+gFgttYWB`?aOF`+(Xu3o*=dH%^S98SS(< zV_!C7AJCh1u!&Eo=qb*%)PGzCACqzyf~Nb&-!s7LKMwGZznD5q-hUixs;IVY9LN2r zBg~@Y4p0CjzE$rCi{R=f144@%$U&tdtPO_>>B4 z#9lFfd`b!=k0E_0$h%7Tk2FTPg6Gh`5pPm|Cpql0br5FHX(E0(Rc`xTN&&&+2Yfb- zr@*>-MP<>ft+B2jYwvrbad`{w9E$E;%Wh`NZeg=V;Yz-4DmA8XF{VgrVO!lKtGNmW z@Rgz(v7kWLsa(o>Kr6N7=+u0*PBWg*eR*N=vf+gy0%Hw2-)VXAvPMA}7I?-}>(||_ zgSAYNEgvFUm?ByXBU+~4v>3i=nGSC;3~%`m4kno1v{=8XINy6u4{xUX+hFaxKBjOL z5?Au;cK#9%aZic&v#f5l{|_mLbuhVv_h6lKJ^Q}tlN}daX=!yi7|m1nBI&k|=i=h^d^6dG=Ie>O zj#ohx=j(Hw7X~Z#8%f}StiAF;klmF6b~~TVi~_Wabp!Dr;%m?vC20>bx4} zezQPS_BS=YsysWb_Y%fKJH|wfLm{{Q3ihd*O}Xw4(q5ypqHQ+y74PN?$sxOVFd|I9 zC3C>Odz$!3xwaL{t=jmUJ^ahvLvylpK_uxnn$FS$VrNhBbv8!- zn~eVaj3lKr=IRYp$)QIfE!KgT&sPcSq`U&rx(q^SNee<~2txRact6V!gzyiNf~_Ub zb_IWMip#Xaf$iG2+{M@DJ&#v|Cd_FoJs-!GZ%3fX>@3nlKe(SAWWd9~twW@ZeS=+J zXG*-94#+8Wy|jQ={yB8I@hGeG%5mmr5vQS1wI9L+F7SD?u1#`6NX2&JZ#E408AD(v zGkvhqQ$pBKM0ncGx`7cgO?VoI_VI^3m%|`6uQSQ1-$ex(T_ipEfH0#^aZ(%48tfBs z+;V?tl+8*TPnL2^k+0aeJ7c0C2u+P4|| zp&9$dna$|r`iE(QcvIVuFFWoyO8z|6j5vKD{}63$hwM$Z9}E>p-B57KSG5n04R;bzRdic{MkPFpTh_ zwiJ#Lm;gZ@Jrg^B6f{9ymOB0TPMLSW85rTz8GC~z=QALJqXbiDvV+B66fBq(U25Zc zeTeJ%T=&&n?oDSXUjjdR&wI*~*|3kKXrX3e$QWAqDif0bL8o@0o#d}Lk6?)!2yQP3 z1U{xYjpd#pBaw*1*eZ5ox~0TbD#p!x)7M7nO z0wfo*ea7C%IfU@*qyX8En*mJl>Y9)|JwOSmF`oBzX1^`+qVa8X_Yk=k7hrA!rKx=Z z$RLCuQv&s@1Hq9cBwmY8So;&wbIS54JLLOP26TH{zEFVMM zDfv1`&JMbadq0cpR(tLzGT;s(hNy(jl!;cRkl-1*m$^P=c`kZq^TE9_B}r4o9~Y*I zhslM`07ClX0>Ds@SU@IwjROekFXBV(*C4z7nWWoW^Tk+kXAVb+Bud?Y*-H=ym>1Z2 zuqCfx+Psc)F*Ov=2LVHVj4UpnQQe$W7;M7YH4KdgW4GO&Id39%Z(lu%!QUpmj7j%c z@VO=IlofuEUia_V?#W|~V-1xl;Ga6VSYM}LC;uI{)i)dbm|s@IJOjN@=*pixldLjn zB55LN?ZJg1^FLobliW2mXGNm#>196ND^0l>Ln2>6=*wO`1A$=UAc%W2G$e}dO;CLa z$t5bRCkHQd3-y(kF=ZzRQ6-#}0@R*20l-7IG!0WxVPhF>sU9X5a&lk*UX#d2%eSX2 z>fr#41P}m;MYjOlH3M>25JIV?%ba<=fN!}UwJ!$?2M)-$1ETipjWdAdNoa0 z)^SdZ{bv5x3_#r_J5piGM8PH?V&FH3A2^QF^f;y7paI@u>dFj4lwotN346fRG;M!^ z9CSr^aknGN4}XL~8~0xk8zu&gY~UUBd7i9_=^IVUx_YB0NT5@!(f1&yQOj+^rTc$) z5kB`8lF&W=v&u{SlB}}Zl>gmZdN5`)zvn}4t%)ROnU0u0-Z-5PKKeU$XtUnuGWsC7 z;G7JFLB=cgaR`&{CZR7Eqdzwws)#CX?2e%#Ag)b~G|9wPP#_#Bx5+4sCT|J3> zrWR^gq!1cQn(FOv<5US(rl{Z=#>w?$JO^E59URa~aD$*8Qsbl}(`OK@ z)`9xREltRp85%Riod-{K6SyCps91PW(1Of%1^}~1CB4yt{z0#1S)Qf zdcKTk_Mu_n#cZFMzK3l>=wH3F#6$YUX&Ippb=q7)H6a;z87t6E=-y1M+a_OghU`1# zldj-eW@WAiMi%6Xk(zN|dsC-vZ|7kJP4#;1j@}Pq=~~vl4yJ%UpX??e#WK2K z@4D_LrzI8h#)?yOvHr5(C-kw&VK0iR>U%}~v z;Pj#J_6Q|?`@Bq|_c@=0K^-KF(^ncYe7Y{XV}m1o^G~plgSiO45`YdmA-JHzI}a+n zE1<%&!Aa{sfAwEUb*$qDw4}Tf)LPEdv0xSSxBvNu+vFDhIMsN?xInu2$9bn-=PP{l z>U(>_Q=UaysQ*}IRTw=Zc>==)$<=yK)-(BDx`Bl`VHm^E8p~ggV@wpAo45|TIB%yzt zWFIh1L&7Ub%r8kSC`l|NNh~5sEG9`TAxSJHNeq=FzAZ^CD@iOTNh~jEs_^4{x2fut z=fHZQ5(Ht#_TS_28>Ca*015mTuue1Is)Mck#_yCA@ki6;-+F(irMY#@ zm&O6n*GX~3LyhPuE(<>uHCkP>sXMs!hf0I%H2HW$=Wb!Id-_(C#SYjCJ5eZ%~1uNdPdsj`4x@evn5XVgT?)j`8yVOEHyE zB#rR_T5JK-RP-Ve3OE_}fjxL9&0bD4XiBD9ZVqa3{7lxSNHp-iIAEWby!PPK!OjFd1I%0JrJSd`eaT$>!kb8B);VfgotXD^%XV28jMNXcjfVds(Cc zVR82ZP2^89(Bdz!e%&ls_Rqu{IWIw9?>EVjKMhC%-VOlJDOJ}j^Jfwpu}*ayFicK$ zjP5QQQ>_PFjdNJLh@nwJ%yzl4bN!^}Gd3?&2F5b>qW6`GU8{~`6g_VN>W^I$#niB$ zVX1_wF`esuL%!U(wX%mHPLtgm@m+q^PY+KQr{)=`48fh9a>lQXZ2rq4f=L35E>c|5 ztKQj%WF(iL_J_f{&t!RjotDpAu1!5cEc)|8qS3puk^0kNB+`^iRG3&|1$Vzw2Ac-> zE>#0qO9%>v!IsC+c6fmrK$O7_(%;6*vz1dfU|m?f&P@c||MXF!$bfkPjwcHE_KBRBym_|tRXY(A7 z7J5O^3o?yr=+W*R11t4BXT8PF7B((a&lpp+{NZya(C_~lYmNd4e88yEA`@Rf@HWk&xi)!sOH z3@$r|fd3Bx(tU>a@*s6iqP;8ld?!IVk4^w9Mgmx|ox7k;uO*~1)&})#vgX`L=`*@87PhlJQo&Et{9#ex0dqtBZm?8rrL?5wk z4C--mB|}?BGg~_V)iOcHK#i<>*beRz`O=@vAn>}dF5vBM2EX?#cg{O4Cb{E%4YW6v zq{VGQ#7?)BqZ*L;DT7z0ie-cJVb7Pkf%H@Huc#%yj{tFI`fi>`ozqay3K1WJ_l`TB zl0Q@R1Ww=dJ%GH!xvsW*{;fI;yiFK*=hfie#4fm5@#A#&Vi52xqZhlL8;RRqb&*(< zm5B}22~Yv{)QIft|4->huv-?p=K(eJyMc<#{9F$_CC3+OP`sd@t(SjO{n=4H>$pcA z=&kHuM18Q&N!!0D4utZj4ZY;y0ke~SYK@+_op2siE=7d)ff8vp?&!aO5)I`EWeqxP zP^r;WCgs=3JNoZ1F5v-jCBZ$lf3M$1mhcxzrFZcHD3I;&@91cIcn>ghZ~Q5w8B_rV z(v>V)1SX%D-yN88S)ycO5g@_C_}_W!H?u5>83QghLqijCV!F)~%2jF95Ev zCtLzr4~;)X3?D@7&+PV4`vhE4{w5JCy8l1s{yHwoZG9idr9m2$kW?5z=@67gau@_b zC8WEgQ@TSyWJpC)zyRs)MnYl`MOr{%kklb0&bP^ zec$W4uXPVkAo-b51CtlOP6z*g`sd~xavcoeyAil1%enV=RtUt=FIo2XjK!JuzN{M0 zz8Kllkr-ws^LylW6>qQp%#b;C!Q;6fqR&|}<9Nkb(o3InR&JbS{XboCf94e0qWy{> z8MU9BsJHCKSphPB@g}Ti9ntDet?1JBXYw&%BPNnAVq#kt*bk^!Ypu_&%`eUAh-HF(zdn=c;A(6fiPo8)|*OFyUeNgL-*r}RU|*F15@X!HK9IIN6iF?g&zp>Z6kxQThtmAuWE zNGj$?YUao@%#mlABWai;X_+JGm`_w45U`jd8JQ!Qm?N2)BUzXuS(zixGe=%vj$~u5 zV5bI}0@m8C%aGe##s{_%dDI5TX7?$GY|=py$sG74SaGoI@4wFDD-i-m&6RtX2)5b-6_-Tdjbh3>8P-e#Z>Ht=}h9*l8ZMeUkXGdmx_P(5W`I&{Wt z)2#m2>J{rD!Vr(agI@WFesQD0!8qZiSoY0RV+^choWP(yLB^63(ep8)=X_+3X(Y5^ z8qxE!=g*CwyK=zZ$e#02J*H7TIZ-_yqk4FveQ+k0Rv^XHXA}zGGa`_afk<{2>;WTM z0eWo8b1~2fg?W4^%>QdApkz)#F@GMET?stV0oDc1_FgL(v;$mq z`8v>gzwk8KI>Qe#qj*Qs@u4gSQy|A_xQpMBW8t(xI;flmSyl6)n6P2H$Ecg5-u_I64Rn&>FlMvnIBK z%y=#QPkR9yKP?ED5~LLVy-lr<+GG`qLGnBV$6sLh7e;^S{10sY4ZtAO3F3dAmwE?m zPM^_#|7)IbTq*i;0`rVmZJva&sL9DqBfG0od_vXh*GVJ3_0P;OJkgLis1~ceH=C(= zIFhAUWNJrtsMksN7`*MzkhQ7sg+1BJtj|8}IK?2v{-y5!zZ$E99OnKl2doT+=hdgc z^k?bUf2DCV+$id!OWH@5=O4GEWGaW-pLGz&%ag=+ki^TA#&?j$%ag@-kj2ZB$9Is& z%TvU6P{hkq#&=N0>rqwVRSu2OK<+{VxtTsR{W+XS^LWto_uo1(QYQlb74TnWsG2PQ zazc`}J~V~?x(1l!^n=g^1NDx?kWuosiqPuqpPW^N2a-cx=&Rvvg;1IZQ3nS_jJ}zf zp?NZNt#C4Ar*l63N2eL-P5n6yp6+4r(pT_CQzn#e9y$2`!T(aRF!lW(%={uT^yua~ zOe*-j^CW2Y*7{jykB;7Z@u&S9>3DhK_`gBw ze?Yd43m~J0AftvL(|{0=I$;dB5M=+L6R7uCVD9^M^5@eCuvypL2WXj67;x16^)1+1 zzb7DWZ0+`LFY@ClKLS~G8}~$Hpavp?h5)1A&Q9i>pW2;i>dL@pn5QxWx*cz+_c~$) zMZoL%>ScZY3p=>nv`**wQ@BDI)UVT#r9q39zfh|DhbRGi`frBy|8tbIPJrJ4aD&kF z$F>2gX_p8D#}%RzHkFy3SnM0}@or_!F78s|+m*~YdBf-V@jvQ@1-5a2JTWJE1Kx4T z_i*Mv`T5Q3(+9Yi$;prNfOfvgpWG!{t|u_~4y{bSiZDue$(fBq1S|m3=AGyx&ouWD z|3T3Ir|dHGwS*nBiQep3py*tnZT94vAN?85u|YWh`NWWqa6*wP^CX&ML(!b*_dB^> zWkNji*U8|G>|oQt{Kcx&!uq)iuYYYboI z-uyz4`4$3-fOc~z@dDXJzhqi%Z^k>nl)e9<=`}7G+w6TDJ%2Lu5j1;VnI8o+ANIuk z|G$80^8_kE+dEeVMH=n}Zi_`7()X~Sy=z`PUnAlY>3}VP_sDKMoMH0^FFyVwlHVNt zQvbvDZadBUSxBjJ;+t;1E$cfM#LWU~FskrIxV5VA+~=|Zp4W_9ttFP$*MV9{~lKJtl`9yw(*D zC6TwQ!UEo#{iljhiAOj4pv&ve0ClhCR0Sj=Kr^4akm(aTYoHRpr2(|uFNYS0#Q(#H zfG~X6IdqS3e)O+AiXdR|BVg5lasI1*M{7!Ql|1E7p8j`LJ%26q`6;5G2r(v$6 z=d(TDW6J|^tqm!wAd>T6J zW8Zh-j`#Y8(`}Z)>#@W65^V11K@;=09CI3N-OsPLiZDDC4My2fQ&b4rQG?n6sO!A} zw0S#q0$1&4Z}P8AKJ;J%LG1VSTv03_x|rBegY5V^9hr=;Z@RPerB}Ap(p3&2M@wvh zxxzCAx$8F-f3d0mTSTa8SU>w>0$1GWh6DEeW^1ZNDB(B;SZ(R>AMXhBdkxGowb}NC!k%e_HNV3b(?*D z=9-Z~?>|o-gEKQ3|^_#3fR_9Sb%Nkb)<;VNPWVDNu-@L8ETupLz?!6Tae2%rbIXk9)Zi@Ffc9wG`=0UZh zar)9$GmiQWlXLw>kg(C-_vWuL{sac=5_9)yov-`I`yhOjeKhuE_f_^4_SN>U?W^wZ zz$agzBy7z>t1qbC-D|Bl9v9c$Wp=6EjY3)aqD6px5wPGykdyXs@$iK)dbmmOWNdZb$;5^=3pT4{)4NFMCgm)T`E`6cxV{&}?XG zv@n_&&5fSqbl_Cq$crYQU=)m@?JV3<$Cdu3V4#GdZr94~`J@n!xX+w=R*H|;$G`|v z+qvxWXku+(_5Xp9>7Id0a{d?nqMm0|(*>oLrzBUnRr*a?F6=q?^a~Ho*NP0!1$k#KG zkr^W1?bv0!y&dWR?r5TuhGncGt+h>RR8 z&DZg&qOgXf&RV9EdHPoS%P<{IeRNPE2#iH%&p!RG<8a-JZlVC^ER)-7YSc~et`j#D zF$#g=M%kknQ2HndlrM?^rHtZ4S)*uCI;a|riGbmjL=GmK^v*n-`7v)!>`^RWTSL^p zZ(FbDv5#jgRb;#`q8429dQ#Y9i0doEuz_)P;X%g52aJpT%>U8U^VrV5_vf|!_CFMTlyhA z?{XQF(fRhJ@?*t2eCo%d;YtYhG6|-l*LC<;;|o3-l3LM#b_O$+!eRXQ^BnBWP-I9q zOCG=NohPq11-;|@)0i9HunR5sRXmVA>`G~SSHw;tPA(1;UA=0O#T!l5LD=!$pMrAn{NEI z4&HnG#?>~6K%+#Br6Pq+5crHdhw7?r6u~x$Y+r@g6XzGD-1C`P`K$&uwk- zqDPTSbD-yP;<7pBlpm2~P{VKe`N%*P{hG%T-dwflm6@?V=3 zRK9x zv{>v2s>vZnhE^-NQm+`~Ivb%Y(Ig>=Kh3`Gz`kr*>dp zP9N|MquAM&F};MHDID~{zQ@ReeUm4&<)gs^H*tK<( z(Muzu;Mh=|n~yAULBM-W$bvI>Fd(Uj4&fKxxT@IR-prZ36{Gv7o4r0&{x z9c>!jHG16(IE8E1K@+K``)&4zHYYi|i{DV!S6LL8K77TG4+Td5J@RUFkv1X$~irE@5?jM z*~xedLS{%N14DJS^3cNm`*2TZk9&f>WW{{K93R@oo-(wM%r=X2N$r2^kB7U@>2I}0 zFeY6%n){>@k3cd{B12J&3&OsB6w7lN{Hta+Y0}Hble!9c#>Dr(e;Su)mRu02j^`(c zWm^0quhKDl1loY@mkAgz6Y~^1n15BA-pfarF-x`hwSKK;HS2d{Hvt#9{F5Pl8kAJ< z`w{;6e0yMr&~txhvx|ZIk%E0$%&9U`)iQ-gdTT=~>J{r1)<T4H4ibte3j(zZ^;B+}))ccQ-ocO#dVuzgS}jo3~KD0c8qS-Dg} z(KcDw3{BR>gD33_N+*psf0vh@|9 zDc7&{p4m_2xJeN&L^_KzZm)H%D|0AjjJnd|v{AODYOcw)0Q^e|`;O7EfS!H#W*G0t zwXEJ~n#OR`!FNvrY?P@_W#Z? zi8BvpkTVH0iP8_Hkv{I69xCMlP(>h`;(dV zQ!GgQ-K~SKrKSYQ8PpF1VA$=?`2G;pE10{;bNy>q^H!ub>T`_0ISFq*eFrwoa+8Di zM#?2Hp_JchTe0Kwk1Z9~d$~u)c+q^8a?Gm0JV{oC{$Nn++SW$`r@L97&dMcSf4m@Wy4!75gF&X|#bjPw%dyAd`52ZXzOnDt`Ek|D zQNJA^-I1b|Oy(&3EwjdREay@wDk|F$+_3!>SF;q+E8cp72nGmA+Y=-mN+%o_uX1Up zGF3du8ETRwP&5d^gib!>vDoZeQ@MuEzm^foX|X(|*UkzU8!g1xXww<&Q~7t81Tpbe zu{n{ITOnSWad}xClWCpAWRB)k{JMtS1M`4I4``9*voyw{%i{F<(wZl_c`Vh84ZB(4 zevLx9CF5QSFG9CZH~oN9byjl@$(Buw7Kah~Kexya8JDuE0#T^(tLfJ?|Su!}A^nq&9Wh(r?|bhB`nyg93z;inVApEar0%q>{gGAa!Prpi z1SH;l5bkJbu6REZBnD$=008hwSeeRY`2uuduK{@VXC8&)X{WNUm6$DV!J2qLe>M9M zU)e;;Vhl(%_LUfDL`&2T=8#4WVR-W^tVeH0i+Ma@romoLI#e_eSnUv4paEd%LSSVH zuWgAf0bkdVc@!QV@5aWndi8>-=`1CTv^~TqGz^+@OIz(UYN=@Q6hPV$2iKj;|Bx0I z{-$oSv~?@8PUk=X)qK;NEG0P{BqrORLO8SX$$`UCweN#th76GdA)$jAbFz04RE!`m1S*;7tI%716 zk7h-4E207xO#)0Vh8J!zH}@0 z8Xx_*XU?l`GFSPv z@7A!e03F+Z;E&h9EC46`mp=|rVV5*MNrM4;b+#f5%&EC0e1cX)!J(<={Hvx_u7R=i z@SIUytP`)AUTfgoN4c(sw!sRLxm#m#-GY1M0B^89G`o_fcyh0{n+ zV_d9N-~fEAzC$K+|FT*e_$Pvsw(AF)AD5clCyj6JZ}*p6+G{i9WtvqAr|2Qx0<_LAIvViybZS+ z_wsOLizaQXw_~puZ(TVdalIf&mN=rw9nHUfgEKas>*EnBo# zuVS~?I@%LEWk|(%rP-qwg|ArQ zd%vkE3Cc}g=HNKdl+mf3i? zPj|vpWNJx!hgi43?AZKa07U({XrlBLQ4YYi3?AVVeL<#W)3O@7yki14Xb4~Y_|(6~ z+WhO0l~H3{?+SLIgBo;`G{|=1dMlIxJo|27;>3j}PHNq?YGiVF{E981vwiP^@&UfH zSMPpEv5a7__i%n3kq`$6{1wGT{G}817B0cNpC9Sa(CV($=`hR8C;RRsTjFW^{zxW> z*Okf$b}(yq3bdPD4rG>EXE^$@XaC~r%gN)lr{3jG?*AxX=)wIXUyv5zJHKyhPA?cu z#0*@r6IUnoY+~9RFfS@<14An{vyj5IZn@qOawAVL#@Q%>orP@NN1eE{x))J8lL?_n zi}J^5Op`1yL{7~lA@`J&j+4JTFr&T9S#~$_dI={2w@d_$;=C@{Tx{DLuT5HB ziv6F`n4TT5&lBXU1loGC(+8(oA&`{rpFl!d4L*&HiSB-i`{g4+v322mgKs;pP0fRRn165&{*Kr%uGb!Q%6M>^-8%NtMHXF z4aLq~oyUIJ`a{^*Ws^$nSo|{)?2M%o5j>*oAA>?u&#xWWmYpXeU~D8o6Y$IX81Uu+ zGb`glK$4{>QaAfxupX;SQIA&@8w-F8#{ttU9H*R}8^1Ht(6TECd`;zMt!|UZBD&HE zf6xu~>j|1v9jIuN00mB@bn-E{!`zaRqx?C#z)eg|0Qu*`3Y2UMfpE+NxQ2=bd`M*o z&ac7`FHtFhJE=IqH&iy@EGjJUER`&{v5GN#R@(O9;<5T;k?d)T3Nu-V(?ew9h^I7Z z+2tw_+j4vUUlp!wTIJjvOXcb^K@Bq;XJ1zh{M@N-?=op=(bRHDuPZyLv-6h(k?d4% z+n{kDcGA)b&@6XT;fdB7UYa}Q$NXAS$*P^(*Si*5xCP$ z&~Elg_4ZAiDx6j>7j0cZE7+&wd!`rq%aDRJ(jgr`AXdh4U%#c-^@pBq_N>J}(1zA1 zH}c>(6V8IRLEe|8MWluJ*!J~I{@$)cT4ij0+7&*qE0I>W)bmGmhy>=zgfMKQ$3Si! zPUbuVsKkjv8-_itdkw2hLYbM_3=E5w#2fqz1#o{Q%MjYIn(KrTqqty;-7`{nz=LC&q+QQBN zjNYqq&DBk-Q=Rr+NBh|`oc5Hc^p`nvI5p%zh}RKl%-gb^ei1CGoZf$v^beL)a%3eq zInGdQ%pC9CY5gPd8MubU|LRFvI5tEYZk&*Y(~x*^4XbcX2{io=^tgtXjILq15P_*) z{%bzAQ5_SUYc9XZZ8HozLjnxzm)Z@?=ycTdRjShvRXq*C0|o>zB6TQk>?MJz$_Ul~ zm>TO>*uafW2K}?~N({rk4S+rY>nCCv>}Qr=W!{jzs|j5VG~VW!)-Zsc@GJ1u)B$#i zSG02%0&MjAJJB<%)mjGuTzhp}6BMU^NFUmuosN`xK~fkJ@YLcTkWjASoiHN2zmU*t zSP%*Q9c+Y%@fs_&@=B`Nzz8e7nt6!u>VGZ@+$gQ9wEA%;S1oYEeVKX9i3*MPS_j}H z|CM5-dOvlm-+N4XA6^~~m!A7#Z;B+>HEbUM0gP9z*=1mbsz9bD9DZePj)vo7(AfGV zB7&FdS6HhBhzMB5_FxwtYz!hd&H~Q#Y7yrJ*rM=lPTn}XiQ-Xh)`5)AuLPXL^W6ws z4(hSa<7oS$KntcjLp>RgXB&Zb+kK@9V3aWeVYL3Vx|bmE_)F?`sAu}ZwwS|5 zPr3;QpYGpQUd4L}CM*TN2c67$S0~kRe`sBFp+A9%6N1U|1g1s^rc`=pPz?!SQ03++ zcr5~Hlify5 z8sz}b#R3bUncQYo5~{AjA}V~~8|lRZvsy1+qo76=l2~`GSKAer*Y zD=RRtxw66mwdEEm#2}U-iEm{z5d(k%o+W{JHbf1y3wGMqNJ$Na)qC2Rob&m0PZ#f9 z$ec_8t#$*ntqmM;DH`bgI+>i0Z#lWPZ3NowE(9_k#kU+j&QjQ)J>KhYIdxH1FxLU&=3T_S-q&nj03nEf|s+HWY5~3GlIT)U}R%>SU*_tuIPj#MVir zO*EJTDg%<(K&o~`ZMr``;85uBqjpLAq8ysEbFFxHd$ zcn5T9)M*dZX=~Jjy*?9FJ~LJ7oJN!GrIWO4lZE&#fvdUI*9ltUzOunk>inV*c3bL&e1N(RLLap|h z>CiNkk0*B(^L$c0u(|^sztLcTCMYoi42nbQM6uzUOK!+vr+qG72hE|ts!tjwSe@)S zc3MC?7NLdVMeLchXLrDTb}YE-f^^o=zy%AXKC1G8DAiZ>2Csp4xH*ehT2yWFbI5Fu z!LU!=kK(2YGWmvQ(Yn7~Y%%FIEn({#?5VHD#|ntK-h?`C@i_`WHRtjb;`>3>#Th9+ zZXbg?nA*l=m*|lNAy7VW?#+Pj@KeJpbKJ%cFggM%sDjWpOXlfh-guUXK)ifJ{2Brk zEZvky=57m?>qI%wL1QP)`gu4({YD;+SxrJ7j?rTVI0}RvHPG~@?Mx%xfw;iwUuCG* zm)u-0t{PvMj{(>)ZFct|1DtD;^KeKr-GLm2B!m2t49HW>EUMcF;Kto~V!b3YfjouLKpo2^=ZQ=8(+ z28ME(W!1(lSh|a>NaY%qhKvg8NdQIu)%J4sbDR5!R5XA1L|Pusv?&ev^3|X*Gb7!z z^)YT^1NA$r5J0q? zU|?WE- zGKg~iQMn_C0Bn=f3Ve5fMgh1QPvD{lZ~+O+;+Qr7lRlnJb@k7AW% z-+FYUl3?^?5gX!9V6@3a=SnB0+3IBZ5w2Ke3@Wa`9eLRS-xyPXSjU(WnBy!{h_86p zfpu1hEwrtHkX~4r0r80G8xTb>fa20mzM#AI0BHc?5=0un{(ht$A7Gm=lL5)ls74Y4 zoy8GGyE(Pa)r$w>(n%P10C5R~aXk>mUHU6#4|o&44ei&j^hXrH(dd?bca9y0g8)3T2Kf=YOVkT_Ba@ z`jz1UC6DH`?wTX=M)t~I(_On7ko4I;1Q{Mk6?F|$K)&fT@T~}B%f1E4H(I%O4WQ)C zk&_4y#G{((9FrRBiCy`&9y0sM8v^TIJH9RCKqJV>o+bEwjliAibS1gzHLSm^%YZw9 zbs2CcU_BsrZ#>EM=%I&iNC3@b!VQqHgkgh(1&qQ$5qebclSCr0!~p36Jk(Gf;Ukb^ zF~D)tQ}cUC9GO21%9hxJywP!)WkIV^v3DrN)j0;q8KrBOi*d7tGW|A0k{+n$QiOAv zctnG|vB*;l&UM2BXbVu+Nq^NO{95qL639hQa%jt5o->=>l0#4tm^Da!8(oL0KuYWg zVF)Pr>_kM!^aGn&ay*uUXAbghB@5IFM;stle*v+2hv>rZ7srVc;Y}C{=73ZMQZhG* zEy+ePqXEi+>{#JpOz`tYV|E7Luokis$Rn);MasM3R5*U%mudtk{kH$ zftCsS);WmqUaE6}3M9~V(s)Y1xgO&Gy2F3RM|aH=puGb06YA4H_HIhB9)DYQAy}O4 zW_sv$s|M$*wM$En-aip9CnB-`Dv86*=mm-JAW1w7Q0=pcH%=t+(~u;7E%*;f+);w8 zs3#aIN9D)~+W3mY?P{V-riujmNlg7XvMYt>4^@0hEG5Uqog2^Z1FctlS1ynI@m{&s zTxyf}YxXm>^*$;96L3)g5l2od1d)R#R7m3yjamTW_&P_wgwx$Bu0kMI_StD_$CTG+ z6lI#e$}|Hkdz;;!7cn8zyW&~ylUW7ruzlsoS-5luBIN4YHHjWS?T5p5*68Bif;pn< zjcVJ*0zc)a=b~q9WU4!wmzTfAIvD@9EU>2BCt-Unct=4}_oPDR&P9-z>|eF`dnxWl zt4#Eo%AZP;aUChZXwLJef|Mg$atF8v5aggDQ70C0Ww*{@srqf~=2J2HD!IW%%?hDw zys@)9&Q%_IMg3-mG+)X=ncU`UhEFEUPGT%h#$Feo&&{k z3SfSF!487|b=ZjP#LTlqb=1pt5_Ktq>oqDG|d(hWk@( zT4_y|FBt2P>N*&L!ThTMIDOkd<@dY;D!~$;;eHz;mAT;=prljL_<)fDs_1wLz~Oll zT;@P>1mZ_OVFUc=gEqLe)U2uq;78rbHmz6stLeAa=7qK7@o z5A1ZvAxXj59doa{cO^JN(sT`-LdMc&T?BM0Iwk4-p^lmge!T!ipC+eGhLl<4%o@XC zz$_N|0h#xdS1WbN=AQ+ZS;rfBM5Gg!1apExG2Dx`ZQOZtomBRUVGx-U^=wXpGHDT? zhE)zIgzV)JSx$uI1c#D8CMhf=B4zZuVNt}4OD|nZc30&Z&XCj<)M2shJt5ygl@c{j z@P&EvcBXhylkI_%&e!LZpkx8<=^(FRnJ|EQ=5?lXt5 za6&TcIzWx#%m50fus^-Iz7g$mPZ&WiS(A=Hg}zK=1{4nLIcdN;4Kqglx)19PI}bW| zY@H8I-dJWzvvV7TOGq|%PZt}PKOildREiZVWXw^3hyqNXt<_j&HPI8h?`fo~4f>s) zYdh~97`I4zzIBQ}jNGvY3(w8j1OG$LYT!Lu={;)3VZQR?uNCJRVR#N2X{A3`pPA@r zqWWV3v<=KoNoits13&?e!?@`O#p?ZI-HO2aGS4;A@+e% z69f@3Z0y3qFn^r! zWWUS7@4@#o7Uut~hVQ#)doxzVcb!CqoxwIF?M{YeTKHvadhzRSodOJ#jAd`g8hGFR zIGpe3>AqW+d|bM>$-gi!-xB?mWnyeipz;mHCGpP>IsdbOReyhb+NEJ5Na8`@fBtlY zll~e57l-`MRcHb$!g;{!&;MGdmv`PonnNFb283`65fAkuhOMs);S$s^={e8M?Xb~< zO7HW%6p}Se@Q4%O=&$Kyl9bUmKFZfm>WuQoVf=nNf*Hj@)O6}QHcpuYJX6c8$`}5Q zesASYYLPHlKxS9^+0?r~>LLWWP$wwa=c}M=v)fJTB)rY3L;+kE9C95d7&5kC@g?=6 z^<66Vo#$z+Ibyycoy}Nx5#Z%St<~%*n=5w_TAj@?vl2&rVi~(}ANdmbCBD%_@Qr?s z{C@WCw+=_{YMCq+X;J6N#mK<#<@h5`6^D_Yff6JR-$H-?#|;_m1P*R#i+>b4q%lVI zN9nnmcw7}kkO7@}e+6Zh!g$3y`sCwft6sS*AvhW1lqdUD$j}y{bu7?GfMx}#@^G@3 z>aMJOh=L)?WUjTFx9+Li3I`+-Pf2|}itxv&4Y$Aw@ml6U;t^`=FrYc>-obk1HkSRi zAJyK0gZ~%@E&_rgbayn!0{Eo(46S;DmpnD#t~Wf8IA!UoNE|RjAVH~)CXxX-sm=I; zrYjBsor0}ZlOyjv_2`&BDy}~ z4&ysZPdj=TWQWl2}9w&b!zqRAPj0@$UU zof(jO0*#Xq$C!RErYnt?d&ysx*DE~oACco%!w$pO2x6_~)bldgraZR#E_YkI!QY}Q zc+8PS&&8yQK}DN!@9|{fBEI%yV$l^6r*4=2v9g5Nthin>$-p|He(?3_SbYHZ?&ASv zAH1*+a$~(@oggP7EAmu^dx9DpxaV;C)ephr1&fm)Am5y5(bd9n8c2MVhp$aO`bzZj zUB2e)qz4Z`&4e9}aaK-MWg+P|?(t&L)oQncZwVjGscRi9_RM2Rj-=lN+7Q%r42udq zUrv+%k6%ChJ9;cm-18V5hIQ0&UvPUKHPE+WS-VS(tjxx`EcN_ri-Yq(wciWoh?L zjU}torWF@McfXtu?k;sBmsnMqHcCnpIz1Ph?s+qp*hd(2O(!j5kB%j&)9$tVy*z7Z z;^R?%`E7VtBFhT4@r+$D3BQovE=3_)NF*X4E?eRUou|ank3`zeK^4p?v=NZcfiKPjg7W4m!u!+e%5_EYOBuqa;_#W zzfp$GnKrbY;!{F(9sYBQ&h?K+xI#wPy9@dRji%PdYnd5#%@;!MPCbm{E_YEDy^ZW9 z@xJlQu6K`(U<@hfi72pxf4he-b9pGPH`M&=Vw$8l$Iye`4s%yT!8w)%H-j3BmsmpT zI*4<^n}Q`Nu>hWKl1A^UJVBgig|ImKD{GoU{sL0IDFd0X3Voqpe;c3=C(X|@cj50= zQwF5pJeEROW!by|$6EKU>Iv0USlHr6#?1xz34UC3JE--`-4AT$vGpG^3$VArpYUCG zXB3x^p7(x+|F=Ks`inKu+l2O{Vg2;J>==UXrS99&TUQ-{J>f`K*(qA%tLgBI3!4Cx z9$}5HD-8tM*05P+xA+R;n9;IZwN`Rdft{+TN5N!>0ofRhG2|dgEf}pZPC+4Tp^`v( zAc%k9DH}L2651^4Rt8sme+ms<$Kqv*yH#r!IL;k9fzOB+>W)XI1*5>#hmdJlqnR+l z&@O!!4-J(8*^RepSip>gO(uAw={G$9SQ_PK_y$wY^aOF51+r|qNYZwAqUi;u2a);o zj61;xb@YDg%ryd`#@Z~e($%{;Qd!K?mt2-(bN>GKZvO^Od=~)c<59fZ!H2!KwWkH8 z@rspVU2a3@e7n?j#qibgKv2v-L8yl$Y8q((4dRicnCqLM7+=@L$6%mLbpRuMq&@y2 zrwan~qk+ZYLL+tFvg`HUvV&XW8#La53`<;iZk`z?h?8#)s%pUB0x5(Ue*f;othTW# zwb*dFprh<+W+!Qyr^YR8ag{=Hg3m4~{y!*2-t_;&TFPw#Y*K>pp1wNZy%% zb^I)b4&G^#rj~EL-hoSpx{d-Q8G`PA|7%mFdf0SAbK-h@r3;S3G@Tf0W~}wX&4o(} zOZa|vx1$G1(~7m+9b4VU%$|S<dB}-{)fz6i09#?t zU|25gB(3zG3L58)1_!2q19#?L-v+}IcO46|C670_JSSMPB`DqRftZJ|K-FOZ=W+o- zr4Ebsr>6~)0^75Avcnb4V6#<=Rb~waZ##sn#R73?K-Ot-3amb+ECyTvkXh9L>+PEU z!^nPusOFo}N%9zjA|canJDMVq7JI1wxNV=|hDJBTm8Bk`&@KGI-wr+w1F{Wr-6T<* z3&3#p25QdcppuHfdCM|^-p{Wa=u?qMGxXm1~4OX=C{DJ zzUKiaLTUgLNLhRwJo7>@6pz`D;LS^}&{KfE;s-=Nae0;Ms!CqMRql%lPb*kPJbmZM+- zYab~QbAnz_X9MhJRbB-y6liz=rGQ%#sp%lVHvl#ark=9DCr^3+qwq~x+=l{BT>$V$ zDuixhLS#yIjgKSW1y4;^H_EKW7$kzw?=CGYl_G8DN-qqzKuX<}DA+bW(cqd53UT$y z-JTt{t9w(Oyz!gwm63s0^#b)qypKOoI|zB56W`aq88P_x6>KUsc@iiu)KP+{Yc?+} z8$oB{)?Gg?Y#F4QLEQ3U5PC&{lR!;}yBnAgCxJoZ{gGa*(c5wAWSD0cU4cbEc#TEt zCG*u%QLW+p_wow`(+Pfhl%p(?JPA1Y;9`z&a0y{(`tsX62|l3&YQCO}pnZ>_%j&6z zCEzqAxPc0B4i)G&2=S}Xv<+?nrg<_j&Bu;1gF|?kud;$*%P6p>M|Bkn$c)PwkJDpl zyuFWth-JT)&6s2$wmc5SWf0XGI9y_QQf>T5IAugdCgTe7-Ke09&` z2sX^wacukeo4Mgtx`;tK@xQO4VVg#-tIWc8l_y>Yduqor`c-hxTg`t>f7!lgu>BUj z(s{)&fB7(|?jKvL-gXr+|8_to(eHLdITK0g}F2jU9tWiGrVUX>6 z@DW2mjyRM>??Kay3>Z~!7l;cIu3JI28u12_CKKP-Xvs0!0|bfD_F=I@?jVj!>Pmhq zvGyCadq@)Sv>v60ofYVjUx8at$ejTbFf;^aX6pq@$#!=Xx!|ee`jm2XUDZ(pb#OUF zwsECzn5s7D3l(C{-p$aA$nWlZ;B=Kkb&&Bshm7||G)x4Y4`T#%T6Hi#$JXk~ux%P! z&V}pA-^}&ey>D9nz9FS#lb$svnq9S|L(E}UY`xfeZ=6pBa=8nTry+r-6~lqokTa*~hM>xD&1%@e;{pdUnW$g`ThEli zScl?uLuT%u?9`!5Ga^mMf$0Mhuv8-j+<2tozDq6%)@z+Hx$Z%)*?uKEW~ zyY@W6^`s|om@h-gD~#2xZ*bqW- zuN*ev%&_=fL82}rf^0)bYxxP{kKbwkd#3X1H*|k;M29nSlQ1Pp8mGQCni}fVp>W*d zz-4SFkqcnndN};90vpc91hvzjRgzmRu=#RU=0~Z!U_NK$Hr{h#mo-o+noJl* zU)T(<14v0%pM0hUaQy|NA2%s~{MZOQ`f2J^EeQ(k7@S-K!U_SDQaDw@`>!FAZHgm#LZb&9>2`eEnBxn6QY^u4Iq*d#i% z@Dru%j*d2wCPU{445Ig%WU|3a{+oUDO8I^HecOnrY5Q-pUlPjhA-RdmxCDH1B|FI+ zS+2Nzp*D}d<;gOCtG5%QtM+X#w@Y`k zczawlb{axRxY7xmnaH443KN>Wxrl}h@vz5y_z9^Bd_P z#}3l1@;$>#WKE_@Y5Uqy^e#(F>XutKW|Q{6zBj#0k#yC7;M6JX^QTTxgCo4}^1Itu zI#~W}1)*1_uj(P<8N{fX30?=X?(=;ad)3ogp7V@zC)UB+g5PyfCFwD<=S|iZ^u)oz ztye!$vC4XurQ+>WN3yB};p(!b9@626mVaO=Q}je!fAHM5YYVax?2iKe2@iZKs*L8YR!}kzhEA3<__Z`-D3xB70F? zcCG0{tUBS#+b=zCg#=ceJ7>S$7PqY+k01X6>0{E~nUEQ=nk4Evn)p(wKY3L+SY7Z7 zuE22;&|Ij`vPR^G@QcfJ9pZjsc;D~MPitluSp*XklBqM`6OelG=xW!Yx3DdplIhci z5vNmX4hs=)OwGr~mxWJBK5Bg_tZ$I<=8nuj84cIL@`#iT*~)DKCv=I^hp9MBNvK%1 zT3~HBOWgjPB7u`Ez4S!8a+9ai+=^H>$?SN$827Z?xI2d7#(<4ad3doxoKIlW1gS;$ zhi2ND2kn{_Dct-w9+IZ3jouvae#wXQA_^GjB5b`AWvX~HPn%e%gZec_+6tq4!(Dbs zko_a}GlO>Ld~N#ME1NpU5uX#I`DWP0k?%x9ZoAs6Ohzm!42Q4a*2vkL$Qelo{+R!u z!Jm0_&_!$9=)L1FU8DKw_1c2FWB<`kNy+g+{Ln~Xz~Pe9?BV6~eMzHl=Myf6kS zkv;wH>sbao4pTU>8vbs(E-Sd+wFalww6gjF)EI>~gr8iJ)vW{OhiL*m zaW$J-A3LA9#!_2J9G)s1w_Y4kRl7TN<5CpniNJ%1n-}smK4%7P-;rybI^G>{L4#FMuzo0+JmB>-Q*PFIvMbZ{>&i)2J_blp zO5(cyf8@PoR9$VdEsO+6aCg_>8rbIoF{YEYm<7xx8X=4h^vFD=K?bN2QPvgyWiA1z=_ zXA-q=S|9h zDcE-Tj(BvkDRQtBF&JFTI}nszRd5t}n3GqG5-|weQbohx-)}ho68OMdxkY?3=^->Q z1}@Gm7Sh+~315}C!95xov~6J)32CcEM12hQW*-<$&y!AL4T0*F`boMG)QLK=TaKpf zv2Aiv!q@{Lk)V1?$>D8%?pSpc{vrg3?euF73K*=u)0NHEO{(*)>H-HpLZ^I!Zj(R7 z;Ff9RIV>-4F1H8}@>T;{GV9NZQ3dN4Q0D|ZM3RGqx!Uul{@KblJ7@u|>D zA{XsL$hftp$9(%;|6P97VG3WT?~K)!pQow0OA_`&)r+W_7v?5ReRz3EEl+CmL9;jl zQ@*Ehh{9oV)|9o!=aH>AB_Og{LwRd7X)E`aXRj=@6?V6xl&XXprrxJ)5 z>`llvhzJ(Va+_C`&H-ipi9X4?1s2&r}#~zp>GI_u}fH?n|q15fMeDlVfzvBuhx@ z20!1i`V(NFMK@@gRjJjmodBLasxL^RKi`6l!s ztv)-q(_-+Tqjy)w<@d*j!mplVmBjmv*6mL&U~k%=e(XSqa~|j@o9< zb(!?c$fr8iC7mk`OPtm{Da`kKKuPQAs-K?GDy=I-$vq;~Y8JNy?UQ*Ew#br!KGPPl zIk0=ve!VvBB%C2XR0O~0UNQ({{dan-un-W~Y!DDge=Ek0PVQC!$G^(3_Ht~2G^%gs z)(c!uYj3EZVIL?W!K%)+d1;jDHsZaa={?Kuu>;)`-_8Trcj`n0Mb`-g`azA~ADq>k zy?<|CO$ovWa|wYyzr8#Af-*9I#B&(GpFr+uvmY-?f~?o3&83&}hVgvObfXkJE~LQj z_5OKRV|5TJE{t8ytKW?;zG&3*nu5zyw$TT%^PLQSGp@~4bdt5|s;=o){>P**TAW2g z618cnY2w5P@k0uo$Bod;;f+j^^qAycdG+FC7u5Qk_6aQ&sCg$?pzEM^!=myUB{^!R zt01MIHjd#02q8#a+7D1$_oa3U!$~^T1@Dqtl~tT%)CnwCONnUBRSr7I3Iy#rkrHrC z15%F+--a_nP?4v3MJArsiuphx;Q&phj+oa$NCx6q#o%eV9!e~hGGxlqxcK?#C$Es> z5VK@*HrysOc$9kL;5I}CgQWRd*E{@=tr zW7$rJ^NrRkp{d`QjN7kY@`yjQozcn6#XTpns>XLi?-&8i_uz0Fbt5>dw+Lvo>poya zkx8T}%ndD5))FMRX$UI&qEAu}#WA3DiiW7d&XcrL8cd{%g-3^D==QhcbO$k{?m7GG zeTKmy)|FVGPqV@9yTY7zKv1=j&7lZPG2Hbl`Z3Bw(E-kZpL>@xcG-KD-c%$M_-onU zXaP>NEi&u9um2X(FQJ6i)YP~Jtkh=H5!`erxeUqT=$|2e+5$bRaMu=jnjROx*G4Uce+@?Ez&W+9d{_*X}qe1 zySr|0--+SLMc5|7)+$1Td_KUcl0E7iqU}Cn5 z46PNL`R|U2TmRNW1&y_%yFDVkkgI)dd=JwSZz^^VWA#}$+nx4)DP$x*uNuYqqdVlA z-x`Vw6N046YrtIQ2Zop#xJ5&5C^O1i8uTX3r)3p;XXAUZc6S7*#zj@|dIO5?<7TsY-MbK))U}SVY=^2@E%)=7nJ$FT=k$j5@NTRvF zgvpGuiM40uXb-;@Xe-T&S$UMdsVh{q4R;*Hi~^QzQTJ&hfvezY#l(^FY;VZhrI(HIw3ieAeqbHSbAj^HJx`%rX0itbC* z9%yjv+;0&ox(CDShzP?m_jvG4PHlY@4t9(~P0gEQaHlHXYS)x`HzS2ob|+Asj&S=E zGd@$1>wal?UHM?B+a0CMT8dwrjfj#QUp!6XL~ya`$A{ zV3iMr3)f7YOszOd1PTaZck`wI-_=lkIs(a8Ax;r>cvi&3S?7gfd&H0&R zyFx1Uxz0Pw`bg4hI+N#+6?AzSO+)ABWR6GMgV_iY7EQheSr+$BVvG|>Uk26nv? zO8Sm>^8;YqDqfjle3I{5NYNa!;N-BO~NeY_kQzvB)3cr1{ zdmJM#Qo&HU4vhhK{?i-164_gv*SFfzN&x|Z`5(2#%)kL)tm@=oZe#jai7EQJYP-mZ z>VuQ_>VMYzTcAj>jRd!aQX-?V*UyGN!^*RSYGIa6nONr2^~*!H4;Wo2r?Nmv#49&u zuyu2gFJX>Z2&eh3C<-L&4Q%r8X-1W$0c7kSgEqzof8q!+205#gvN1QbXb~pqwzWkU zT3Tl3**3Im9c3+cYBE{ZsRq}NkF-xxf|E+(VqQKi7B3S}skW;A!n${GcE8`Oi%ZuF zE2^w7v}f+@{2C@}`l^1=@oav*j1swMnYpajx`@)b8jn^Ys{=6hX?OZLH}Z8-_NOd8 zg|2xYSKa(8L!)el^F26p;^(0}J-!N`>JIkSb|M=_?~&vF0Z+IojjDJ__Cm%eQOEU6 z_L|Zp?PlHam^pwEB9X|w=XHU&evvknswa-mdtcS$%V%N7DLOKZuA_uDx|?2v|o@@>}K#*d5cSw+c@O+?ixv<<;hU9$aWPEXwGtn3cgMgXV z@25n3

hLN9vChra0-;?%xfL9DubPtW+%$!x2*Y;sq_qGP4gzIDmNH5cB^Bw4?UVHwBrKzdvaWc*NQJ8KIt zA1cI=c+{Y5uIGF3@BQ?lx8NkBvNH{Knai;tnE63uI64#2u|5+JYcU!4_FSS=Uy)q< zcPAiq+!padgDbIHU3KZjQq{m)-Xj>lJ=`*jxYf zXEVOjWOB|>Q5q6TFXvV$d2`)nn581{EQN*hrP?pNK@8fI8wj{LYn6j55 zygm~7h5KOktXPgaNOFtBRtY%a=IwMu>=JR<F2s0FUO$gH`MtmL?1JbyghI0~LCJdP zI(&zQU*(88cNMh(Kk2r_2@5ee#!AZfDVEG&xj2G8tVD<$0+u4ciksYmxUnF=IcEFp zS)4r-IeThsT|*Uytl<6zrFIjRrG^}ek+dN1gZPjWLD*fj#XIHfIe6G3%S;i>>EmxK zF*ViK;;d#A2;W-H_y(6m8`z=Cz@My1&>-5=)de85*B`=UyXwOU^#|f1~BvilD zB5X^};{C@i56~tB>B!c=%aS5h~|{D-cZY&kCvKFza@5S6~({fh#O zJn_gbJAgIUHv;?=D4YHm-NJt53UN~s&{geqc8==orHvL4W5ZyC4SGw~V8Cm0G=A!|cr9gqIj)3mpd+;t)umsKwsPt*CY#^NJ6y%Pe zkaXT<>WjMwp+W~UH}RY!3L^2MqsI%wmG^BAX$-6q;@XXqAFmp>pU2Ef+QfvVBLa!k zXf~cQS%;w8o<2#R^4@*->eUk|L$m2B$z=W*~gTcKm=84MqU z_jQWJ+8FZvQByR?_499<`2Tq`#{6+*A^cClRDpmX_*=m=b}(@LQ!ANWtgXcVcFfB3 zc6^kj`&KtGLr&q2gx()W*bw$T#Ti$)_!WqqHbO4?=^{$xYs!wQV(MkngG2t6 z0YB4)TB9I0`d0d{9Rp0ged;RYg{B;9g~S5`-FM`n>lYsRJ>h^r)vc{*5kQ+5Cv^O=FJDg^h%?*-+QXAbg%)HOu0 z2pmlzm*6=um#ph_Fq0>Bll6D%kFw0#_4x$&-Too6kTZ zx1$O=l>o-1IJJ!B>AE(Je(j@ay2YjNgE|HqwO{Y`D}K7fCLAVpQNF2c%|NqHTX6# zNL+5L*5z}djKazfE@zsS-TD1|q&GY$(m%Tf(le ze#&WNU8O>({q1VkQTXy{dB2LyXf%v5W}F#j)H(q3Oj#I2@*!RKc=DM&zuonBJ_^h4 zIg+|MrmGJi;;9kchtwwzfdPu9i6bu*{vUL}RhvyR{^ReSJC5c&f+H+mgxR<;1MhcH z*yIIz15LQWrfCqr1VjlfEs%Z-A+S0IrNzDPJw{O(C=J@(5!!Dq~TUj&N(Cp>2Y5|4pi-z#St6`9v`tWP~`z7ykU4CV@u$qkgAHEb&VwK$Eo$sG|R?m!<^O1x=cgm_OBVcg4VV;;YR zF|^aWt}bR`qygxrmXdtGYabaVKajI60vxSus(}hL5JAR*ec=@cB!aWwk5odtckAd1 zpey|zT%@OV)Jootj~3M!s=ACto-v(tiittWbW~t$$x(3U z;6x#zFn}$$k@oG%Go+=WMk;{e0nYSn(s!c;sWGf%88|h-&cTJm;EA;O!j^a6qgQW8 zvjLcHCj=nXZxweuNNP|esV|x!7LGQd9suU^hK?O6WBtq#SYGo9F#5tG$QK^9j~6e} zf{IsVL*kzD(oh6`^zJRWXMJYp;M&)><#0`S;o+KV+Iswa|jtkN46m)r7A<1qS9Aehg$x+ufIz=o}B<=aVKMz^lQ+vD$2h6{9Tp0B5Bl_dF%zpOUv zjFQuHD%cL+28ppYRT%U%pwyMJZj>p+r+eJm&QlseLPz4147~mOH5RNR18b)H4b3ly z3_Ga=K7GWQ1Y_H0q)cP_6vkmtK-tRY6JdHs4PelDAy}eTFZe-aL#q9f%PQ&kh}>cn zov&58I@&?zaSW6=Wz7o<*p*P8Ds1rn&^<%NoN#2_>5l;Tg08NdH|=Mogn(H8nNZW@ zVF3fGFwlX}wLvxqbZ<3Wcl!)GR);k5krp2uWp>-|fHNi7T%meV*_hR5EpK&T-8rYM zZDOJ&qpEEZ0~CyIbtPwURo33Aswhm@8oJJ`;Y^GybA5(I1|khgGute%!MbMY_Knf) zFdg8IRgnl~bn}~kBFXibvt45`!ozb8uC1XJj!H=9Q)D(YEyZy@E5==#xz0SOo?M`3 z{uu;r^?|N7=D5!d2JiSjcQ1)R;X0o@C}@KWN>;8tv8qUV9_u~Se1G_f(e^Ts+3R+A zz^rPzKNI~kIk7yd@E8gb0^$o41jO52-=E%|tF42jqZt6;j~~f~@(uo0_)S-f3keNu<8G#bItKHYr3CTVd&%B2xam zGEdIjOW1fSeu|-2T98H|OtG{riZN!P?I6WY74vNZ3|n3fYkxOg7@Q}eeQ%`K>5s3fn_D5?)2H_@rsOwlMo(k(>7Av8-C*r9p*NuZry{Gz zP3-e;LCE7H8m`W_}7*H zTM$m_b#chQ$=!)+{~M2D`%fN~DgDNyG!|a{_TG3@aQb?0iAzy=#maG2ee0xNWqAZH z`jp`S8^+vz^(hZ~T+|0ARTOFQ4CxO+v8>Rsu`4{McqxO{>s_{5{h`U8oy?mS?Zb^S z&ds1XPrY+(`{td+yBfwNec$h~TSB}Et1CN)4C;}kjxo&KG<;RSp7xM&t&64w%GOG0 z;=6V`WP2A(dM-IfGS|%){$=XcL;=N&Fyn3XTUkzf=gEQVi@Xd>l-AMVspQA~z3y=u z5rG3<%duPYWZw~WCaU~h(3bw3Kc~ytup6RI3Q(@kWZT9xc%f5c5ZmJIEk%)mO6Z<1 zRE~m|ZIyjukUwl3 z`0|`lYKVb>;%Z&@WJ9rG2YI;4bjb#aVT}h7UeI+8N9yqRW%4TkFM66mk%l0I4*h0Z zn{^tBF<-~i!o#vh)l)k6^lq06IzVzbqf-9kaQj*;7nXJ)91Lhe-SfhK(>j45qj4$l2Fg}hFIIY`V$-IJiSYyO&%U? z6Go5JCI%>fDdtF;1NV5@$~Lg*Vc*^s(0#LOD zqfk>>xI@a|ovY8#3#fm`TOBj`fW_v8AnkbmsY=K8PER*Y%mn#^Neq|c{OGwUGSapX znywy^(X+JKDUs72^wQoTii|Sg!p|h~V4L+N(zpG_4yB5NU=>j^M44xy?!mfD3ga<`CT$wBF|HydKjzX~P2HHTjf;5U!M>(lGQ zDPTz$wfj(KNSP2W1o11rmeVGY5LJQbkc?+?+WwnNP8%<*gED?#W!X0C5WtW$7O+Jm zLp-iPQ72`8R&uzB5px|@=}7oBh6cn6oX06io*oBDe$j&o@LiGBxI|;2?MM_kOkj8A6A;c#zeTNg+yhDx%7nZmu z1}&llC*5>fxN%FROd$~IX(7hw_0tl<$`Ve&Tb~hz5#%oFXJCSi5qp~axTDDMpg2N| zc{uo(BP%;#hdV{ zBHy#{Z!#Bg6jj?3kJgW4mXOe~Jju)hU}hVlrTy(-aRSe_^>E*V|dpnMykBi zLWmx+K0`H8%(eHZX2jp00+7)X6?D-h`J=j9(FYwNFbj z6om@-tKT3^GOZTB*AL)s12^&l(Qn^jE_vdvBY6Gnl;XS^tGRjV+y?+WeX@6F*tCsn zvdV}pM3}shRpdVsO!@xlg?fw)yH|vzN^8Iaph*-7(8mujp?3N9RkKKCc;UCL$aN2S z(wOe0bXmhQb&j^A={o={{`|*m%lg`}p>K5IzuKL{q9CsvzVY68a@L*(GiXoQZZK3d zd&hn>VuWp3pvdR<^C!YZc`i`L4ywZ^X1CQa2mm&F=e&Sph|8FKaKT!l!!Y+T4)*pRvI;H4vrUk z>^>MU+T)D;TzPoQODBkC$}k#7mHRRO+!_kjNGREp;QJOki*LMeYh9Qdf^pLXemlSR zuI&cDQL_WP0K8nJFz@CTIwm3YCp`Ix2&SA+X_K@L1;4u@HCm~)XI%lTdyiVehLhDk z_kj$hY237eUQrVsh%e*|Tg6axxl8>(#D|ar@LdCx$nOlsYn7ZI(A?HJ01v!Pdk`=- zP(&wmo0}hn6`zAC!xCp31AX72kh!6|@&os|6}w`T%GK8HR?p8PV(kZR5?d7=l{$tX2P!(x3d zTdct^0x@*&!lCG0&UNm$bQTwvLl67I$z8kYKLQ{F+CDU1MhB#BPJy7`D=ug+!zsa! z_0?2VRX2`LA0~0xFCsCp^#cPqiwy#YN~lV;Lj1iH4^Xoe-SG$r>)mIN*2_Phu)G)x ze=>*&R6|9%!R|Zf-x3<1uVQDlD@)|)HYhtBi<-V59x;R;n3kMOIWn?~@%kl9L?8=a zlx50KQVC#!XgX?q=(opOpUW{PyaIF#29g18J3;#NIsIh618|~m0Xo;bzb+S*fE#oRVCK` z-Z-uT+(@jtTpgTh>i9asafXivh;WSd>S zy`-OwbA6-GZmZmR6vV@ON7)d+8g#3+11LU0z;#Zhul*i78YtH7-?pfbv0d=xL1+7C zbM+ZCP8~zP=(_#bGZMq6-6>xGP@s9Pj0A2V@b>LdT~7y}YvrsTfaD6D6d4cQ;SI&` zTQ~IzC&$cGoZu$q4-{TMRx&@yR$;&k`HZtS$V>ZWrjU2)mQzL<+z(3VnHZ*`3XF1A zUmUg#&3M<10_BrTR}}Ox5wwOtOTMnq`T=gQ3W4Jt8v3Nb?@7ge*IU4j0Xy3f*|Ox( zwWldQDv~O?7+6>MkV4PReZH7EEG7VT=NupCdC*_LU6=8M6Y8Y3IS0kn;51^>llU|d z>Y{2JIR@u+16-bfqZPvi!?Ypswd@_r8KV1Tn^uZ#CegWfjX)pDf_b8 zAK`-O8{t&_LGrzGMx%rSg#&2=)-iaP#_$Zion+;#**B05X4)}gm}ra&-K9l=v1UFKI7<9Rq-VcV02{_B# zF+_u#JGdt9nh8ilO<^kTqzU1iW!7?ReSGTi@7Il$^u#^-C%#bHvBct=m|Vd?D*I!@ zibaVqP|UP6l>Lx0C4d}Q&smm-`qd&2tqaR-lV!gU z>O)8e5J2LHVTT!IOkx+9?v#J5$x63|=ce*R8l?=Yxqm5hap74TY5?vV4p>y$b9#?1 zEZ3N2R@8cT7g2cEv#~F7oTbamC7Y(gbs(jl_?vD{A`Lo{%)$br-{lHWNti7aD+IPk zpDFOuN{9gV-uH78?AgtKG1U!%vzYnT=ubK~e$n8?+tb6eklrF}x5Sd^mYHFKB>!`w z1ZXu|mP_mY)}fiSo_#Fz)irQ&Uu!b3338lrpMJlaZ+0TVwK(7(b-IYpFrZgtbSZpr z(G|al-%ue^D*=tjYh=L^v><6p81>o1Y|3;t7IL7BZh<#&@J{1hOwSQicYLJD6#HO} z0(7s)Ua44d_t^}TsU!+z0KTK#NIDocV4OEgMk-5}l-nUIdi%-GQ8hcXr#Y3=)Fku0 z;BD6W>6#PmI1`!|K@NN1q}2k4lk)%|^T4Z&xt?SM%!w97K8xyUkg~TImSFj|o?Nyq^ zM0W{khrF8$?SsQ*T%NlN0ZC(>^7wVHYGeEw)zk58MeaXk4b5r;WB-z=y*7KCj73c|*K2zg!vIDlBxj@bhXW0i`zBT z)TBq(IK!0<)8u`<$7_2L#X=c-L=1~ar3n{#&Ij2|^+-(Bck%r7mCvoWtbYY)49jlW z&x`|9%4Hdyg_L<#chYYMGfPSyIx{Sqlfu_-uAG#-djtv;#dkpKJna1yYHiqBPkEdf zR?TJ3Tj5_URHL@pK%3V<^n?N482%lksPJxff=lph#x#nhh%vsqz9osE{-?`yDfHrB z^$9}nmXroNe2rp=Aqgzq*k=7r;+-os;!{_R8{6ug**(i8rnub_m}Xo>I%u`NJdiI32 zM(fs!!oyNchEc71Q=+&o8vC?H)m>+0xF9#Yct_Ru$Lc4%uRrclnN9)a8d(pJMH-?j zKR8H2<6b6GCFm>^qmzGBZi$+x2%RO%t14d&shDULu&GI|EK+&Bqk2_%W+txfox;Q_ zM~zeJOhXF^XQaz_Ocp)ej8=~|ikSVD(?!TvW4c2ig$2u|cH5}3C4fh5p-h#j(pAY- zc+U+7w;6rQ4@!VN!Y&WF*&w{RkiAw7SA{Z~k7Wnd`U@0A$7#|1Y>jXuNg8Pl*etu* z0YWBacmTUoxJ-_nLALBcYf65pUV2{8hp%bhUyGceG1F(B3Kt@Li}jWx7%HqnSs!hM zZ-S3*CF)d)t#Qw7hcJE4h%zX$VR!fqfFhvVih%Ch--ReFlb0y=>K{}MQjJ=*E<2~6 zmj==0kJxPeOWSJh74Re9-Z%StvQqNy-o;C3}SC*9<&s{`0Zl?ea=bqhJn<%nkwN6DlBF!D3z@VD(d@v_Gu)Il-GGZ zOiXDEVd%ORAso@528h*ggrVQgIFBBedr!_^J`h_Pc~O0Ug7VQ7*?rpo3BUflkK6dF zw=9~r$;<-t#C>C)du1eSv&*&54Lt@qs9%OKmM^9{!wCKahrf0^EkIDx)r%~O*4cFs z(s?V5t@l{3jQqWl@=zl3)}cDKpPk>@SLbeFR*FS+n%wI6eb#t+?a#xI?@+S~Y&t7% zB(E^-S&ub+kSYWg+_p`cLlJ~=oDIUgMfB=LE`B?=4ufM;nfw-{+^~xb=%GttZC>-AP5>|_?d(SfURy#^;m#)Kp8oj_G%D{X+rO2^|9b4~zxq+otpC-Il7Yc8FF}a> z{bL&L{ctu3uT032=J8@?e%RP&-R8`!u3us%7%3KBQD2BE&sf4g@&~x# z@;LCr7$R$22nMUwTR-X=0f{>GuYQ#1hsyjBcQ0|{#dt1;P3e3}1^Jb&I(f#J0b+T% zrt)$=c_F;d<7tMw97Wl!FttL_+v5n0r`8dKnGDufxS1tCG1Qg|%Xhww>XL5`2}qpM zZb5N;FRm2%1sE9dPbh|11N?l;<2W9x))wCJU-h_4MnvdjY=> zlmwV{vRop|spS@}$3P?b8bc!sJ`IiMQzBBb?<(IXWC;cDV(`XUy!k(dyDVwuS72f= zcZF%l?-qP5z1!C2bn6=8{kr|-Afohch|wpj-Ir_G5?D*Miwr3SP}Skppy{yH8pdkK zsd9(m3+9aAP0MJowd^%t2}ru?CB?muh=6xR3~vqB6rZ$$tYnx zZWa2()UotipwHvR5nw-YeC0*ia$fq5ec_ey?+Y?SGEsn>F+a%f)su8mPWAZtL_AuRs-5m!h+^g5Mfc6K1 z+Fc_)kMA<=z_VSMrOZqv$>;0=1-HW2TgIb7K3t;zABI8)<2Os zXaq2Dc#a{oB%egC_-+~M@@f)^$Yx~7UTg+v!&<|dwAI8Le1)Rnk4FccLRrKUFWhz% zMp3WyvLJ>~MzI=dAJ+3mW}gwE*Zn3JL*)}Oij6Axb|pj_v_ej{j0x^`$p^U?w3(Y zo(x+$g#@2Bf0Deu+%3lXih~Hu^z=hB&ee<_?ddNc3_hm&ZSCe8XUOH`h9=-0jb4s? zW{=HyYK~Hq8USCYrjNu=&@gsd=G+)|n z%eQVGcb9X1JEDkw%(^YIpXK)330%yLxd9VJQdo}=MPisQ)$SQ+SD$ER4GW!U2D!-! zu4eO8$%oPGxA$p;@9wqt5&cG0n(z#Nd=llC&_5a9$ans8a7uY%7UCAMV!Ic(y87qm zFwq(6`0q!y6U`8}fEBr=e8lsow$vsHyp4UHJGW=Du+m-O&DRn)4`hxP(!Qz7m(!QE zPv=){uVJ}Q@e!rF-?UFg5zem&r_TrJa&iKBUxfRn9P`el$a8X_K|jUL3!ihHA7e*2 z0$-aVUiEeRZeEk0e^S4C;_JS+K0X%m8QN+b#7y8eU=*oVvWW2x!Xp%6O;Szb@57r1 zIz`t@w1~7ww1~Axc?d6%36Kkr9mnSsn%c1h(ckRaa42I}RK{>#SllRwNnXUGXK%4; z)7GRyWzyDULM_tPQp3d-|&Fl$KBtOU2buR za0+pXaq`!$_}nV~l|t8G8FnAvJklx9Dbgv_DfY^*Ud%(>Lv%s^JKys71UtFu83xjX z_{-!*^vr~$9!L=0s;Em5-l`b2eT!@O?`@b+2CLx`7oZAk&rEzF23CmH&3IOiXMK{+ zK#8RUNgQc#jA<33q;W_OO4CS}N}EdWO5;e=7s(ZUn#7v?=QdCXP#}?Q+`bIRTqtp# zm6%ZC0N4xSpJZuxhd0fNk4h$;fy7em?u5%*_VtN_6H3Iud?-FTak+c)qwlB+Z+55= z1Ajn0(isonzl-)JID3}Mh@6Lu9%+~u$K{CcaP|F#JM+gMKze8A-7;O$A8|~zHqi_J z+8n95v*%<#N2tY*ZM7NJnDbKp(U=8^b&)hHl%BLT>7q`F)u4ar5*Z`?32)y?oxT)~ z*QQpyqs|9{=Kz^NrzocYr~hzDy=coHpO9I{RmC?-b4mY{c9p)BMv>+#l6o6;E->kM z6*YvHg)uL_9zA1K*!5q|=+c9?D(SL;w<_)OfwwB_ih;NK-jxGyRo+zvZ&eX@ZxP3S z12g$oKx%NDy#4*vY3$N(@u*ILF|DY~_t%z~Rxi^f{}Epm$Ek5J>uI?ZicN3zxmBX2 z7v8f7u83&%ZDSY&6YukQNG=t1^Y|-Zu4ocjZ>1s}6{1EbdGGf%BqrAppid~wif%nX zB;?omZ+*A&v&Nd&Aj2Oy^>s=awEBVCo_y+EuOZllj4*6?k3{h5j;u1MPPdxNi&Lva zbnE=QN0P?-5Ajh!?>zi7YT>p@qfl{aUqiE1q(yRpe3^8ae3@*SLV)x*jyOIi?nZii zf{WbUPVpN>Fqb+oS@a^I(+B0h!I)4Hgh(jWk)@L38U06Sx;o&kD&u%8<2aRs1kkzk zX9R6^4#$VYr?oL^J8gRpK5WwBq_gHY#=}(5t25)x=@|DPgq&yCkGS=^jkpcEjo)=O z8F?9d87{F461+U*roJc30eDB7Ri9DcX`XGd_{wo;9hDxrI67##=+jt}jnr?|=hnq% zg5N%?b@9f{ZJ;s(T6RNCNHh=wadnzmno5zZU3n>(h85m|w}}66_4jY!#Nb zxpcNM8F7%f0}=PEDediWOMlF&hRr+!Cb*XqYD79Lltl zXcoF-tt}WHV%AL)fRG5Lu+R|#GmO*B(iCpr_dOUl@aHxWXs3Z@KkK}ja1PPr#3SzB zz8N$`Q|08CJz5&dTb!GxwjgxG%Z9d+E6V>iSN=TdJnB5_Jixx+ZMey#&2WiDkKG2( zmfWRko^8cGUSJb?Gv@rwU-sj7R!wP&ONaDFfc-uX0~XK9lXE4r1cS%t znmfa$_XpwEJ?0{spvmxC{WMV3nP%?Mj)rxQtq3mDRt!N^Ch6jP&(>H~Dfm_v&J+Flk2J~omzkI~G5T)VC#Cbzp05fkk)931Z@_&1r@&-}EsJQg+cXb_oTo${X||hseOqD` zWD{gP!OMMn+*anw#qrY!^~5q`lU*9%?Q4mFRuLX|Hz&LsA*6)dBW_ ze+*Pm6QC`eyS1(t#bxk7VD2qYaY^w0V*~9|^7b@m6esR|zfILll{ z+2w8C3}H6&DvNz`XdPBp=xtT(h+UFACyM@W??@flL^5jXvg$d+XUxzKAmL~MTL5kA z?>m~|FUe+pLsLTs=xTdojSDnP@At8jbH$$SwUR+HHd zQof2Ck?nE3k(H`S(nzH8Zg+OM^$r*Oi@st|M^g47?!%ioqZoaF|_IF@`OLJChR4?5fVLhj8%z(GOt9 z7)v&>XXmG@@Xk9^?VR)4a;^cRfqxb(M75FmoZp`nVv$n8Z2ytha7o<9l`8*=+^O)~ zaRU2Kax}9Dky^a9dTFDRZyVd>2H9#Bp%AQHqs3flLjU$lAULz8S(OjWcu0f#V9ZtB zdCz#-E$^A(irF>9l1qGm?i+$*)M;EnW2NT}Y_2K8XaA4n^48s0UeEcWWBm6i?!R|- zRBV*e2Ty{VN};zLi{UB~+mx~W76ttbxth%KCVvtu@aZAuy2mU<=}(+^q3do0(EPmD zk#QNhl(`BUd~g@671SC#Ox*1_H((Q(CVBC)-Y3$TYrosokPn)3ULqNL^N)GoNo>|% z#rq#z#J0k+^52;J=N4+FeCTb?*iP6(!O`I!J>j#@-l{!1h7o1fG0||=(Z3t5I?!a! zG7%!}@mT-VT42;}m-!s_{xEp;$Oq%laS+LIKmX;-bPwfL2$7QwO+W*umapnB-&6@0 z@$ZuVeaZQ=lMUJcZANYX5U;D-39qjlXAG?*W8M>gq898Sa}|n^!=JK?K8MBx)G=&7 zOW#I644z_0)nslnY3}n_=NzG$Md*CX&4R8=EUCl?$i5*bXBxX@%9!@CZsvA6_*qG? zrN!M^=5~7cI!Ukk|tw+vaoc+OrKDoRQbh&smS1Z8OAlL6U@R(We!9o$E4VK=VFBl#rH5g+NpW6=K9$Q({b4&s|ihe+D;L|47=D*~pOp;&h z-TEYsGZ81i%uOsA5=nbUIB%wqM;yNYsU;ph=F=U#eiTDChn5253#r5@`D<-_TMK|f zn}KT6BVyUl`ZG5nZjWOxvpk0+voZL3lHfHeXD>;KBW58j0fmF3fW(neJZ4;tsMILS zfw%m;`Drud{H=VR$DHS!``ZuR4c+xMnR;>R$bP>#3yTcl>K+VGde`X=at;^^(sKj( zh-E)6yfFxkX)Wd_lg>gW-X|EG6kx6j&hv8s!W_8zyCcy$-=lzYHA%^|*z+^uvX&wn^W$drIfGAT?R(IOKob*RaT2 z7yU!IkT%~q`sP1UK3ui_bu^rYGIa0PH(+pLfPWpW%Kv|iy>&oT>-PAqA_hoF3xa@v zba#ogbaxCXHN;4lf^>&;C?FsWLw6%B4bmM$ck}K+J)V2-Ip6p9#~J2~6VG1t#QLnY zS3JDrR=t;KvmL8Bx+>3_Y;csd#2o1#3?j~P2U6!VxSGf>9X1~h2|c}LC=!|ACavdV zSeqfm6Cus5*q&L|QzJ<(c5ZX*b;Kx=Ob4c{z@fiZe*ZWOg_~i7J?^;~h79!P870(+ zAqm|X#DPpTozJL+9s&)U=Wc@FDCFYiL&JaomE;{YiYB!FdHFiVy#DA?B*kM%ILgm& z{{AfsR8g#XsvMJ`l_1>zvQ^BYz!AC}humq(()x0TS?0ejCej0%$>_~U$dm7)yOLJy z#KTnph6eHjQExnnY6Q7`T!a|ry+WI=#-L?F(&{|NgG@CtlenVdTDj{(a`-*+7Q^WB z=6Tqjti7231F9B-1TPYA$IkWtow=E(g#iV-EAgmoD{k_p7fQ9C+y2_v(fiyu4qbZ1 zr4}ch6x>W-1;CKY)y0c+PJpMtatbbiWVi&Zlf1ED<(`xaMg`bdXF_ehz#t56GmPSl z0D}m1fDgjmrlzMc>=m!k;XiH!dS4+4WIxS0{%TL8pq8=V#`Sx>DU{90JD-Z9Fh8{m zw+tOX_Ge9&@LzTZw6h(8tEJXUf80{NfCyK{Jhx_JU@V(tK z0gJ$IwOq;l-z|}4Q{9po=KbQRCN%T*^~<~}%?HG4GrKZGzt;?0%3wZVFp8;%T08!; zijCe=xw--6G>>pUELhNyYxE%kAvE!E-iqgp>k8PCa$<=Kcv0jsVZT^&rLI?x?(j?w zcB)!x&gZcw{Xo7&(8%lH0WmPc{L{jcA`wxU&^qN@Wz8XXhJ{~vL%(n#iT9Uj#CT@& z0;Vx1y;C&XWFc5V_n;bauKXE%AfAO<=*h@UZv`vpA53^BY`e<&_iZloWXhIx(Vqz? z8P-^xf#mpx+^&$OR<7{lvM{(>H&@twag>5GgZ{}^|D1aSj){!^b*_M~4CaUHCG`|8 ziYC)&c`aSX9_H7qDaiC2#`vvvALeM&)EH<@@X6x=x#S}^*$bY~FSZdF4+-_G6pT{L zHcp@y2UVE~i`pDl4z~u)Z$W-W@PD03aBZUHPdfK7ao6CRF_!E}yIv8xG~v<8Sv zPvo3&Za^2^#;_M7$kpni{`rtbV3K`GUeybz1-Z!%7%NO`XVS4b$TTp-{k6?g5GVX6yMUk6iS1PYBBy;c5HH%GQGg_BL9h9S?62a>0ZI58q>83v9Rre( zqT1pCBr#9>rSO|)pwhprD`KpZ6;Aq-0hmqe5;!(dSgM22n^BRU^&+M`;K$~Tqtlh# zwfCN){i4S3$b_oZ8N?+?)$c%GVXci`28{yZsAIH3>Nf8}bCh$|_63{$?U{Pyxefs$ zOcBzIiv7uDJt>@2>rH0E&lBiT>>Fr8y9_CVbr+51c&E~rqYIP9JAZM4|EyIm(#;c);lZdf73<^N; zeJ%mskf3kce^!TVfP2)NvOle{k&5tL%SxBDOgwWAQQ_-h)$Qn~`*v-ODTxS2??H4D zc+XL7N0BVMAd<#8t)05gwWjAAW9J}q`Nh6^CZCQ&rl{;QVGpX1`q$O1IH;Hc75anM zHTvO-!?O`6`bn2XTqCJr$DT;aY*Jm5YILEm^wI+?tq5|fS09bt3vMh1KoG-$07G7+ zcT0X54!M4%w8G9+S|LRRx|NC>FZ(=(#4xQ(%byn~)DuL>!z9@u*AQuHMFmHv}HPNvt7q*q3_x$Ivc zma;s$?f+VkPU6C5-ax?3E@a9{QT$H5QBPSB^y_PtOfH59XC=Q9g;I?*`D~;zy7YY* zgQetQZM0eGV=9xQ;_bo2@4g&VF}-)DSyXd-BiUj)(tf-tHmd0_YG(N?%{CI4D?W0c z?YZRJ$2@f`>=bSH*>U#s*uD~vCi>CZ^K;O<@|*V^5=Kxf8)eV=-na%d6gwWE zLF(i5m2sRPybj z86np}bl(?^S0VH0hMaeE3!b-5m+-XT{vet(|8OQr1{DEmrk!zKj6~#nr0d{wv1j$- zwuTo@>$odDV9e6xIqvGp83 zkVhDo)sehrhv9RFw!^gR6Cxv5!4HT60a2NJ-Q`bp-rc&k893%!J9rfky~Vq{W>w| zIx!G5mddIbPgWsfTKA;qJr~>Bi=HH}WrJ8U59dyNU5{_a6sV`XW3dw^9z=w3Z!wJf z61}x3F9Ydi_BPV-ypF`&tUMPRzzpT*c!KSV432cC=Ty=}&GntM|J=Cp-1NTW5I`6l z?EEX1a>HgOMri{r$APdxxhWuA=j*EYI9%mBR^EV;^5)3q2q&f9a0tr;)@sRhUF3&? zkk>*V-{G~daBVwnF7yv;Agv&l%w>1_%^dq7U@IaPEaRVES^gk++^(j9t(1ktTzp$7 zpI*S@NnVsYJMt~qv(L=<>S~&q`Q~GHp0l|r3wc|;{z6`QqS~jeX#Por;JG(}<8;Hv zDAjbb^fN*qYRTSbsuonAi=PU6eZo0RKrHA9DWmV5$Bxf097#$h^LIKn?%Pkpbc>Dp`6gR(xQuLnUo;$H8z@EQ-rK zIOSIT9B^Bu_%+v#s7;AXnPl%>GSy~etB{e~*sk3o0@F}W1lo&d7KOfS_G@oD0)s9> z_D?9JDC8NWYzsK>#U!j^SKO^4v>x3Tw8m`jKR~M_#3t<1QT&+px%=CgY`biKSVc_Q zk%fSbT3lMues+|rp;CkP;G;Vev@PuBPu^qQP($fs0Y?z-DsH^SONp2!{;Xl#H>^>_6Mg5DQPwdc1e6Eem&a zM-AVmfM}L_+|z|ivv?S)bWe(0xa<$*bfoA1?wl|YitKY($ev|zIIOrDxUT?ojbd7m9-C7hP7=L5b3}= zEFt2)i(OY^%=h~@>)PWI%8&uCh_ z4%WO|Huum&`(qdmrmd`aMRn^UAR>Ogt6j`s6CcM9!28|s=Whf3JM4}8+E->54y&IL zr62@*$nKsHzPXSAUh6u(oHn1|J>4U7-*KTPO7R-C^)&%r{}eK#3cP7(mxmPoi5X1z zC+ao>QZ~^hE(;P&i|fJLo7U7AI8r&UIH*7w2hd3j}uO*{?S5u3^!1 zhxHaU-C27KU@yTXfj24gp$hf2_hM=O7oU6RkpSr9 zsoSCrYeZ=D>hsX?*Y9hw#-!d)VU5YWzr-4od;b)BOyNBr_L$OpZR|0X_iotC1}OU9 zNKe`gR=GRc!zOzALd!pvX}+1sn&`kDcsM{cfM0h*zeuLj0O-d()4&p;D$zd#4jevl z7IhZ4!go?|G_lYxzpA#I4xmBo3w4%Fz2X%A)c)_!6$VNa_7W==jUG3T4l#YCYTgCC3rzRKz%(?0zCJVnR+gj03>o1QvdM_4cEx zn5I+rZ_Gi+-k(wvoe!XU=9fOhp4UIK-95fQUY1L@n$pacT3*#5k-Q!mqP>D14dRi4 z-cQ9K9x3VxA=)eHNg^I8=_w-GE9+?@9w~ndV)G+`U7ze8ez%JVB{>GHqX17xJcq!} z1DP@_Db*g}qp)$Zv{UW9OGJM>v$Pt@!{M3qVN)6VWDhV>*hc`61DME(8K9ip?>A;@ z_uh{(7+;LwwDsQk3t=)N-549oG6g@KVt%U8_OOj8l;>iA)|AF?ol^@NoNk8p?#1R| zrqriy$x9*A8?XWpd1!4Hy*F}#_af9meR(P`I(Tv_I{10>((s2BPeyI@Uk5~! z!7-MwDVOnYQ|`cj@5jZ#O`r(v>HxQuYX=5*fl24^`#s^?(C03*&#UKXJLLF^RDn7Z z(T#v^yuQkBp1TnM&!uBqHF1h3;PWNt1|kTeM-(oHn`o zThZkc8|v6#CELXuj4!Ex}z8*UqlN!c*T!J!A-M9CorHc9|o*-|P3k6M@4zcKBK4r;23Ec+RkHGF5 zG^WO|Mqi5s?1(Ute&jL0!&bO_Mvz}`RI@+|Po@L>iF*f24qSlICQIzr?wikeaUwb{ zUei=|LqIIm)yvTZIw?WRPPXwY9cIat1y0G`%azs%dF?0L4O5m|uB>rAqq5*Gme+W_ zV6!>dRWO%Y@NQ7`rcO5=MT`{&1EuN)3i0z8n1+*kL-Zum0=2#am0pu$r-*SboD0!m z)?v!W=KzjjwVU|AkL1qmOw1HJ-_TC*^o^#ivFjWupFvli?F?tV-%|7{Aug{IZ695J zq*n9_4lJ^7Z&KgQRwG$6S5{}=8N{v$u}N@Y)=buvPx&IvoR|_$xNV?Ny;T-j zd6=ENcmEBwM@n8zp_QElWUi)ApBKby#0$@y=>3uuUU?7?K&b!~$UJ4g2qO4UP5=#7 zm{_bSgE?>qKh#?cw^|N0f$aH$4x0?@$yi{KU3&-f6z7Xj-deSk#OO?1tI(ZzZA1e% zTg{`q)bGgkh@gsRgt)d{D5IJMW`Z8?hz@j%C!4nBi+WloU-5ztf5AFG4XFY&$-D*7 zRaT_Ewt)!)mA?(CeDRkdX>vsRLz7LIq))nlH%xgq)0V3-@ETXPo-G#hmRC6OodQxC zq0C&WY@HwjGPtd9R%V?K=HN-$e7ae~gPL!1s)|Q}%a-SH9Cr_Gs5Ci=Nk3+LY+2uy zB)QPJ(-EG(X>{xWU<2bx#(!|ThvzI)&?gm>_Y=Kb+!4D91ZAtQtFLJ@9>%D<+rTP; z{4p*(e_StYUJV=+A35UXlWiEId4g&g6O-7Ab<}$Ehs4~DYR!wa-AA&FpTm~$+a{vA zkEI2q3RUf@1zuL4-(Sf$!33cp&IkOBQjr8QeQXOpHUvczPEQvAgott4HXKM{07qhf zVnMXFm*@#^wJ>GP3(7~vvhBbLsJ{J|;S zHKiRk>KxgQ4o#4txTKT93s7XAbaf0x^_{tq>4P~kH|oDZ#@J8I$-`=`&fjM2`6+6AXmsKvI7 zyZZ23t7Uw;z#vqw&l1;h z%!6G31n*CxgB(a($J;bl{EX6n;TilC8)%7`u%A;&wBC}t3owgR6vmR{9fu%Cs(S(l z>)pvJ-OeCZ7ab;;_wa^)YihZU^&Gp)8>UeVcjzLJj!Ps5EPjQyGJx-p%-QG6VH@*| zyGtOi&tv8dsg@H-z8c>@h8mdPRNU4L_4WAZGt}hr?^Y85<3Uqf>D?`QK}l=e0i-rM zXe-qiEP$uLNF?b2y{Ow3U4hFilI)K)*&}FIRLwdd>M1dfo7FI_<2uG9K8K_kA#n9v z6-+r)hqw_sDsW=*Blf`{@?T1VV?}?2BC?@xmNax#yyMelf&UYZgQH(5rzqNLQ!Hf% z)W@TNrUs1wKLhmb)B<3hKmg{6*2%>cg&yeF7RG-8L@>TlPAcrdx6R69usD5eSe`5U zYIWPEs~R?lvsA0Y$lP6^dN>;Tss75#<09CfvA&`{`Im$5`vDeSK`u#O44<-o8}45@ z2w<{mGii(YP%G&&qUrvTXQQ>GX!heQ5Pz2>-;N|v6yxuAKQOpYN%)>C+mBS85GUcG#Li1uO>iKpG$1C5<``51i7!G4B`FOH;vSjvF z_CWLlIENS@Z*2~M1<^L`jy-=k+`ml+mC9R%xPxtQgr+Kxr$4gQ6_q^HiC)SWMxPJp zsS>WIE;_iLz(5+JXGA*}*B&~v1`I7RiK&>se3G$TXIR(6?OBM&?CgCHlQ93fE5`iu z%6z{m_X7A}DvyB7Z6GaxPZr>f0{A2yfNaM9p&a0gi(e7OHs_+m5pK1Nu}e4(c9elY z=ywJ&!+$+N9qixh+~_^Hk%?Ft0!&MpCID0ff84owv`*B{JU#<~U6(Q?xVd z{jDMGX-#^{|Hbvhd}&lREu=?b|I%2-I`;A2=znlMUMNk7-Ug<mE-wE(K(pLVO@tP&IU#v{Q{U>FfH?)sRB!Kzs zPjnWumcQ}PG>TrvLA#}C6TO^`0E(S2aK3x}Vp>~|!DGAca(IRSTV(PJ!TnQ`}0GJnJF!gJP(ybK8CuzRL&~eko zNeY?z9T1xX1;Wk1G@NZ4Xa&kV7fQ~yPwV!>9TUB{-m{$TITk1(Zavkrli>xWm38pE zsxD?dcl^n4S4#msh5I4HZVtp>mrwFyU1eT1<#9cCRK-q4UUaQ{EVSTE`F($$$Ynn_ ze?1@59?7}>NycMbDkcw_oJZT>CdVf>_ThUfEpxy+_ixJ~_+0V_mVgtFEDOjB$wgn( zPVN2ncGQ%x`Jq?~`4@{N7l|o{P3C2Hi~yD9akpw?cp^8Ca|yuF8i+eQ<-;Wa^WNNm zzsoUNUq?W^$Q=Fhgr%mww|KfHCa1OE>)M$!d&iX0fS{)Bqhvg_e*H^B-X&5XT~A;9 zUyGtY`M{sPh}*`ufF2a%9`%Vg(PDW}?1WaTMdf*>5f#Njleu6l-Gg#OQ2Db9oDTrg zqJ%)>^29cTy0$fPAa7=U0xHk2J+^S0@d;N`bnsJ6?K+Xh+17IM9n z!7tLRmi~#@%>DotA@m9?;KxUsrgSj)V(}m3V$fBzhl05@SCpmkkRL)hY4O>KEJ>cktVAtXIm#?>)p!vSEU0SW}lW`{j2GypWX|An@--{ zlm|e9*py-`4gCVleGO}T1*FSO+X`IDwW5u40R~DL9|mBc8xcUR(B8ho8N|y|4J?G> zYu8Z&%qP_aX6&@DH#ck->lK2j!;M|Zyn7m1g?>RMr24q(zvS~T0UR(I1IeO2ORT?y z!`Y({v@MFR1BFQ1aCpPDqz~6pFP%CtE~++X5Vs`L2=at_004&fDRjepsg}1hUR?1H zE#x)2S({ocqF%4Ocs%D_QOt#>DDz~1ej}FIs zm$uF|3FO)@jXf%0v4j`Mq5MdOO6;mKGL+-vHzo^>d<)cNyuWay|C~}VZK4fM>Qa%Z!FyfSBxt<#O&R2^Y>P_kFnREq5;Gh^0MhN)gcr=tZx%qTuH2(b%x87xJ6X z`tKD!h6$Gs@o>oSxe`wwQ{D0V@}=J!A1-@H!r5ikU-naD3}f0=ya!LVhrpBVU4VwV z)IeAg3w<2t+7D~IIXqz9Dp1*-U8MOvF8RB zhTMS+{k05^%sNE6sV=%h9Z4KP1*(?;jRqIkUUO^d+tZ=c;RchnRuIPazZjwa-V8zs zn>BImZ|X_@HvLnnY)vx&mNWeNCyJsEe7`lY3;h5{ghTTKV6MG5Aoy!@T#`)rP;9NS z-u$(|Ew@G>)F^DMDNmj2nw)N3hQo&-!$k0*A{##El*JL#}V^`25OWAXp#HQU^Q8^t2`t6wW7bcI^2A zx5JE)MB7>Qb|U{cy(}|;?3+j|A2vUCQAWGQJlqL&Me|Vdf4)HEp5P_%JL>&?#R@-c z`Lm2?<`>y$+WfUg)?fN5lbd*#XiU}ibfw8WS|9G(hj;)_ZNb;(JjW@?)ZYj&s(&2> zJy=i=INc=77|U@#ob~5E5v%hO$QBN;xnJMYh_!&~{)-j*_qm8_p0;(z^b{8;>?y;d zaoB>3!HoO{%V;vQSLsMvYq9|T0BECTpntC$zzWf}0yB^ndIl5=GwIwDc}6|JCTY^C z8Oud^`Ho~WDR1IWZIGf>LIWU;Ji-k>LdW7_bR|AA$W+b=p6xMR0sU4+t2yg z0X=zs``IJK?(!Y6kK#-%9|1wY!#%BjIUhvj|C*Mm=E}Dw)>&W-7}b}9&>yzBlTErr zM1k2&ruIfbn!Vnm8;O9ITH4D1Udq9fX$VBxB*kJK$D0ZI_gn}7@X7Gmc^f`E(_P38 zHC?Z2o}|5A#4Fzc@Fgbj+zqtT4u^JV|8<#6HOJe&gIT|GwN_cRq*m!&4-X$SH~bW^ z=~gXVc?IljVNCr^3>DUiOL(Ig$xuSzmy?!a-^}4^;q~PQ13t}H%?{Wm;Xd6(>&R%P zCd=Wc!^3`uEwOMvhONnRELhJ$y zY**ZWu2^L6rXVGEN4MiTWB^gs^_Okb0k)CY3hb+3v~|KOb+b*5IJ&72-JMC7<`d^( zw7a}h@!7tiP{#wh3wV6jJh24koAMnf9HugW!&K4#x{HajPBu94*LA!;(3<*7Hy>}H zHcIFcR~y|re8B|Y>~{ec5eds;#$?7|1hIX_Ybo*&7)^lv6T37=M_JS%A^TNij>zZ_|Cq1BLA=` zlM5}7vql+!&<5X8i4Yw)deLB&?%MR6oGWZ?8BQB{I`@kYg=g6JZ7aA3q zZtvThcN)5x+B37Trb_ z#?59MmyrWi-VXhONOC;kt~Kh@zr?;+b@JSU9_^aCdSO}A!fw+KrH;hmoCSy0 zT_ifxDmU^=_fT586fQOkbtyQ(g`aU?JOfG>O~-wwOw^odyCyio-`0F!8|D2MzcR?= z6M~N&CyuhVGZ%ua!{EU;aV5l_HWJzG|FpP&FdW6qjpnROTVGCY0G_9<6CS!-CS7z6+T|b>WOu*9eAdp2&+i z-c6p&6KxQr`-nLOJRCc3+LK6{QkmjFbm59qw^H5fxi77QJ2TdOD6!~7c3KZ>z%xI2 zGkNc*OWj1D3!<8)kT(PL%R4MIaNeGcZ1M3h>c!PjDs`j=4Jnh1iEhRrRC^+D9VgH- zsF5!g?G>=7&fceYR4(#vxJ;}ha2z4*YrM@%+%p$G)Ur=y^YpOSfgwb8vvoLr)cb|w zN8@rf2I9Lep<<<-?8hQ@+QmHK_devey3`8q*u9=zG6)YedR=oXnL*vA9?Nv2ao=iG z_BbMT}#LmRQoW+U$93ucgIE z-gaxtoOplizPS_V{l=6&puwXmlFxoB?r@ylttnZ~PmQk;dXN<@-#~32;!v(i@2xku{ov)Z^%qckD zL}#TpbJS2at8@>^v4&pGeHD;G*>m!nLmPN7|6PdN16_aS74I9r z1tGtOg_C5h;Vmi{mxEaMeXuYigatxu^q7c%ejtls3k|-ig?FvD(w>}4;676vv?V-c z;6lBFg-@v@!pbFwJru$FCW7r-|F`!ZcVD)726@Nftp=O~<*(jd{EogDOCf+@`$mwc zj8_yqry7}fAj$k$duZ{Sl9uqp-VIb%+v-$8_2~OO%-(BL$wmRG_^`LK%5nF!3keg7 z2?tMHXw$~Nm$f3l$Iy)S?O|RISgDfwOz$fgO6#j#nD}W1U)~)Vl`zUc zPlLz$t|b@3S6Xr#U7X35u!R?oLSNg`giuB=fFMEch*5@!%>0p&b;;r?9#4J1wySZS z$ge{d5x>0Y*3Rx|fij{!S$+vazLR|?`QD4DU0e~UptcHT4W>8O>MPKPjEBVjp=A8j zmz9B>6H!)URK&yV_inqrr$#s^Zowy(wvHZ3Ziur78Ggu6*L_SJ*Y$RwO^bqki@?!1 zZ__AMi%a*stL}58U@v}mIu1b+L zNW3-V@WtA#TQwbfU_HhvY0*D=>w}=Vc+z_f1jtJS*=B*!x{WSDX;XHT+n8KyJgYqq zge-Wg#G%r+@8>7lp|Ql{i9^BqNAhwB-)U!D^q+`Vts0<8r!}aeYD>mj;+(J(R{1Qq z4Xp+*(`yBPV?}Yb^HRbp`Px&5f-uvXME9gveI}i`>&~QNsHkE5U4?NJhi5FL zPO3BHadQ*TtjAu!)FxDoS!Awmtv7r(S1FH3d|4SN$FqJBQXlMKO0dKNSK}#eN~>F}@^~hSk*HO1WD7jk6ek~V z-$xtTo_c5a-k6Po!6!wNTaeil|6AmXKKWXP@we*COu-MKD7WsT zLbpssmHWj_467MpdDhHAq!1cZi6FEHje*q7%N|zE2B=E7Zx)#D`)GfnF5T)f2$9;a z=WMe=8)X`+>?C>E*btv zV3T*zSPFY#aFK#Ol{~MD4#$tek1d#7A093&>g@#V zgfI{qkG?Li?mmfoo79oO2%0At^Up8Qk?)@<#tZ4oUB$+!$B0e_X;(>=VH?SKga#hC z7rQ>HF;bJNF^s^Pwuqz9VU+OVtdb>48$UqP?v{LCIw~UaNQsNCDfdO{!$aKty0UuY zv57#g(=VlGP3jJ6phUTW(%#oF>GEBCZv;b)u!d?N%6%aFLL})_p@}0_lYpe$8*B9?ocF4ohsbx)-(#uG29IF1EhC(3Px@ zxdQCt*J?9ravVINg5I{P%5-qcUbsUYsr?rA)_L6&e*Sx{FCv8Ln-j-!sTPofrr@a;+}g7b+_dl2(g^YlO`!Scs!};~Ji*|=QNO(n zCd%yQ__lGpA7s~=wdqI&txpoO@N-QROYHIkb(7Is1QjKi)KKl)n?EXQawf z&FYaGw(ch-9M`wc&1BR<5Zlk6h29pjj!>8vwa575Wi9&NI|4#Z*R;4!{}Qq*Ve zaLANvZ)Yd$=+MU>bl+#@SU<8ZNr^SsTb*FI5s5zA7u$8{7MpOeoWamtHjy4V$k3fq z;XFC>p?jqwn{qBgw})9XRU&er)025?JxsLDgXr3hV@do$EhhaEA|tO#<)13y68ALc zhCcb^lZ9ax6#pqB3*Rh6<5P4Nwps9xPi0w8%|f|9#V-%N{xLmSaPu9C$(@8pj+4Tr za+{F>XxjIOMGocMTuW|2Nz6VxF_`+tam=O9V-5mC88}+rjGrcK-m9P@IFZ>-*qq4z@fgWX z|7E;rX)06;-_WtBNS!W4X{ia|OI#G>M}J<48b_1G|8Xf&nL^7vx?V%aXG>FDKFSX6 zPWx=)It-kyO zHz23&K6WZiB4*dVjLbwgDy~f&AN@Rpd-m-&LYwf|!H1B}M5dK)L`a7g(`Yveq-&Dt zxEldl#VJshZpQ336Eb=_(>s4aR$)rLbJSErLV@nP8GU}#g6kG&nh?l|8AwVGZ5MG} ze#d1PazbJtL)lFQX-nJ}!s#Z*{Tge&>{GeqyAkU`&&-%pmlQ8GQ=YRdVMbiRONO2j z=W8m4QF|CXUNx2%B4m~@K1WKQ$!DCxrH2|vwj9gB$dy^p>d5G3MY3?lHOdexNw4s8 zh-l+BRVGBS%G>n#9G0p*2_dF-9v`^l>mLcB?AjA5tva3qCFQrr;|KH~8?o?cO)QnP zZ?N3&CV{jNvQT!f#TiMOB=()I;U?0@(JaSTg1}Ca+{?*jJ#|vDfHgndG z{CK6OgT+=$S+Bhdt^WPW7=GXu~N>`A>{3WfA{SfNUs)SO!vBP$jsB}ROW7%TsVlWA$}J8pDD zfn!zDM`#@$Hi(gFu>66`s5cfe9pO1h4*4k@Nc$uUT6eZpOm}Y^=E#zpi&P*C#KXm- zNOEC!>-Ruv{aZ~d`qtiYEt1}ROJI5;@HO-bj4?okR(>48a&)D^R@{B#!<_fzP!@yX zjg8o74wEie1@#BFKnt56#`dw=#-ZaTsIfxJeq6>fv5Lz=#^7FBD-27n znH%p|pClh-F_xp%{afsNT*Fu3~!pQ|`6zOE?%E;l= zg=_oQ1wnud3Q~qb4K#EV!;WdP(Cz7eU(J7rl@f<8K51~zQ?b_wX*r_u*VQag4ndst zRFbLm!r50dgGA)e22kS zA8qIr!iw?=eOxkrPKHT)$%VVUO>);D%V9ZiMeLbL{!4uN-9)&%aR|%*`Xzu^hsz~1 zOxm*7#+m5qQ#y$4bI_+uSc)!8IiNio)|N5j3#5M;TT>#lNjD%7EoRkjHc0a%vqLxE zenak=eTP*`+x26U*2BUU ze!6CmU7$ahX_(Hqs#U98Z52myO8<*y?l7tA6?;{^sa;?axM-9PwMwLvSQ^56MZVJm z^WZuZxH+o1d3Y^T@(dT3~d+1a%vAuA&xMZLpO=vEYh+Jt^i|bvMz*}|6 zG2VBp(xIeaV(OCetaQCUg*5T=`107+hKdr+%JP~>0~V>S3)X-3OG{tk_AoaVL>0J z-Z{##sP8z3hZE13B$4cve6|y$Q5O;|`_yqb+>W+TswC_DP>=Wd5I32mL45eOdD&e- zJ5_eMg-8WzK@^>A?*anE5^GZVQ+f0QH$f+9jV5vJrZA%pd?|9xhgs6d@^bQCSBDWC z&ax9&xVuOqqhqk?J|I|}FE$`YvUqs1keAX*$4JbTkMvecT^=Z>cxn%1eOfmiev3Y- z2Z89=KCD!jUFyr7VV&A`0p!YuA%$}hp~B!I*-$QoxiAVK61A;?Vdi2u;@ zS@3mOj!mP@=6F6YqLJvHb(8>w(7bN&)DQQ?!VyY?zVD|9N)3iObYyr=2P)bJW5uW^ExmGe}lk`Fc5qVAXPpuaDwf4 zk_Z>^vTzQ?J2YPYiL|ZDI^RO=z3i5 zErqV{6Q>AVp8e|B+UI}<^k_YsNx8lpzS<_16{tU-I~KS)1s{M8;2m2X&n_ket`0qh zV+!isPRskzw#ByjFV80I(yXV}t2RS1YJ_(NtBG^>a)vKY*XCk$JudbK3k0rk%-iJl zcNYpXs)l*%&Gr{xQ^*0mSG!ml@PmEGSsaS<5S+6zt&&j#Te@*lu&7?0q*PVjnj1Y?}h$mULaUy_)L_I?YdqIa!XB7KS*# zpD$e@8LZ%g*b;Dv_74qkj9$ zc>(Zf@CI??sLO!t7N2wFmEy??rC2-1`g9JGInX_H3#WeExMW!6Au`dnGh-FlTGVk4x zXqEKR?$BV5eQi)cETb+|d0&eZr{;xM{NU;euL`_q@U6CoA+(&#(&3X^+Lm;Fm`s>V zc<)aCvRd^^pkXH|(6I9#>Nog)(fH_K>aTcJmO$ysLY6#^kk(U?z2W|JTA!0OsN{Pi zBnw47ppXSiEvrEkQH8ot@%fi z5{|hLDwcY0iIl78$A3Bv7sD$hcp1y_3*oP>b|sw#Bh&Kl|((c0}b4Aanq4+lX9o~(D< zI+BmCB6>JSOprT1TacJW(FsQuH-DZWb0pO6q&Km#E9p=bOX3aTcOvxOiw#D~Rq~TL zrJz;zQ!(Od@gR4>R?7{GWBW!@-4OW{mG4Krd}|;emy5YDmn{YX5UR-8bS*u$iY#0+ z;Gx4LgUvgp!EgQD2y%gjs{d2sb zhJs$)tCEgmx9cLmj5haf&YGqVAZStt+hJ(RbwPn@ikrF zj{_)S3BKckrS?-Ph5rv$F-Sb)!TWaVTYK2~?DmpL5J~!Qa1~I%qQ=wu1Al-+YBf}gUh!5g}w{;V$IQoHMYh3#3ZI`N8W$_^N}~y8Xfk>^@fnm2OYry^gHN(FJY)o52bKeoKk{ zx~4z&#!bTv_ePO_d5Cvb)XYSrB7IYY0xDGg{YRt?CB3hAG_lok8gvkGXbROy6wT2^ z(TJyQqmM>a_(LXN4hCZPv&y$zIb*28(ajD3-AtgZs9IhpU+_uDt@u?M@;6!mXpx7P zg0G7=vAUuxP0)^GAgT|&L0TCJ7HifLw1;Kfio0fCLBn{@!;Gf2OD&ebnq?6mx~d

!7i!h+w2N>Sl(A98qUnm(brLx1jh+K`(M)e_1 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

*$1o>Q6wYRA-28ku3 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 output conditional probabilities, scaled probabilities - - 2) conditional_probabilities_update: input start node, list of probabilities --> output conditional probabilities - - 3) single_simulation: input start node, adjacency martrix, list of node nanes, update boolean --> output graph generated - - 4) single_backward_simulation: input start node, adjacency martrix, list of node nanes, update boolean --> output graph generated - - 5) monte_carlo_sim: number of iterations, plotting boolean, starting nodes, adjacency matrix, list of node names, random seed - booleanm, midpoint boolean --> output average probabilities, similarity between average and conditional probabilities - - 6) single_conditional_prob: inputs first probability, second probability, midpoint boolean --> output conditional probability - - 7) multi_cond_prob: input parent nodes, current nodes, probabilities, midpoint boolean --> output conditional probability - - 8) bayesian_table: input adjacency matrix, current node, midpoint boolean, node names, alternate probabilties boolean, - alternate probabilities, multiple turbines boolean --> output parents list, probability distribution table - - 9) write_bayesian_probabilities: input adjacency matrix, node names, probabilties, multiple turbines boolean, midpoint boolean calculation, - number of iterations --> writes probability distribution tables to Excel file (nothing returned) - -10) probability_over_time: input failure rate, time --> output probability of a failure at time t - -11) bayesian_inference: input adjacency matrix, node names, indicator nodes, evidence nodes, hypothesis nodes, probabilties, hypothesis -boolean, printing boolean, multiple turbine boolean, parent or child indicator, twoTurbine boolean --> outputs inference probabilities - -12) backward_bayesian_inference: input adjacency matrix, list of node names, array of nodes to start with, array of evidence nodes, - array of hypothesis nodes, probabilties, boolean for indexing into the right values of the array, boolean for multiple turbines, - parent or child indicator, boolean for twoTurbine method --> output two arrays of inference probabilities (one normalized and one not) - ------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------- - - - transition_natrix Documentation - ---------------------- - This method inputs an adjacency matrix and vector of probabilities (corresponding to the probabilitiy that each - failure will happen). Since the data we have does not tell us the probability that any two event will happen, we - find the lower and upper bounds of what the conditional probability of every pair of related events (related indicated - by the adjacency matrix). We then take the median of these values as the probability that the second failure will - occur given that the first already occured. This method returns the array of these conditional probabilities and - a scaled array of these probabilities (scaled so that the sum of the columns is 1).''' - -def transition_matrix(arr, probabilities, mid_point = True): - arr = make_binary(arr) # Array to show which failures lead to other failures - nodes = diagonal_nodes(arr) # Matrix with numbers on the diagonal - - bounds = [] # Initialize list of upper and lower bounds - transitions = np.zeros(arr.shape) # Initialize matrix of conditional probabilities - - for node in range(arr.shape[0]): # For each node... - children_bool = arr[node] @ nodes # Find the children of the node - children = children_bool[np.nonzero(children_bool)] - - parent_probability = probabilities[node][0] # Find the probabilitiy of the node occuring - lower_bound = [] # Initialize lower bounds of all children of the node - upper_bound = [] # Initialize upper bounds of all children of the node - - for child in children: # For each of the node's children... - child_probability = probabilities[child - 1][0] # Find the probability of the child occuring - - # The lower bound is the probability of the child occuring (i.e. child and parent are completely indepedent evens) - lb = child_probability - # The upper bound is the overlap of the child and parent probabilities (i.e. child and parent are completely dependent events) - ub = min(parent_probability, child_probability)/parent_probability - - if mid_point: - tm_val = (lb + ub) / 2 # Calculate the midpoint of the two bounds - # print("Midpoint = True") --> for debugging - else: - rand_val = np.random.rand() - tm_val = rand_val * (ub - lb) + lb - # print(rand_val, tm_val, tm_val == ((lb + ub) / 2), lb, ub) --> for debugging - # print("Midpoint = False") --> for debugging - - transitions[node][child - 1] = tm_val - lower_bound.append(lb) # Append the lower bound to the list - upper_bound.append(ub) # Append the upper bound to the list - - lower_bound = np.array(lower_bound) # Convert lower and upper bounds to numpy arrays - upper_bound = np.array(upper_bound) - - midpoint = (upper_bound + lower_bound) / 2 # Calculate the midpoints for all the children of the parent node - - bounds.append([lower_bound, upper_bound, midpoint]) # Append the lower, upper, and midpoints to the list - - transition_matrix = transitions / np.sum(transitions, axis = 0) # Scale the conditional probabilities - transition_matrix[np.isnan(transition_matrix)] = 0 # Replace any NaN values with 0s - return transitions, transition_matrix # Return conditional probabilities and scaled conditional probabilities - -''' conditional_probabilities_update Documentation - -------------------------------------------------- - This method inputs a start value (indicating the starting node) and a list of probabilities that each node will - occur. We then compute conditional probabilities of all the nodes given the starting node. This method returns - the list of calculated conditional probabilities.''' - -def conditional_probabilities_update(start_arr, probabilities): - # Create vector of the parent probabilities - for start in start_arr: - parent_probability = np.reshape(np.repeat(probabilities[start][0], probabilities.shape[0]), probabilities.shape) - - # Calculate the conditional probabilities (using the midpoint method described in transition_matrix() method) - conditional_probabilities = (probabilities + np.reshape(np.min(np.hstack((probabilities, parent_probability)), axis = 1), probabilities.shape)/parent_probability[0])/2 - probabilities = conditional_probabilities - - return conditional_probabilities # Return the calculated probabilities - -''' bayesian_probabilities Documentation - -------------------------------------------------- - This method inputs a start value (indicating the starting node) and a list of probabilities that each node will - occur. We then compute conditional probabilities of all the nodes given the starting node. This method returns - the list of calculated conditional probabilities.''' - -def bayesian_probabilities(parents, child, probabilities): - for i in range(len(parents)): - rand_val = np.random.rand() - - - -''' single_simulation Documentation - --------------------------------------------- - This method inputs a starting node, adjacency martrix, list of node nanes, and a boolean that either uses the method of - updating conditional probabilities based on already visited nodes. We then compute the failure propagation and graph the - propagation via Networkx. This method returns the graph generated.''' - -def single_simulation(start_arr, arr, nodeNames, update = False, plot = False, rand_seed = True, mid_point = True): - monte_carlo_array = np.zeros(arr.shape) - probs = np.zeros((arr.shape[0], 1)) - - # Use the probabilities from the COREWIND failure rates - probabilities = np.array([0.0195, 0.0195, 0.013625, 0.0055, 0.0175, 0.2075, 0.001, 0.001, 0.001, 0.093185, 0.001, 0.001, - 0.027310938, 0.033968125, 0.033968125, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, - 0.0205, 0.0205, 0.02, 0.01, 0.01, 0.233, 0.288, 0.543374, 0.1285, 0.01, 0.01, 0.01, 0.015, 0.0155, - 0.015, 0.0155, 0.015, 0.0155, 0.015, 0.33, 0.025, 0.025, 0.025, 0.025, 0.025, 0.105]) #0.01375, - probabilities = np.reshape(probabilities, (arr.shape[0], 1)) # Reshape these probabilities into a vector - - G = nx.DiGraph() # Initialize a graph and a list of modes and effects for plotting nodes in different colors - effects = [] - modes = [] - - if update: # If you want to update the probabilities, update the probabilities given the indicated starting node - probabilities = conditional_probabilities_update(start, probabilities) - transitions, tm = transition_matrix(arr, probabilities, mid_point) # Compute the probabilities for all linked nodes - - if rand_seed: random.seed(16) # Use a random seed to get the same results (yet random results) each time - - adj = make_binary(arr).astype(int) # Make a binary adjacency matrix to determine the children of each node - nodes = diagonal_nodes(arr) # Diagonal array of node indices + 1 - - nodeList = np.reshape(np.repeat(False, arr.shape[0]), (arr.shape[0], 1)) # Create a list ot determine which nodes have been visited - gens = {} - queue = [] - - for start in start_arr: - G.add_node(str(0) + ": " + str(nodeNames[start-1])) - nodeList[start-1][0] = False - queue.append([start, 0, 0]) - gens.update({str(0) + ": " + str(nodeNames[start-1]): {"layer": 0}}) - # Determine if the source node is an effect or a mode, and add it to the correct array - '''if source < 49: modes.append(nodeNames[source - 1]) - else: effects.append(nodeNames[source - 1])''' - if start < 27: effects.append(nodeNames[start - 1]) # Add the start node to either the effects or mode list - else: modes.append(nodeNames[start - 1]) - - while len(queue) > 0: # For each node in the queue... - current = queue[0] # Get the node from the front of the queue - children_bool = adj[current[0]-1] @ nodes # vector of zeros and child names (numerical names) - children = children_bool[np.nonzero(children_bool)] #list of just the child names (numerical names) - - for child in children: # For each child of the current node... - if nodeList[child - 1] == False or gens[nodeNames[child - 1]]['layer'] > current[1]: # If the node has not been visited... - if np.random.rand() < transitions[current[0] - 1][child - 1]: # If the random value is less than the probability... - monte_carlo_array[current[0] - 1][child - 1] = 1 - probs[child - 1] = 1 - G.add_node(nodeNames[child-1]) # Add the child to the graph - if update: # If updateing is desired, update the probabilities with the addition of the child node - probabilities = conditional_probabilities_update(current[0]-1, probabilities) - transitions, tm = transition_matrix(arr, probabilities, mid_point) - - # Determine if the child is an effect or mode and add it to the correct array - if child < 27: effects.append(nodeNames[child - 1]) - else: modes.append(nodeNames[child - 1]) - - # Add an edge between the current node and child in the tree we are building - G.add_edge(nodeNames[current[0]-1], nodeNames[child-1]) - - queue.append([child, current[1]+1]) # Append the child to the queue - nodeList[child - 1] = True # Change the status of the child to say we have visited it - gens.update({nodeNames[child-1]: {"layer": current[1]+1}}) # Add the child to the layer dictionary - queue = queue[1:] # Remove the current node from the queue - - nx.set_node_attributes(G, gens) # Set the layers of the graph - - # Plot the graph - if plot: - pos = nx.multipartite_layout(G, subset_key='layer') - nx.draw_networkx_nodes(G, pos, nodelist=effects, node_color="#98c5ed", node_size=2700, edgecolors="#799dbd", node_shape="s") - nx.draw_networkx_nodes(G, pos, nodelist=modes, node_color="#fabc98", node_size=2700, edgecolors="#c89679", node_shape="s") - nx.draw_networkx_labels(G, pos, font_size=10, verticalalignment='center_baseline') - nx.draw_networkx_edges(G, pos, arrowsize=60) - plt.box(False) - plt.show() - - # draw_bfs_multipartite(arr, nodeNames, start, "child") # Plot the graph when all probabilities are one for comparison - return G, monte_carlo_array, probs # Return the graph - - - - -''' single_backward_simulation Documentation - --------------------------------------------- - This method inputs a starting node, adjacency martrix, list of node nanes, and a boolean that either uses the method of - updating conditional probabilities based on already visited nodes. We then compute the backward failure propagation and graph the - propagation via Networkx. This method returns the graph generated.''' - -def single_backward_simulation(start_arr, arr, nodeNames, update = False): - # Use the probabilities from the COREWIND failure rates - probabilities = np.array([0.0195, 0.0195, 0.013625, 0.0055, 0.0175, 0.2075, 0.001, 0.001, 0.001, 0.093185, 0.001, 0.001, - 0.027310938, 0.033968125, 0.033968125, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, - 0.0205, 0.0205, 0.02, 0.01, 0.01, 0.233, 0.288, 0.543374, 0.1285, 0.01, 0.01, 0.01, 0.015, 0.0155, - 0.015, 0.0155, 0.015, 0.0155, 0.015, 0.33, 0.025, 0.025, 0.025, 0.025, 0.025, 0.105]) #0.01375, - probabilities = np.reshape(probabilities, (arr.shape[0], 1)) # Reshape these probabilities into a vector - - G = nx.DiGraph() # Initialize a graph and a list of modes and effects for plotting nodes in different colors - effects = [] - modes = [] - queue = [] - gens = {} - - if update: # If you want to update the probabilities, update the probabilities given the indicated starting node - probs = conditional_probabilities_update(start, probabilities) - else: probs = probabilities - transitions, tm = transition_matrix(arr.T, probs) # Compute the probabilities for all linked nodes - - # random.seed(16) # Use a random seed to get the same results (yet random results) each time --> feel free to comment out - - adj = make_binary(arr).astype(int) # Make a binary adjacency matrix to determine the children of each node - nodes = diagonal_nodes(adj) # Create a matrix with node names to determine the children of each node - nodeList = np.reshape(np.repeat(False, arr.shape[0]), (arr.shape[0], 1)) # Create a list ot determine which nodes have been visited - - for start in start_arr: - G.add_node(str(0) + ": " + str(nodeNames[start-1])) - nodeList[start-1][0] = False - queue.append([start, 0, 0]) - gens.update({str(0) + ": " + str(nodeNames[start-1]): {"layer": 0}}) - # Determine if the source node is an effect or a mode, and add it to the correct array - '''if source < 49: modes.append(nodeNames[source - 1]) - else: effects.append(nodeNames[source - 1])''' - if start < 27: effects.append(nodeNames[start - 1]) # Add the start node to either the effects or mode list - else: modes.append(nodeNames[start - 1]) - - while len(queue) > 0: # For each node in the queue... - current = queue[0] # Get the node from the front of the queue - children_bool = adj[current[0]-1] @ nodes # vector of zeros and child names (numerical names) - children = children_bool[np.nonzero(children_bool)] #list of just the child names (numerical names) - - for child in children: # For each child of the current node... - if nodeList[child - 1] == False or gens[nodeNames[child - 1]]['layer'] < current[1]: # If the node has not been visited... - if np.random.rand() < transitions[current[0] - 1][child - 1]: # If the random value is less than the probability... - G.add_node(nodeNames[child-1]) # Add the child to the graph - if update: # If updateing is desired, update the probabilities with the addition of the child node - probs = conditional_probabilities_update(current[0]-1, probs) - transitions, tm = transition_matrix(arr.T, probs) - - # Determine if the child is an effect or mode and add it to the correct array - if child < 27: effects.append(nodeNames[child - 1]) - else: modes.append(nodeNames[child - 1]) - - # Add an edge between the current node and child in the tree we are building - G.add_edge(nodeNames[child-1], nodeNames[current[0]-1]) - - queue.append([child, current[1]-1]) # Append the child to the queue - nodeList[child - 1] = True # Change the status of the child to say we have visited it - gens.update({nodeNames[child-1]: {"layer": current[1]-1}}) # Add the child to the layer dictionary - queue = queue[1:] # Remove the current node from the queue - - nx.set_node_attributes(G, gens) # Set the layers of the graph - - # Plot the graph - # pos = nx.multipartite_layout(G, subset_key='layer') - # nx.draw_networkx_nodes(G, pos, nodelist=effects, node_color="#98c5ed", node_size=2700, edgecolors="#799dbd", node_shape="s") - # nx.draw_networkx_nodes(G, pos, nodelist=modes, node_color="#fabc98", node_size=2700, edgecolors="#c89679", node_shape="s") - # nx.draw_networkx_labels(G, pos, font_size=10, verticalalignment='center_baseline') - # nx.draw_networkx_edges(G, pos, arrowsize=60) - # plt.box(False) - # plt.show() - return G, nx.to_numpy_array(G), probs # Return the graph - - - - -''' monte_carlo_sim Documentation - --------------------------------------------- - This method inputs the number of iterations, a boolean for plotting the average graph or not, an array of the nodes to start with, - an adjacency matrix, list of nodeNames, boolean for a random seed, and a boolean for using a midpoint calculation. We then generate - a graph with failure probabilities for the number of iterations and find the average graph and the probability that each node - is in the graph (the number of times the node shows up divided by the number of iterations). Lastly, we calculate the similarity - between the probability of the nodes in the graph (that we just calculated) compared to the estimated probability calculated via - conditional probabilities. We return the first list of probabilities and cosine similarity between the two lists of probabilities.''' - -def monte_carlo_sim(num_iterations, plot, start_arr, adjacency_matrix, nodeNames, rand_seed, mid_point): - # Initialize the adjacency matrix to trrack which nodes are in each graph (which we will then calculate an average from) - adj_matrices = np.zeros(adjacency_matrix.shape) - - # Initialize array for probabilities to track average probabilities for each node - probs = np.zeros((adjacency_matrix.shape[0], 1)) - - # List of probabilities for each failure happening - probabilities = np.array([0.0195, 0.0195, 0.013625, 0.0055, 0.0175, 0.2075, 0.001, 0.001, 0.001, 0.093185, 0.001, 0.001, - 0.027310938, 0.033968125, 0.033968125, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, - 0.0205, 0.0205, 0.02, 0.01, 0.01, 0.233, 0.288, 0.543374, 0.1285, 0.01, 0.01, 0.01, 0.015, 0.0155, - 0.015, 0.0155, 0.015, 0.0155, 0.015, 0.33, 0.025, 0.025, 0.025, 0.025, 0.025, 0.105]) #0.01375, - probabilities = np.reshape(probabilities, (adjacency_matrix.shape[0], 1)) - - # Run each simulaiton - for i in range(num_iterations): - arr = copy.deepcopy(adjacency_matrix) - G, adj_mat, prob = single_simulation(start_arr, arr, nodeNames, rand_seed = rand_seed, mid_point = mid_point) - adj_matrices += adj_mat.astype(float) - probs += prob - # print(i+1) # Debugging --> feel free to uncomment - - # Calculate average graph and average probabilities - adj_matrices = adj_matrices/num_iterations - probs = probs/num_iterations - K, abc = matrix2Graph(adj_matrices, nodeNames, True) - - # Calculate similarity between average and conditional probabilities - v1 = conditional_probabilities_update(start_arr, probabilities) - v2 = probs - - # Plot average graph - if plot: - draw_bfs_multipartite(adj_matrices, nodeNames, start_arr, "child") - return v2, cosine_similarity(v1, v2) # Return average probabilities and similarity of average and conditional probabilities - - - - -''' single_conditional_prob Documentation - --------------------------------------------- - This method inputs the probability of first failure, probability of second failure, and boolean for using the midpoint calculation - or not. We use these probabilities to estimate the conditional probability between the two failures. We return this conditional probability.''' - -def single_conditional_prob(pa, pb, mid_point = True): - lb = pb * pa # Lower bound for conditional probability - ub = min(pa, pb) # Upper bound for conditional probability - if mid_point: - overlap = (lb + ub) / 2 # Find midpoint between the lower and upper bounds - else: - rand_val = np.random.rand() - overlap = rand_val * (ub - lb) + lb # Find a random value between the lower and upper bounds - return overlap/pa # Return the conditional probability - - - - -''' multi_cond_prob Documentation - --------------------------------------------- - This method inputs array of parent nodes, current nodes, array of probabilities, and boolean for using a midpoint calculation or not. We - calculate the conditional probability when the node has multiple parents (i.e. probability of current event given the parent events). This - method outputs the conditional probability.''' - -def mult_cond_prob(parents, current, probabilities, midpoint = True): - if len(parents) < 1: - return probabilities[current - 1] # If no parents, return the probability of the current node - - # Create list of parent node probabilities, with the probability of the current node appended to the end - parents_sub = [int(parent - 1) for parent in parents] - parents_sub.append((current - 1)) - parents_sub = np.array(parents_sub) - parent_probs = probabilities[parents_sub] - # Initialize denomenator - denomenator = 1 - - # Calculate the single conditional probability for pairs of events until there are no events left - while len(parent_probs) > 1: - if (not isinstance(parent_probs[1][0], float)) and len(parent_probs[1][0]) > 1: - parent_probs[1][0] = parent_probs[1][0][1] - lb = parent_probs[0][0] * parent_probs[1][0] # Lower bound for conditional probability - ub = min(parent_probs[0][0], parent_probs[1][0]) # Upper bound for conditional probability - if midpoint: - val = (lb + ub) / 2 # Find midpoint between the lower and upper bounds - else: - rand_val = np.random.rand() - val = rand_val * (ub - lb) + lb - parent_probs = np.vstack(([val], parent_probs[2:])) - if len(parent_probs) == 2: - denomenator = parent_probs[0][0] - # print(parent_probs[0][0], denomenator, parent_probs[0][0]/denomenator) - return parent_probs[0][0]/denomenator # Return the conditional probability - - - - -''' bayesian_table Documentation - --------------------------------------------- - This method inputs adjacency matrix, current node, boolean for midpoint calculation, list of node names, boolean for alternate probabilties, - array of new probabilities, and boolean for calculations among multiple turbines. We calculate the probability distribution and format it - into a table. This method outputs the list of parents and the probability distribution table.''' - -def bayesian_table(adj, current, midpoint, nodeNames, pvs = False, prob_vals=None, mult_turbine = False): - nodes = diagonal_nodes(adj) # Diagonal matrix of node's numerical names +1 - adj = make_binary(adj, 0.5) # Binarize adjacency matrix - - parents_bool = nodes @ adj[:, current-1] # vector of zeros and parent names (numerical names) - parents = list(parents_bool[np.nonzero(parents_bool)]) # list of just the parent names (numerical names) - prob_table = np.zeros((2 ** len(parents), len(parents) + 2)) # Initialize the array for the node's conditional probability distribution - - # *** Code in development for multiple turbine bayesian tables *** - arr_nonbinary = adj.copy() - num_parents = len(parents) - - '''if mult_turbine: - for p in range(num_parents): - if arr_nonbinary[parents[p] - 1][current - 1] > 1: - print("Unlocked!", parents[p]) - parents.append(parents[p])''' - # *** End of code in development ********************************* - - for iteration in range(2 ** len(parents)): # For each combination of parents having either failed or not - if pvs: # Determine the probabilities being used - probabilities = prob_vals.copy() - else: - probabilities = np.array([0.0195, 0.0195, 0.013625, 0.0055, 0.0175, 0.2075, 0.001, 0.001, 0.001, 0.093185, 0.001, 0.001, - 0.027310938, 0.033968125, 0.033968125, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, - 0.0205, 0.0205, 0.02, 0.01, 0.01, 0.233, 0.288, 0.543374, 0.1285, 0.01, 0.01, 0.01, 0.015, 0.0155, - 0.015, 0.0155, 0.015, 0.0155, 0.015, 0.33, 0.025, 0.025, 0.025, 0.025, 0.025, 0.105]) #0.01375, - probabilities = np.reshape(probabilities, (len(probabilities), 1)) - # print(probabilities) - - true_false_array = [] # Iniitalize to record which parents have failed - - for p in range(len(parents)): - probabilities[int(parents[p]-1)] = abs((int(iteration / (2 ** p)) % 2) - probabilities[int(parents[p]-1)]) # Determine parent probability (given if the parent has failed or not) - true_false_array.append((int(iteration / (2 ** p)) % 2)) # Append whether or not the parent node has failed - prob_table[iteration][p] = int(iteration / (2 ** p)) % 2 # Append whether or not the parent node has failed - - # if mult_turbine and nodeNames[int(current - 1)][:3] != nodeNames[int(parents[p] - 1)][:3]: #and p > 0) and parents[p] <= parents[p-1]: - # probabilities[int(parents[p]-1)] *= 0.7 # If there is more than one turbine, then decrease the probability of failure by 30% - prob = mult_cond_prob(parents, current, probabilities, midpoint) # Calculate the conditional probability of the node given if the parents have failed or not - # print(true_false_array, prob) - # print(probabilities) - prob_table[iteration][-2] = prob # Add calculated probability to the array - prob_table[iteration][-1] = 1 - prob # Add the probability of the node not failing to the array - #print(prob_table) - return parents, prob_table # Return a list of the parents and the probability distribution array - - - - -''' write_bayesian_probabilities Documentation - ---------------------------------------------- - This method inputs adjacency matrix, list of node names, probabilties, boolean for calculations among multiple turbines, - boolean for midpoint calculation, and number of iterations. We calculate the probability distribution for each node in the graph and format it - into a table. This method writes the probability distribution tables to an Excel file (nothing returned).''' - -def write_bayesian_probabilities(adjacency_matrix, nodeNames, probabilities, mult_turbine, midpoint=True, num_iterations=1, twoTurbines = False, name=""): - ps = probabilities.copy() - with pd.ExcelWriter("bayesianProbabilities"+name+".xlsx") as writer: # Write to file - for current in range(1, adjacency_matrix.shape[0]+1): # Repeat for each node in the graph - if any(probabilities != ps): - print("Unequal probabilities", current-1) - break - adjacency_matrix2 = adjacency_matrix.copy() - parents, our_table = bayesian_table(adjacency_matrix2, current, midpoint, nodeNames, True, probabilities, mult_turbine) # Probability distrubution table - if twoTurbines: - parents, our_table = twoTurbine_bayesian_table(adjacency_matrix, adjacency_matrix, current, nodeNames, nodeNames) # Probability distrubution table - parents = [int(parent) for parent in parents] # Format parent list - - # Find average probability distribution table for the number of iterations - for i in range(num_iterations - 1): - parents, our_table2 = bayesian_table(adjacency_matrix, current, midpoint, nodeNames = nodeNames, pvs=True, prob_vals=probabilities, mult_turbine=mult_turbine) - our_table += our_table2 - - # Update column titles for the probability distribution table - parents = [nodeNames[parent - 1].replace("\n", " ") for parent in parents] - parents = np.append(parents, "P(" + nodeNames[current - 1].replace("\n", " ") + " = True)") - parents = np.append(parents, "P(" + nodeNames[current - 1].replace("\n", " ") + " = False)") - df = pd.DataFrame(our_table, columns = parents) - - df.to_excel(writer, sheet_name=nodeNames[current - 1].replace("\n", " ").replace("/", " or ").replace(":", "")[:31]) # Write to file - print(current, nodeNames[current - 1].replace("\n", " ")[:31]) # Print current node to keep track of which nodes have a probability distribution table - - - - -''' probability_over_time Documentation - ---------------------------------------------- - This method inputs a failure rate, lamda, and a time, t. We return the probability of a failure at time t.''' - -def probability_over_time(lamda, t): - return 1 - math.exp((np.log(1-lamda)) * t) # Return the updated probability of failure given a certain time - - - - -''' bayesian_inference Documentation - ---------------------------------------------- - This method inputs adjacency matrix, list of node names, array of nodes to start with for generating the graph of the Bayesian network, array of - evidence nodes, array of hypothesis nodes, probabilties, boolean for finding hte probability of failure of the hypothesis, boolean for printing - information about the calculations, boolean for multiple turbines, parent or child calculation (string), and boolean for using the twoTurbine case - study method. We calculate Bayesian inference given the evidence and hypothesis variables. This method outputs an array of inference probabilities.''' - -def bayesian_inference(arr, nodeNames, indicator, num_evidence, num_hypothesis, probabilities, tf = True, printing = False, multi= False, poc="parent", twoTurbine = False): - # print("E", num_evidence) - # print("H", num_hypothesis) - a = arr.copy() - non = nodeNames - prblts = probabilities - evidence = num_evidence - hypothesis = num_hypothesis - if not multi: - K, a, g, e, m, non = breadth_first_multi(a, nodeNames, indicator, poc) # Generate tree for Bayesian network - # draw_bfs_multipartite(arr, nodeNames, indicator, type = "multi-"+poc, multi_turbine = False) - prblts = [] # Initialize array of node probabilities (in order of appearance in graph) - for node in non: - node_index = np.where(nodeNames == node)[0][0] - prblts.append(probabilities[node_index]) # Add nodes to array of node probabilities - prblts = np.array(prblts) - - evidence = [] - hypothesis = [] - for hypothesis_node in num_hypothesis: # Adjust hypothesis node so that index in tree matches up with index in original adjacency matrix - if twoTurbine: - non = np.array(non) - if nodeNames[hypothesis_node - 1] not in non: # If node is not in tree, then there is a 0% probability of failure - print("Probability table of", nodeNames[hypothesis_node - 1], "...", np.reshape(np.array([0,1]), (1,2))) - return np.reshape(np.array([0,1]), (1,2)), np.reshape(np.array([0,1]), (1,2)) - hypothesis.append(np.where(non == nodeNames[hypothesis_node - 1])[0][0]) - - for evidence_node in num_evidence: # Adjust evidence node so that index in tree matches up with index in original adjacency matrix - if twoTurbine: - non = np.array(non) - if nodeNames[evidence_node - 1] not in non: # If node is not in tree, then this is an error! - print("ERROR - evidence node, " + nodeNames[evidence_node - 1] + ", not in graph!") - if evidence_node in num_hypothesis:# If node is in hypothesis, then probability of failure is 100% - print("Probability table of", nodeNames[evidence_node - 1], "...", np.reshape(np.array([1,0]), (1,2))) - return np.reshape(np.array([1,0]), (1,2)), np.reshape(np.array([1,0]), (1,2)) - evidence.append(np.where(non == nodeNames[evidence_node - 1])[0][0]) - - probabilitiy_table = np.zeros((2, a.shape[0])) # Initialize table of inference probabilities - nodes = diagonal_nodes(a) # Diagonal matrix of node names (numerical +1) - a = make_binary(a, 0.5) # Binarize adjacency table - hypothesis_nodes_array = np.zeros((len(hypothesis), 2)) - - # Depending on tree (forward or backward propagation), either iterate through nodes forward or backwards - if poc == "parent": - this_range = reversed(range(a.shape[0])) - elif poc == "child": - this_range = range(a.shape[0]) - - for node in this_range: - pts_bool = nodes @ a[:, node] # vector of zeros and child names (numerical names) - pts = pts_bool[np.nonzero(pts_bool)] #list of just the child names (numerical names) - - if len(pts) < 1: # If no parents, the probability is the initial probability - probabilitiy_table[0][node] = prblts[node] - probabilitiy_table[1][node] = 1 - prblts[node] - continue - - parents, our_table = bayesian_table(a, node+1, True, nodeNames, True, prblts) # Calculate the probability distribution table - - # If only using two turbines (specific to case study), calculate using twoTurbine method - if twoTurbine: - parents, our_table = twoTurbine_bayesian_table(a, arr, node + 1, nodeNames, non) # Calculate the probability distribution table - mlt_table = np.ones((our_table.shape[0],2)) # Initialize table for multiplying across rows of probability distribution table - - for i in range(our_table.shape[0]): - for j in range(our_table.shape[1] - 2): - parent = int(parents[j]) - - # Replace boolean in probability distribution table with probability of this event - if our_table[i,j] == 0: - # print(a.shape, probabilitiy_table.shape) - our_table[i,j] = probabilitiy_table[0][parent - 1] - - # If the node is in the hypothesis or evidence array, do not include this - if probabilitiy_table[0][parent - 1] == 0: - # print("indexing_error!!", parent, node) - # print(non[parent], non[node]) - break - if (parent-1 in hypothesis and tf): # or (parent in evidence): - our_table[i,j] = 0 - # print("in hypto and tf or in evidence", parent) - else: - our_table[i,j] = probabilitiy_table[1][parent - 1] - - # If the node is in the hypothesis or evidence array, do not include this - if (parent-1 in hypothesis and not tf) or (parent-1 in evidence): - our_table[i,j] = 0 - # print("in hypto and not tf or in evidence", parent) - - mlt_table[i,0] *= our_table[i,j] # Multiply the probabilities across the probability distribution table - mlt_table[i,1] = mlt_table[i,0] * our_table[i, -1] # Multiple by the probability of event not failing given combination of parent failure - mlt_table[i,0] *= our_table[i, -2] # Multiple by the probability of event failing given combination of parent failure - - sm_table = np.sum(mlt_table, axis = 0) #/np.sum(mlt_table) # Sum the products of probabilities across the columns - - if node in hypothesis: - print("Probability table of", non[node].replace("\n", " "), "...", sm_table) - hypothesis_nodes_array[np.where(np.array(hypothesis) == node)[0][0]][0] = sm_table[0] - hypothesis_nodes_array[np.where(np.array(hypothesis) == node)[0][0]][1] = sm_table[1] - if all(hypothesis_nodes_array != 0): - return probabilitiy_table, hypothesis_nodes_array - - probabilitiy_table[0][node] = sm_table[0] # Update the inference probability table with the probabilites just calculated - probabilitiy_table[1][node] = sm_table[1] - - if printing: # Print the inference probability, along with the indicators, evidence, and hypothesis conditionals - print("Indicator:", nodeNames[np.array(indicator)]) - print("Evidence:", nodeNames[np.array(evidence)]) - print("Hypothesis:", nodeNames[np.array(hypothesis)]) - print("Probability of Failure:", probabilitiy_table[:, 0][0], probabilitiy_table[:, 0][0]/np.sum(probabilitiy_table[:, 0])) - print("Probability of No Failure:", probabilitiy_table[:, 0][1], probabilitiy_table[:, 0][1]/np.sum(probabilitiy_table[:, 0])) - return probabilitiy_table, hypothesis_nodes_array # Return array or inference probabilities - - - - -''' backward_bayesian_inference Documentation - ---------------------------------------------- - This method inputs adjacency matrix, list of node names, array of nodes to start with for generating the graph of the Bayesian network, array of - evidence nodes, array of hypothesis nodes, probabilties, boolean for indexing into the right values of the array, boolean for multiple turbines, parent or child - calculation type (string), and boolean for using twoTurbine case study method. We calculate Bayesian inference given the evidence and hypothesis variables. - This method outputs an array of inference probabilities (normalized and not).''' - -def backward_bayesian_inference(adjacency_matrix, nodeNames, indicator, evidence, hypothesis, probabilities, start_bool = True, multi = False, poc="parent", twoTurbine = False): - # Calculate Bayesian inference for hypothesis = True and hypothesis = False - #print("--- First Run of Bayesian Inference ----------------") - # print("Before bn", probabilities.shape) - printing = False - if len(evidence) > 0 and len(hypothesis)>0: - printing = False - - pt1, hnd1 = bayesian_inference(adjacency_matrix, nodeNames, indicator, evidence, hypothesis, probabilities, True, printing=printing, multi = multi, poc=poc, twoTurbine=twoTurbine) - #print() - #print("--- Second Run of Bayesian Inference ---------------") - if poc == "parent": - pt2, hnd2 = bayesian_inference(adjacency_matrix, nodeNames, indicator, evidence, hypothesis, probabilities, False, printing=printing, multi = multi, poc=poc, twoTurbine=twoTurbine) - # print("pt1", pt1[:, 0]) - # print("pt2", pt2[:, 0]) - # Choose correct indexing value - index = 1 - if start_bool: - index = 0 - - if multi: - return [hnd1[0][0]/(hnd1[0][0]+hnd1[0][1]), hnd1[0][1]/(hnd1[0][0]+hnd1[0][1])], [0,0] - - # Depending on what our evidence and hypothesis nodes are, index into the correct values - if poc == "parent" and 0 in evidence: - p_true = pt1[:, 0][index] - p_false = pt2[:, 0][index] - - elif poc == "parent" and 0 in hypothesis: - p_true = pt1[:, 0][0] + pt2[:, 0][0] - p_false = pt1[:, 0][1] + pt2[:, 0][1] - - elif poc == "child": - if hnd1.shape[0] == 1: - p_true = hnd1[0][0] - p_false = hnd1[0][1] - - else: # If multiple nodes in hypothesis, multiply all possibilities together to get normailized value - print("Starting hypothesis for-loops...") - p_false = 0 - for iteration in range(2 ** hnd1.shape[0]): - p_false_mult = 1 - for node in range(hnd1.shape[0]): - p_false_mult *= hnd1[node][int(iteration / (2 ** node)) % 2] - if iteration == 0: - p_true = p_false_mult - else: - p_false += p_false_mult - if (iteration) % 1000000000000 == 0: - print("Percent done:", iteration/(2 ** hnd1.shape[0]) * 100) - print("Percent done:", 100) - else: - p_true = np.sum(pt1[:, 0]) - p_false = np.sum(pt2[:, 0]) - - probability_distribution = [p_true/(p_true + p_false), p_false/(p_true + p_false)] # Normalize the probability distribution of the event happening - - return probability_distribution, [p_true, p_false] # Return the normalized probability distribution and unnormalized distribution - - - -''' -# ----------- For running the code: feel free to uncomment ------------------- -arr, nodeNames = excel2Matrix("failureData.xlsx", "bigMatrix") -#random.seed(18) -start = 30 #np.random.randint(0, arr.shape[0]) -probabilities = np.array([0.0195, 0.0195, 0.013625, 0.0055, 0.0175, 0.2075, 0.001, 0.001, 0.001, 0.093185, 0.001, 0.001, - 0.027310938, 0.033968125, 0.033968125, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, - 0.0205, 0.0205, 0.02, 0.01, 0.01, 0.233, 0.288, 0.543374, 0.1285, 0.01, 0.01, 0.01, 0.015, 0.0155, - 0.015, 0.0155, 0.015, 0.0155, 0.015, 0.33, 0.025, 0.025, 0.025, 0.025, 0.025, 0.105]) #0.01375, -probabilities = np.reshape(probabilities, (arr.shape[0], 1)) -start = 44 #np.random.randint(1, arr.shape[0]+1) -single_simulation(start, arr, nodeNames) -single_simulation(start, arr, nodeNames, update=True) - -num_iterations = 100 -plot = True -start = 11 #np.random.randint(1, arr.shape[0]+1) -monte_carlo_sim(num_iterations, plot, start, adjacency_matrix, nodeNames, rand_seed=False, mid_point=False)''' - - - - -'''# ----------- For running the code: feel free to uncomment ------------------- -adjacency_matrix, nodeNames = excel2Matrix("ExcelFiles/failureData.xlsx", "bigMatrix") -probabilities = np.array([0.0195, 0.0195, 0.013625, 0.0055, 0.0175, 0.2075, 0.001, 0.001, 0.001, 0.093185, 0.001, 0.001, - 0.027310938, 0.033968125, 0.033968125, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, 0.01375, - 0.0205, 0.0205, 0.02, 0.01, 0.01, 0.233, 0.288, 0.543374, 0.1285, 0.01, 0.01, 0.01, 0.015, 0.0155, - 0.015, 0.0155, 0.015, 0.0155, 0.015, 0.33, 0.025, 0.025, 0.025, 0.025, 0.025, 0.105]) #0.01375, -probabilities = np.reshape(probabilities, (adjacency_matrix.shape[0], 1)) -targets = [44] -starts = [16, 33, 35, 36] - -# C, D = bayesian_table(adjacency_matrix, 16, True, pvs = True, prob_vals=probabilities, mult_turbine = False) -# A, B = backward_bayesian_inference(adjacency_matrix, nodeNames, [0], [0], [44], probabilities, start_bool = True) -# print(B) -write_bayesian_probabilities(adjacency_matrix, nodeNames, probabilities, mult_turbine = True, midpoint = True, num_iterations=1)''' \ No newline at end of file diff --git a/failureProbabilities.xlsx b/failureProbabilities.xlsx deleted file mode 100644 index 0b960e89958fd8be2f6d1da59b295bd0a4e48a5c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 73055 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

8y+4x}vQRnm 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 NF|dGGCIQUo3; output adjacency matrix and name array - - 2) matrix2Graph: inputs adjacency matrix, name of nodes, boolean of nodeLabels --> output graph and adjacency - matrix - - 3) export2graphml: inputs G, string (file name) --> writes graphML file with inputted file name (no return) - - 4) get_node_dict: inputs adjacency matrix and name of nodes --> outputs array containing dictionaries with each - entry containing the node's name, number, and whether it is an effect or mode. - - 5) get_node_array: inputs adjacency matrix and name of nodes --> outputs array with dictionary entries of the - names of the nodes - - 6) get_graph_edges: inputs adjacency matrix, string indicating type of output desired, and threshold --> outputs - an array of edges - - 7) make_binary: inputs adjacency matrix and float type --> outputs binarized adjacency matrix - - 8) plot_graph: inputs graph and type (str) --> no output (prints graph) - - 9) max_degrees: inputs adjacency matrix, names of nodes, threshold for binarization of adjacency matrix, boolean - for showing names --> outputs tuples of max out degree, max in degree, and max overall degree with the nodes that - have these max degrees - -10) getProbabilities: inputs fileName, sheetName, boolean of nodeLabels --> output probabilities and name array - -11) diagonal_nodes: inputs adjacency matrix --> outputs nodes diagonal matrix - -12) cosine_similarity: inputs vector one and vector two --> outputs cosine similarity of vector one and two - -13) make_undirected_unweighted: inputs adjacency matrix and (optional) threshold --> outputs undirected, unweighted - version of the adjacency matrix - ------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------- - - - excel2Matrix Documentation - ------------------------------- - This code is meant to take in an excel file (already properly formatted by theh user) and output the corresponding - graph. This assumes that the contents of the excel file is an adjacency matrix. The sheetName input identifies which - sheet in the excel file the adjacency matrix is in. The nodeLabels input lets the user choose if they would like - words (input TRUE) or numeric (input FALSE) labels on the graph. The plot input lets the user toggle on/off plotting - of the graph. Finally, the graph is returned.''' - -def excel2Matrix(fileName, sheetName = None): - # Read in excel file as a dataframe - df = pd.read_excel(fileName, sheet_name=sheetName) - - # Convert the data frame to a numpy array - arr = df.to_numpy() - - # Create an array of the names of the nodes for the graph. - nodeNames = arr[:, 0].flatten() - - # return the adjacency matrix without labels and an array of the names of the nodes - return arr[:, 1:], nodeNames - - - -''' matrix2Graph Documentation - ------------------------------- - This method takes in an adjacency matrix, the names of the nodes, whether or not to display the node names, and - whether or not to display the plot of the graph once constructed. The output is the graph and adjacency matrix.''' - -def matrix2Graph(arr, nodeNames, nodeLabels = False): - # Create the graph (DiGraph because the graph is directed and weighted) and add the nodes selected above - G = nx.DiGraph() - if not nodeLabels: - nodeNames = range(arr.shape[0]) - G.add_nodes_from(nodeNames) - - # The following nested for loops find the edges of the graph and add them to the graph using the 'add_weighted_edges_from()' - # command. 'i' indexes through the rows and 'j' indexes through the columns. If there is no edge (i.e. 0 in the adjacency - # matrix), then move on to the next set of nodes. Otherwise, add the edge to the graph. - for i in range(arr.shape[0]): - for j in range(arr.shape[1]): - if arr[i,j] == 0: - continue - else: - if nodeLabels: G.add_weighted_edges_from([(nodeNames[i], nodeNames[j], arr[i,j])]) - else: G.add_weighted_edges_from([(i, j, arr[i,j])]) - - # Return the graph and adjacency matrix - return G, arr - - - -''' export2graphML Documentation - ------------------------------- - This method takes in a networkx graph and ouputs a graphML file.''' - -def export2graphML(G, name): - - # create a name for the graphML file that you are going to write - filename = name + "_FOWT_graphML.graphml" - - # wrtie the graphML file using networkx methods - nx.write_graphml(G, filename, encoding='utf-8', prettyprint=True, infer_numeric_types=True, named_key_ids=True, edge_id_from_attribute=None) - return - - - -''' get_node_dict Documentation - ------------------------------- - This method takes in the adjacency matrix and the names of all the nodes. It outputs an array of node dictionaries. - Each key/value pair contains the node's number, name, and 'class_id' (which refers to if the node represents a - failure effect or failure mode).''' - -def get_node_dict(arr, nodeNames): - # Initialize array for the nodes - nodes = [] - - # Iterate through each node number - for index in range(arr.shape[0]): - - # If the number is less than 26 (value chosen based on excel sheets), then the node is a failure effect. - if index < 26: - class_id = 'effect' - - # Otherwise, the node must be a failure mode. - else: - class_id = 'mode' - - # Add a dictionary with the node's number, name, selection criteria (in this case they can all be selected), - # and class_id (whether they are an effect or mode). - nodes.append({ - 'data': {'id': str(index), 'label': nodeNames[index]}, - 'selectable': True, - 'classes': class_id - }) - - # Return the array of dictionaries for the nodes - return nodes - - - -''' get_node_array Documentation - ------------------------------- - This method takes in an adjacency matrix and list of node names. It outputs an array with each entry a - key/value pair. Each key is the string 'label', but the values are the string names of the nodes.''' - -def get_node_array(arr, nodeNames): - # Initialize an array for the nodes - nodes = [] - - # Iterate through each node number - for index in range(arr.shape[0]): - - # For every node, locate its name and append a dictionary with 'label' as the only key and - # the name just acquired as the only value. - nodes.append({'label': nodeNames[index]}) - - # Return the array of nodes - return nodes - - -''' get_grpah_edges Documentation - ------------------------------- - This method takes in an adjacency matrix (array type) and a string indicating the type of edge list - desired. This method outputs an array of all the edges. If the 'dictionary' type is indicated, then each - entry in the array is formatted as - {'data': {'id': name_of_source_and_target, 'source' name_of_source, 'target', name_of_target}}. - If the 'array' type is indicated, then each entry in the array is formatted as - [source, target]). - Lastly, if the 'tuple' type is indicated, then each entry in the array is formatted as - (source, target).''' - -def get_graph_edges(arr, type, threshold): - # Initialize an array for the edges - edges = [] - - # Iterate through each row of the adjacency matrix - for i in range(arr.shape[0]): - - # For each row, iterate through each column of the matrix - for j in range(arr.shape[1]): - - # If the entry in the adjacency matrix is greater than 0,5 (can change this threshold), then - # add an edge to the array of edges. Each edge is added by appending an array containing the - # source and target. - if arr[i, j] > threshold: - if type == 'dict': - edges.append({'data': {'id': str(i)+str(j), 'source': str(i), 'target': str(j)}, - 'classes': 'red'}) - elif type == 'array': - edges.append([i,j]) - elif type == 'tuple': - edges.append((i,j)) - - # Otherwise, if the input does not indicate an edge, move on to the nect entry in the - # adjacency matrix - else: - continue - - # Return the array of edges - return edges - - - -''' make_binary Documentation - ------------------------------- - This method takes in an adjacency matrix (array typle) and a threshold value (float type). It outputs a - new adjacency matrix such that all the entries are either 0 or 1.''' - -def make_binary(arr, threshold = 0): - # Initialize the new adjacency matrix by setting it equal to the original adjacency matrix - arr_altered = arr - - # Iterate through each row of the matrix - for i in range(0, arr_altered.shape[0]): - - # In each row, iterate through each column of the matrix - for j in range(arr_altered.shape[0]): - - # If the entry is greater than the indicated threshold, set the entry of the new adjacency - # matrix equal to 1 - if arr[i,j] > threshold: - arr_altered[i,j] = 1 - - # Otherwise, set the entry equal to 0 - else: - arr_altered[i,j] = 0 - - # Return the new adjacency matrix - return arr_altered - - - -''' plot_graph Documentation - ------------------------------- - This method takes in a plot and type of plot desired, then prints a plot of the graph.''' - -def plot_graph(G, type, effects_mark, nodeNames): - - # This type places the failure effects on one side and the failure modes on the other in vertical columns - if type == "bipartite": - pos = nx.bipartite_layout(G, nodeNames[:effects_mark]) # positions for all nodes - nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98", edgecolors="#c89679", node_shape="s") - nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed", edgecolors="#799dbd", node_shape="s") - - # This for loop places labels outside of the nodes for easier visualization - for i in pos: - x, y = pos[i] - if x <= 0: - plt.text(x-0.075,y,s=i, horizontalalignment='right') - - else: - plt.text(x+0.075,y,s=i, horizontalalignment='left') - - #nx.draw_networkx_labels(G, pos, horizontalalignment='center') - nx.draw_networkx_edges(G, pos) - plt.box(False) - plt.show() - return - - # This type draws all the nodes in a circle - elif type == "circular": - pos = nx.circular_layout(G) # positions for all nodes - nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") - nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") - nx.draw_networkx_labels(G, pos) - nx.draw_networkx_edges(G, pos) - plt.box(False) - plt.show() - return - - # This type attempts to have all the edges the same length - elif type == "kamada_kawai": - pos = nx.kamada_kawai_layout(G) # positions for all nodes - nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") - nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") - nx.draw_networkx_labels(G, pos) - nx.draw_networkx_edges(G, pos) - plt.box(False) - plt.show() - return - - # This type attempts to have non-intersecting edges. This only works for planar graphs. - elif type == "planar": - pos = nx.planar_layout(G) # positions for all nodes - nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") - nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") - nx.draw_networkx_labels(G, pos) - nx.draw_networkx_edges(G, pos) - plt.box(False) - plt.show() - return - - # This type plots the graph in a random configuration - elif type == "random": - pos = nx.random_layout(G) # positions for all nodes - nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") - nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") - nx.draw_networkx_labels(G, pos) - nx.draw_networkx_edges(G, pos) - plt.box(False) - plt.show() - return - - # This type uses the eigenvectrs of the graph Laplacian in order to show possible clusters of nodes - elif type == "spectral": - pos = nx.spectral_layout(G) # positions for all nodes - nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") - nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") - nx.draw_networkx_labels(G, pos) - nx.draw_networkx_edges(G, pos) - plt.box(False) - plt.show() - return - - # This type treats nodes as repelling objects and edges as springs (causing them to moving in simulation) - elif type == "spring": - pos = nx.spring_layout(G) # positions for all nodes - nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") - nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") - nx.draw_networkx_labels(G, pos) - nx.draw_networkx_edges(G, pos) - plt.box(False) - plt.show() - return - - # This type places nodes in concentric circles - elif type == "shell": - pos = nx.shell_layout(G) # positions for all nodes - nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") - nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") - nx.draw_networkx_labels(G, pos) - nx.draw_networkx_edges(G, pos) - plt.box(False) - plt.show() - return - - # This type places nodes in spiral layout - elif type == "spiral": - pos = nx.spiral_layout(G) # positions for all nodes - nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") - nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") - nx.draw_networkx_labels(G, pos) - nx.draw_networkx_edges(G, pos) - plt.box(False) - plt.show() - return - - # This type places nodes in spiral layout - elif type == "multipartite": - - pos = nx.multipartite_layout(G) # positions for all nodes - nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[:effects_mark], node_color="#98c5ed") - nx.draw_networkx_nodes(G, pos, nodelist=nodeNames[effects_mark:], node_color="#fabc98") - nx.draw_networkx_labels(G, pos) - nx.draw_networkx_edges(G, pos) - plt.box(False) - plt.show() - return - - # Plot the grpah with networkx and matplotlib using regular algorithm - nx.draw(G, with_labels=True, font_weight='bold') - plt.show() - return - - - -''' max_degrees Documentation - -------------------------------- - This method takes in an adjacency matrix, the names of the nodes, a threshold for binarization - of the adjacency matrix, and a boolean for labeling the nodes with names (True) or numbers (False). - The output consists of three tuples: - 1. (the nodes with the highest out degree, value of the highest out degree) - 1. (the nodes with the highest in degree, value of the highest in degree) - 1. (the nodes with the highest overall degree, value of the highest overall degree)''' - -def max_degrees(arr, nodeNames, threshold = 0, name = False): - - # Create copy of the adjacency matrix so that we can alter it without losing information about - # our original adjacency matrix. - arr_altered = arr - - # Binarization of adjacency matrix. The threshold determines the cutoff for what will be labeled - # as a 1 versus a 0. Anything above the threshold will be a 1, and anything below the threshold - # will be set to 0. - for i in range(0, arr_altered.shape[0]): - for j in range(arr_altered.shape[0]): - if arr[i,j] > threshold: - arr_altered[i,j] = 1 - else: - arr_altered[i,j] = 0 - - # Calculating out degrees and the maximum of the out degrees - out_degrees = np.sum(arr_altered, axis=1) - max_out = np.where(out_degrees == max(out_degrees)) - - # Calculating in degrees and the maximum of the in degrees - in_degrees = np.sum(arr_altered, axis=0) - max_in = np.where(in_degrees == max(in_degrees)) - - # Calculating overall degrees and the maximum of the overall degrees - degrees = out_degrees + in_degrees - max_deg = np.where(degrees == max(degrees)) - - # If the user chooses to list the nodes by their proper name (rather than with numbers), then - # we index into the array of node names, nodeNames, to find the names of these nodes. We then - # return the three tuples about maximum out degree, in degree, and overall degree. - if name: - out_name = nodeNames[max_out[0].tolist()] - in_name = nodeNames[max_in[0].tolist()] - deg_name = nodeNames[max_deg[0].tolist()] - return (out_name, max(out_degrees)), (in_name, max(in_degrees)), (deg_name, max(degrees)) - - # Otherwise, if the user chooses not to label nodes with their proper names, then we return the - # three tuples about maximum out degree, in degree, and overall degree. Rather than listing the - # names of the nodes, their corresponding numbers are listed. - return (max_out, max(out_degrees)), (max_in, max(in_degrees)), (max_deg, max(degrees)) - - - -''' getProbabilities Documentation - ------------------------------- - This method inputs an Excel file and reads the probabilities from the file. We return an array of these - probabilities and an array of the node names (indexed the same as the probability array).''' - -def getProbabilities(fileName, sheetName = None): - df = pd.read_excel(fileName, sheet_name=sheetName) - - # Convert the data frame to a numpy array - arr = df.to_numpy() - - # Create an array of the names of the nodes for the graph. - nodeNames = arr[:, 0].flatten() - - # return the adjacency matrix without labels and an array of the names of the nodes - return arr[:, 1], nodeNames - - -'''diagonal_nodes Documentation - -------------------------------- - This method takes in an adjacency matrix and outputs a diagonal matrix. For an adjacency matrix of length k, the - outputted diagonal matrix places values 1-k on the diagonal.''' - -def diagonal_nodes(arr): - # Return the diagonal matrix with i+1 in the i,i spot and zero elsewhere - return np.diag([i+1 for i in range(arr.shape[0])]) - -'''cosine_similarity Documentation - ----------------------------------- - This method takes in two vectors and outputs the cosine similarity between them. In linear algebra, cosine - similarity is defined as the measure of how much two vectors are pointing in the same direction. It can also - be thought of cosine of the angle between the two vectors.''' - -def cosine_similarity(v1, v2): - # Find the dot product between the two vectors inputted - dot_prod = np.transpose(v1) @ v2 - - # Calculate and return the cosine similarity of the two vectors - return (dot_prod) / ((np.sqrt(np.sum(np.square(v1))) * np.sqrt(np.sum(np.square(v2))))) - - - -''' make_undirected_unweighted Documentation - --------------------------------------------- - This method takes in an adjacency matrix and threshold. It outputs an adjusted adjacency matrix that - corresponds to the undirected and unweighted graph of the one inputted.''' - -def make_undirected_unweighted(arr, threshold = 0): - # Make sure that the array is a numpy array - arr = np.array(arr) - - # Add the array to the transpose of itself. This makes the graph undirected. - arr = arr + arr.T - - # Use the method from graphBuilder.py to binarize the matrix. This makes the graph unweighted. - make_binary(arr, threshold) - - # Since the array now only consists of zeros and ones, make sure that it is of integer type - arr = arr.astype(int) - - # Return the adjusted matrix - return arr \ No newline at end of file diff --git a/searchMethods.py b/searchMethods.py deleted file mode 100644 index 11b3ffe9..00000000 --- a/searchMethods.py +++ /dev/null @@ -1,429 +0,0 @@ -import numpy as np -import networkx as nx -import matplotlib.pyplot as plt -from graphBuilder import * - -''' searchMethods-------------------------------------------------------------------------------------------------- - - ****************************** Last Updated: 10 April 2024 ****************************** - - Methods: - 1) breadth_first_child: inputs adjacency matrix, names of nodes, start node --> outputs tree, adjacency matrix, - dictionary of nodes and generations, list of 'effect' nodes, list of 'mode' nodes - - 2) breadth_first_parent: inputs adjacency matrix, names of nodes, start node --> outputs tree, adjacency matrix, - dictionary of nodes and generations, list of 'effect' nodes, list of 'mode' nodes - - 3) draw_bfs_multipartite: nputs adjacency matrix, list of node names, start node --> plots breadth first tree. - Nothing is returned. - - 4) breadth_first_multi: adjacency matrix, list of node names, starting node, type of breadth first search --> - outputs tree, adjacency matrix, dictionary of nodes, arrays for effects and modes, and array of nodes - --------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------- - - - breadth_first_child Documentation - ----------------------------------- - This method takes in an adjacency matrix, list of node names, and an integer indicating te starting node. We - traverse through the graph. For each generation, starting from the source node, we find and add all the children - of the nodes in this generation, unless the node has aldready been placed in the tree. As such, we create a tree. - This method returns this tree, the tree's adjacency matrix, a dictionary of nodes, and arrays for effects and modes. - - --> Note: a tree is a graph with no cycles or loops. It does not have to be directed, but it is acyclic.''' - -def breadth_first_child(arr, nodeNames, source): - # Initialize a new digraph for the tree we are going to make and add all the nodes (we will determine edges later) - G = nx.DiGraph() - - # Initialize effects and modes arrays for plotting purposes - effects = [] - modes = [] - - # Add the source node to the graph - G.add_node(nodeNames[source - 1]) - - # Determine if the source node is an effect or a mode, and add it to the correct array - if source < 27: effects.append(nodeNames[source - 1]) - elif source <= 47: modes.append(nodeNames[source - 1]) - - '''if source < 49: modes.append(nodeNames[source - 1]) - else: effects.append(nodeNames[source - 1])''' - - # Note that we are changing the numerical names of the nodes so that the values of nodes are from 1 to 47 rather - # than from 0 to 46. This is so we can find all the children of the nodes. Using the 0-46 range poses a problem - # since we do not know if a zero indicates no relation or that the 0 node is a child of the node we are looking at. - # We binarize the adjacency matrix so that relationships either exist or not (rather than having an intensity) - adj = make_binary(arr, 0.5).astype(int) - - # Create an array of booleans such that the ith entry indicates if the node has already been visited. Nodes - # that have been visited are set to TRUE. - nodeList = np.reshape(np.repeat(False, arr.shape[0]), (arr.shape[0], 1)) - - # Create a diagonal matrix such that the value of the i,i entry is 1+1, referencing the node with name i+1 - nodes = diagonal_nodes(adj) - # print("nodes", nodes.shape) - - # Visit the source node - nodeList[source-1] = True - - # Add the source node to the queue. We use a queue to iterate through the nodes. This allows us to search through - # the graph generation by generation rather than following one specific path at a time. - queue = [[source, 0]] - - # Initialize a dictionary that will tell us what generation each node is in. Label the generation of the source node - # as zero. - gens = {nodeNames[source-1]: {"layer": 0}} - - # Continue while there are still nodes in the queue (reference algorithms for a breadth first search for more info) - while len(queue) > 0: - # Set the node we are looking at equal to the node next in line in the queue - current = queue[0] - - # Determine the children of the current node. - # Proof/Reason Why This Works --> Using the unweighted adjacency matrix, we can find the children - # by multiplying the row of the adjacency matrix corresponding to the current node by the diagonal matrix of - # node names. If the jth node is a child of the current node, then there is a 1 in the j-1 column of the - # current row. When multiplied by the diagonal matrix, this 1 will be multiplied by the j-1 column of the. - # since the only non zero entry in the j-1 column of the diagonal matrix is j (located on the diagonal), then - # the entry in the j-1 column of the returned vector will be j. However, if the jth node is not a child of - # the current node, then there is a zero in the j-1 column of the current row of the adjacency matrix. When - # multiplied by the diagonal matrix, this zero is multiplied by every entry in the j-1 column of the diagonal - # matrix. So, the j-1 column of the returned vector is zero. - - children_bool = adj[current[0]-1] @ nodes # vector of zeros and child names (numerical names) - children = children_bool[np.nonzero(children_bool)] #list of just the child names (numerical names) - - for child in children: # For every child of the current node that was found above... - # Check if the child has been visited or if it is in the same generation as child. If either not visited or - # in the same generation, continue with the following: - if nodeList[child - 1] == False or gens[nodeNames[child - 1]]['layer'] > current[1]: - - # Add the child to the graph - G.add_node(nodeNames[child-1]) - - # Determine if the child is an effect or mode and add it to the correct array - if child < 27: effects.append(nodeNames[child - 1]) - elif child <= 47: modes.append(nodeNames[child - 1]) - '''if child < 49: modes.append(nodeNames[child - 1]) - else: effects.append(nodeNames[child - 1])''' - - # Add an edge between the current node and child in the tree we are building - G.add_edge(nodeNames[current[0]-1], nodeNames[child-1]) - - queue.append([child, current[1]+1]) # Append the child to the queue - nodeList[child - 1] = True # Change the status of the child to say we have visited it - gens.update({nodeNames[child-1]: {"layer": current[1]+1}}) # Add child to dictionary of nodes - - # Remove the current node from the queue - queue = queue[1:] - - # Return the tree we made, its adjacency matrix, the dictionary of nodes, the effects array, and the modes arrat - return G, nx.to_numpy_array(G), gens, effects, modes - - - -''' breadth_first_parent Documentation - --------------------------------------- - This method takes in an adjacency matrix, list of node names, and an integer indicating te starting node. We - traverse through the graph. For each generation, starting from the source node, we find and add all the parents - of the nodes in this generation, unless the node has aldready been placed in the tree. As such, we create a tree. - This method returns this tree, the tree's adjacency matrix, a dictionary of nodes, and arrays for effects and modes.''' - -def breadth_first_parent(arr, nodeNames, source): - # Initialize a new digraph for the tree we are going to make and add all the nodes (we will determine edges later) - # Initialize a graph in Networkx - G = nx.DiGraph() - - # Initialize effects and modes arrays for plotting purposes - effects = [] - modes = [] - names_of_nodes = [] - - # Add the source node to the graph - G.add_node(nodeNames[source - 1]) - names_of_nodes.append(nodeNames[source - 1]) - - # Determine if the source node is an effect or a mode, and add it to the correct array - '''if source < 27: effects.append(nodeNames[source - 1]) - else: modes.append(nodeNames[source - 1])''' - if source < 49: modes.append(nodeNames[source - 1]) - else: effects.append(nodeNames[source - 1]) - - # Binarize the adjacency matrix - arr2 = arr.copy() - adj = make_binary(arr2).astype(int) - - # Create an array of booleans such that the ith entry indicates if the node has already been visited. Nodes - # that have been visited are set to TRUE. - nodeList = np.reshape(np.repeat(False, arr.shape[0]), (arr.shape[0], 1)) - - # Create a diagonal matrix such that the value of the i,i entry is 1+1, referencing the node with name i+1 - nodes = diagonal_nodes(adj) - - # Visit the source node - nodeList[source-1] = True - - # Add the source node to the queue. We use a queue to iterate through the nodes. This allows us to search through - # the graph generation by generation rather than following one specific path at a time. - queue = [[source, 100]] - - # Initialize a dictionary that will tell us what generation each node is in. Label the generation of the source node - # as zero. - gens = {nodeNames[source-1]: {"layer": 100}} - - # Continue while there are still nodes in the queue (reference algorithms for a breadth first search for more info) - while len(queue) > 0: - # Set the node we are looking at equal to the node next in line in the queue - current = queue[0] - - # Determine the parents of the current node. - parents_bool = nodes @ adj[:, current[0]-1] # vector of zeros and parent names (numerical names) - parents = parents_bool[np.nonzero(parents_bool)] #list of just the parent names (numerical names) - - for parent in parents: # For every child of the current node that was found above... - # Check if the parent has been visited or if it is in the same generation as parent. If either not visited or - # in the same generation, continue with the following: - if nodeList[parent - 1] == False or gens[nodeNames[parent - 1]]['layer'] < current[1]: - - # Add the parent to the graph - G.add_node(nodeNames[parent-1]) - - # Determine if the parent is an effect or mode and add it to the correct array - if parent < 27: effects.append(nodeNames[parent- 1]) - else: modes.append(nodeNames[parent - 1]) - '''if parent < 49: modes.append(nodeNames[parent- 1]) - else: effects.append(nodeNames[parent - 1])''' - - # Add an edge between the current node and parent in the tree we are building - G.add_edge(nodeNames[parent-1], nodeNames[current[0]-1]) - - queue.append([parent, current[1]-1]) # Append the parent to the queue - nodeList[parent - 1] = True # Change the status of the parent to say we have visited it - gens.update({nodeNames[parent-1]: {"layer": current[1]-1}}) # Add parent to dictionary of nodes - - # Remove the current node from the queue - queue = queue[1:] - - # Return the tree we made, its adjacency matrix, the dictionary of nodes, the effects array, and the modes arrat - return G, nx.to_numpy_array(G), gens, effects, modes - - - -''' draw_bfs_multipartite Documentation - --------------------------------------- - This method inputs an adjacency matrix, list of node names, and a start node. We use the breadth first searh to - find generations of nodes starting from the start node. This method plots the resulting graph from the breadth - first search. Nothing is returned.''' - -def draw_bfs_multipartite(arr, nodeNames, start, type = "child", multi_turbine = False): - # Obtain the graph, adjacency matrix, dictionary, effects, and modes from breadth_first_tree - if type == "child": - G, arr, gens, effects, modes = breadth_first_child(arr, nodeNames, start) - elif type == "parent": - G, arr, gens, effects, modes = breadth_first_parent(arr, nodeNames, start) - elif type == "multi-child": - G, arr, gens, effects, modes, non = breadth_first_multi(arr, nodeNames, start, "child") - else: - G, arr, gens, effects, modes, non = breadth_first_multi(arr, nodeNames, start, "parent") - - # Give each node the attribute of their generation - nx.set_node_attributes(G, gens) - # print(effects) - - if multi_turbine: - effect_colors = ["#ffd6ed", "#ffb3ba", "#ffdfba", "#ffffba", "#baffc9", "#bae1ff", "#b1adff", "#e4adff", "#e5e5e5", "#e8d9c5"] - mode_colors = ["#e5c0d5", "#e5a1a7", "#e5c8a7", "#e5e5a7", "#a7e5b4", "#a7cae5", "#9f9be5", "#cd9be5", "#cecece", "#d0c3b1"] - pos = nx.multipartite_layout(G, subset_key='layer') - for node in G.nodes: - # print(int(str(node)[0])) - if str(node) in effects: - nx.draw_networkx_nodes(G, pos, nodelist=[node], node_color = effect_colors[int(str(node)[0])], node_size=750, node_shape="s") - else: - nx.draw_networkx_nodes(G, pos, nodelist=[node], node_color = effect_colors[int(str(node)[0])], node_size=750) - nx.draw_networkx_labels(G, pos, font_size=5, verticalalignment='center_baseline') - nx.draw_networkx_edges(G, pos, arrowsize=20) - plt.box(False) - plt.show() - else: - # Plot the graph - pos = nx.multipartite_layout(G, subset_key='layer') - nx.draw_networkx_nodes(G, pos, nodelist=effects, node_color="#98c5ed", node_size=2700, edgecolors="#799dbd", node_shape="s") - nx.draw_networkx_nodes(G, pos, nodelist=modes, node_color="#fabc98", node_size=2700, edgecolors="#c89679", node_shape="s") - nx.draw_networkx_labels(G, pos, font_size=10, verticalalignment='center_baseline') - nx.draw_networkx_edges(G, pos, arrowsize=60) - plt.box(False) - plt.show() - - # Nothing is returned - return - - - -'''breadth_first_multi Documentation - ----------------------------------- - This method takes in an adjacency matrix, list of node names, an integer indicating te starting node, and a string - that indicates which type of breadth first search we are conducting (child or parent). We traverse through the graph. - For each generation, starting from the source nodes (handles multiple start nodes), we find and add all the children/parents - of the nodes in this generation, unless the node has aldready been placed in the tree. As such, we create a tree. - This method returns this tree, the tree's adjacency matrix, a dictionary of nodes, arrays for effects and modes, and array - of nodes (in the order they are added).''' - -def breadth_first_multi(arr, nodeNames, sources, type_poc): - # Function that determines how value of the layer changes, depending on the type of calculation - def layer_fcn(layer, type_poc): - if type_poc == "child": - return layer + 1 - elif type_poc == "parent": - return layer - 1 - # Determining if the current generation is after the former node's generation, depending on the type of calculation - def same_layer(layer1, layer2, type_poc): - if type_poc == "child": - if layer1 > layer2: - return True - else: - return False - elif type_poc == "parent": - if layer1 < layer2: - return True - else: - return False - - # Initialize a new digraph for the tree we are going to make and add all the nodes (we will determine edges later) - G = nx.DiGraph() - - # Initialize arrays for tracking nodes - effects = [] - modes = [] - names_of_nodes = [] - adj = make_binary(arr).astype(int) - nodeList = np.reshape(np.repeat(False, arr.shape[0]), (arr.shape[0], 1)) - nodes = diagonal_nodes(adj) - queue = [] - gens = {} - - # Initialize value for the first layer created - if type_poc == "child": - layer_val = 0 - else: - layer_val = 100 - - # Add the source node to the graph - for start in sources: - G.add_node(nodeNames[start - 1]) - - # Determine if the source node is an effect or a mode, and add it to the correct array - if start < 27: effects.append(nodeNames[start - 1]) - else: modes.append(nodeNames[start - 1]) - - '''if start < 49: modes.append(nodeNames[start - 1]) - else: effects.append(nodeNames[start - 1])''' - - # Visit the source node - nodeList[start-1] = True - names_of_nodes.append(nodeNames[start - 1]) - - # Add the source node to the queue. We use a queue to iterate through the nodes. This allows us to search through - # the graph generation by generation rather than following one specific path at a time. - queue.append([start, 0]) - - # Initialize a dictionary that will tell us what generation each node is in. Label the generation of the source node - # as zero. - gens.update({nodeNames[start-1]: {"layer": layer_val}}) - # print("non", names_of_nodes) - - # Continue while there are still nodes in the queue (reference algorithms for a breadth first search for more info) - while len(queue) > 0: - # Set the node we are looking at equal to the node next in line in the queue - current = queue[0] - - if type_poc == "child": - # Determine the children of the current node. - children_bool = adj[current[0]-1] @ nodes # vector of zeros and child names (numerical names) - children = children_bool[np.nonzero(children_bool)] #list of just the child names (numerical names) - else: - children_bool = nodes @ adj[:, current[0]-1] # vector of zeros and parent names (numerical names) - children = children_bool[np.nonzero(children_bool)] #list of just the parent names (numerical names) - - - for child in children: # For every child of the current node that was found above... - # Check if the child has been visited or if it is in the same generation as child. If either not visited or - # in the same generation, continue with the following: - # print("child", child) - - if nodeList[child - 1] == False or same_layer(gens[nodeNames[child - 1]]['layer'], current[1], type_poc): - - # Add the child to the graph, and to the array of node names - G.add_node(nodeNames[child-1]) - if nodeNames[child - 1] in names_of_nodes: - x = 14 - else: - names_of_nodes.append(nodeNames[child - 1]) - - # Determine if the child is an effect or mode and add it to the correct array - '''if child < 27: effects.append(nodeNames[child - 1]) - else: modes.append(nodeNames[child - 1])''' - if child < 49: modes.append(nodeNames[child - 1]) - else: effects.append(nodeNames[child - 1]) - - # Add an edge between the current node and child in the tree we are building - if type_poc == "child": - G.add_edge(nodeNames[current[0]-1], nodeNames[child-1]) - else: - G.add_edge(nodeNames[child-1], nodeNames[current[0]-1]) - - queue.append([child, layer_fcn(current[1], type_poc)]) # Append the child to the queue - nodeList[child - 1] = True # Change the status of the child to say we have visited it - gens.update({nodeNames[child-1]: {"layer": layer_fcn(current[1], type_poc)}}) # Add child to dictionary of nodes - - # Remove the current node from the queue - queue = queue[1:] - - # Return the tree we made, its adjacency matrix, the dictionary of nodes, the effects array, and the modes arrat - return G, nx.to_numpy_array(G), gens, effects, modes, names_of_nodes - - -'''# **** Below code is still being worked on ***** - -arr, nodeNames = excel2Matrix("failureData.xlsx", "bigMatrix") -source = 11 -G = nx.DiGraph() - -effects = [] -modes = [] - -G.add_node(nodeNames[source - 1]) - -if source < 49: modes.append(nodeNames[source - 1]) -else: effects.append(nodeNames[source - 1]) - -adj = make_binary(arr).astype(int) - -nodeList = np.reshape(np.repeat(False, arr.shape[0]), (arr.shape[0], 1)) -nodes = diagonal_nodes(adj) - -nodeList[source-1] = True -queue = [[source, 0]] -gens = {nodeNames[source-1]: {"layer": 0}} - -while len(queue) > 0: - current = queue[0] - - children_bool = adj[current[0]-1] @ nodes # vector of zeros and child names (numerical names) - children = children_bool[np.nonzero(children_bool)] #list of just the child names (numerical names) - - for child in children: - if nodeList[child - 1] == False or gens[nodeNames[child - 1]]['layer'] > current[1]: - G.add_node(nodeNames[child-1]) - - if child < 27: effects.append(nodeNames[child - 1]) - else: modes.append(nodeNames[child - 1]) - - G.add_edge(nodeNames[current[0]-1], nodeNames[child-1]) - - queue.append([child, current[1]+1]) # Append the child to the queue - nodeList[child - 1] = True # Change the status of the child to say we have visited it - gens.update({nodeNames[child-1]: {"layer": current[1]+1}}) # Add child to dictionary of nodes - - queue = queue[1:]''' \ No newline at end of file diff --git a/twoTurbineCaseStudy.py b/twoTurbineCaseStudy.py deleted file mode 100644 index f49be228..00000000 --- a/twoTurbineCaseStudy.py +++ /dev/null @@ -1,76 +0,0 @@ -import numpy as np -from graphBuilder import * -from searchMethods import * - -''' twoTurbineCaseStudy.py ----------------------------------------------------------------------------------------------- - - ****************************** Last Updated: 18 April 2024 ****************************** - - Methods: - 1) twoTurbine_bayesian_table: input adjusted adjacency matrix, adjacency matrix, current node, list of node names, - adjusted list of node names --> outputs list of parents, probability table - ------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------ - - - twoTurbine_bayesian_table Documentation - ------------------------------------------ - This method inputs an adjusted adjacency matrix, regular adjacency matrix, current node (integer), array of node names (strings), - and adjusted node names (same indexing as adjusted adjacency matrix). We then calculate the probability table for the current node - given its parents in the adjusted adjacency matrix. We use the weights from the regular adjacency matrix to determine the transitional - probabilities between nodes. We output an array of the current node's parents and its probability table.''' - - -def twoTurbine_bayesian_table(a, arr, current, nodeNames, non): - # arr, nodeNames = excel2Matrix("ExcelFiles/failureData.xlsx", "twoTurbines_simplified") # For debugging, feel free to uncomment - adj = a.copy() - nodes = diagonal_nodes(adj) # Diagonal matrix of node's numerical names +1 - adj = make_binary(adj, 0) # Binarize adjacency matrix - parents_bool = nodes @ adj[:, current-1] # vector of zeros and parent names (numerical names) - parents = list(parents_bool[np.nonzero(parents_bool)]) # list of just the parent names (numerical names) - prob_table = np.zeros((2 ** len(parents), len(parents) + 2)) # Initialize the array for the node's conditional probability distribution - - current_index = np.where(nodeNames == non[int(current - 1)])[0][0] - - for iteration in range(2 ** len(parents)): # For each combination of parents having either failed or not - true_false_array = [] # Iniitalize to record which parents have failed - - for p in range(len(parents)): - parent_index = np.where(nodeNames == non[int(parents[p] - 1)])[0][0] - prob_table[iteration][p] = int(iteration / (2 ** p)) % 2 # Append whether or not the parent node has failed - prob_table[iteration][-2] += (1- int(iteration / (2 ** p)) % 2) * arr[parent_index][current_index] # Determine parent probability (given if the parent has failed or not) - true_false_array.append((int(iteration / (2 ** p)) % 2)) # Append whether or not the parent node has failed - - if prob_table[iteration][-2] > 1: # If the value is greater than 1, set the probability to 1 - prob_table[iteration][-2] = 1 - prob_table[iteration][-1] = 1 - prob_table[iteration][-2] # Add the probability of the node not failing to the array - # print(prob_table) - return parents, prob_table - - - -''' -# The following code is for writing excel file with all pair-wise probabilities. To run, copy and paste the code below into main.py and hit 'Run.' - -adjacency_matrix, nodeNames = excel2Matrix("ExcelFiles/failureData.xlsx", "twoTurbines_simplified") -current = 1 -midpoint = True -num_iterations = 1 -probabilities, nodeNames = getProbabilities("ExcelFiles/failureProbabilities.xlsx", sheetName = "Conditional Probabilities (2)") -all_probs = np.zeros(adjacency_matrix.shape) -with pd.ExcelWriter("bayesian_simulations3.xlsx") as writer: - for i in range(1, len(nodeNames) + 1): - print("----------------------------------", i, "----------------------------------") - array_of_probs = np.zeros((len(nodeNames), 2)) - for j in range(1,len(nodeNames)+1): - adjacency_matrix2 = adjacency_matrix.copy() - probabilities = np.array(probabilities).copy() - A, B = backward_bayesian_inference(adjacency_matrix2, nodeNames, [i], [i], [j], probabilities, start_bool = True, multi = False, poc="child", twoTurbine = True) - array_of_probs[j-1] = A - df = pd.DataFrame(np.array(array_of_probs)) - df.to_excel(writer, sheet_name="Probabilities given "+str(i)) - all_probs[:,i-1] = array_of_probs[:,0] - df2 = pd.DataFrame(np.array(all_probs)) - df2.to_excel(writer, sheet_name="allProbs") -''' \ No newline at end of file From 13e9b6e73937c37d618dcca91cd8bad6fb7c7e1c Mon Sep 17 00:00:00 2001 From: Matt Hall <5151457+mattEhall@users.noreply.github.com> Date: Tue, 4 Jun 2024 21:01:31 -0600 Subject: [PATCH 07/24] Mooring edits for smoother connector information, and a few other things - Mooring: added setSectionType method to adjust lineType dictionary. - Mooring: changed up some connector property handling. Subsystem.makeGeneric now receives a list of connector dict information (see update to MoorPy). - famodel_base: new Edge.delete method, not very useful. - Ontology readme: minor adjustments to metocean inputs. - Made RAFT import in Project optional (for cases that may not need it). --- famodel/famodel_base.py | 9 ++++++++ famodel/mooring/mooring.py | 43 ++++++++++++++++++++++++++++++-------- famodel/ontology/README.md | 35 +++++++++++++++++++++++-------- famodel/project.py | 5 ++++- 4 files changed, 73 insertions(+), 19 deletions(-) diff --git a/famodel/famodel_base.py b/famodel/famodel_base.py index caddc939..7d73a7d3 100644 --- a/famodel/famodel_base.py +++ b/famodel/famodel_base.py @@ -568,6 +568,15 @@ def setEndPosition(self, r, end): raise Exception('End A or B must be specified with either the letter, 0/1, or False/True.') + def delete(self): + '''Detach the point from anything it's attached to, then delete the + object (if such a thing is possible?).''' + + self.detachFrom(0) # detach end A + self.detachFrom(1) # detach end B + + # next step would just be to separately remove any other references + # to this object... # general functions diff --git a/famodel/mooring/mooring.py b/famodel/mooring/mooring.py index f8eb25be..1053874a 100644 --- a/famodel/mooring/mooring.py +++ b/famodel/mooring/mooring.py @@ -54,6 +54,14 @@ def __init__(self, dd=None, subsystem=None, anchor=None, rA=[0,0,0], rB=[0,0,0], called. <<< ''' + + # temporary hack to deal with duplicate parameters + if not 'zAnchor' in dd: + dd['zAnchor'] = z_anch + if not 'span' in dd: + dd['span'] = rad_anch - rad_fair + + Edge.__init__(self, id) # initialize Edge base class # Design description dictionary for this Mooring self.dd = dd @@ -65,7 +73,7 @@ def __init__(self, dd=None, subsystem=None, anchor=None, rA=[0,0,0], rB=[0,0,0], # Turn what's in dd and turn it into Sections and Connectors for i, con in enumerate(self.dd['connectors']): - if con: + if con and 'type' in con: Cid = con['type']+str(i) else: Cid = 'Conn'+str(i) @@ -76,8 +84,10 @@ def __init__(self, dd=None, subsystem=None, anchor=None, rA=[0,0,0], rB=[0,0,0], #self.dd['connectors'][i ].attach(self.dd['sections'][i], end=0) #self.dd['connectors'][i+1].attach(self.dd['sections'][i], end=1) + # >>> add some error checks for the correct lengths <<< + # Connect them and store them in self(Edge).subcomponents! - subcons = [] # list of node-edge-node... to pass to the function + subcons = [] # temporary list of node-edge-node... to pass to the function for i in range(self.n_sec): subcons.append(self.dd['connectors'][i]) subcons.append(self.dd['sections'][i]) @@ -135,6 +145,17 @@ def setSectionDiameter(self, d, i): self.sectionType[i].update( setLineType( ... d)) """ + def setSectionType(self, lineType, i): + '''Sets lineType of section, including in the subdsystem + if there is one.''' + + # set type dict in dd (which is also Section/subcomponent) + self.dd['sections'][i]['type'] = lineType + + if self.ss: # is Subsystem exists, adjust length there too + self.ss.lineTypes[i] = lineType + + def reposition(self, r_center=None, heading=None, project=None, degrees=False, **kwargs): '''Adjusts mooring position based on changed platform location or heading. It can call a custom "adjuster" function if one is @@ -243,14 +264,18 @@ def createSubsystem(self, case=0): lengths = [] types = [] # run through each line section and collect the length and type - for sec in self.dd['sections']: + for i, sec in enumerate(self.dd['sections']): lengths.append(sec['length']) - types.append(sec['type']['name']) - self.ss.lineTypes[types[-1]] = sec['type'] # points to existing type dict in self.dd for now + # points to existing type dict in self.dd for now + types.append(sec['type']) # list of type names + #types.append(sec['type']['name']) # list of type names + #self.ss.lineTypes[i] = sec['type'] # make the lines and set the points - self.ss.makeGeneric(lengths,types,suspended=case) + self.ss.makeGeneric(lengths, types, + connectors=[self.dd['connectors'][ic+1] for ic in range(len(self.dd['connectors'])-2)], + suspended=case) self.ss.setEndPosition(self.rA,endB=0) self.ss.setEndPosition(self.rB,endB=1) @@ -265,13 +290,13 @@ def createSubsystem(self, case=0): for i in range(startNum,len(self.ss.pointList)): point = self.ss.pointList[i] - point.m = self.dd['connectors'][i]['m'] - point.v = self.dd['connectors'][i]['v'] + # point.m = self.dd['connectors'][i]['m'] # now done in ss.makeGeneric + # point.v = self.dd['connectors'][i]['v'] # now done in ss.makeGeneric point.CdA = self.dd['connectors'][i]['CdA'] # solve the system self.ss.staticSolve() - return(self.ss) + return(self.ss) """ # rough method ideas...maybe not necessary or can use existing dict methods diff --git a/famodel/ontology/README.md b/famodel/ontology/README.md index b8169d8d..6a96fbb7 100644 --- a/famodel/ontology/README.md +++ b/famodel/ontology/README.md @@ -19,7 +19,7 @@ better suit the scope and emphasis of floating wind arrays. The sections are as * [General ](#general) * [Boundaries ](#boundaries) * [Exclusions ](#exclusions) - * [Seabed ](#bathymetry) + * [Bathymetry ](#bathymetry) * [Seabed ](#seabed) * [Metocean ](#metocean) * [Resource ](#resource) @@ -136,14 +136,31 @@ csv filename. ```yaml metocean: extremes: # extreme values for specified return periods (in years) - keys : [ Hs , Tp , WindSpeed, TI, Shear, Gamma, CurrentSpeed ] - data : - 1: [ , , ] - 10: [ , , ] - 50: [ , , ] - 500: [ , , ] + return periods : [ 1, 5, 50 ] # in years + all : # unconditional extreme values + Hs: [ , , ] + Tp: [ , , ] + Gamma: [ , , ] + WS: [ , , ] + TI: [ , , ] + Shear: [ , , ] + CS: [ , , ] + 8.5 9.8, 10.4, 11.8, 12.4, 13.7, 16.8, 18.1, 18.6, 19.8, 20.3, 21.4 + - probabalistic_bins: + WS_2_4 : # conditional values from wind speed range of 2 - 4 m/s + Hs: [ , , ] + Tp: [ , , ] + Gamma: [ , , ] + TI: [ , , ] + Shear: [ , , ] + CS: [ , , ] + + WS_4_6 : # conditional values from wind speed range of 4 - 6 m/s + ... + + + joint_probabality_bins: # a set of cases representing the joint metocean probability distribution keys : [ prob , Hs , Tp, WindSpeed, TI, Shear, Gamma, CurrentSpeed, WindDir, WaveDir, CurrentDir ] data : - [ 0.010 , , ] @@ -368,7 +385,7 @@ an 80m section of poly_180, a clump weight, a 762 m section of poly_180 (note th taut-poly_1: # mooring line configuration identifier - name: Taut polyester configuration 1 # descriptive name + name: Taut polyester configuration 1 # descriptive name anchoring_radius: 1131.37 fairlead_radius: 40.5 diff --git a/famodel/project.py b/famodel/project.py index a2796b3f..bdc70300 100644 --- a/famodel/project.py +++ b/famodel/project.py @@ -10,7 +10,10 @@ from moorpy import helpers import yaml from copy import deepcopy -import raft +try: + import raft +except: + pass #from shapely.geometry import Point, Polygon, LineString From b970fff7a5c739e7826f86b622945a86161563d2 Mon Sep 17 00:00:00 2001 From: Sirkis Date: Thu, 6 Jun 2024 09:02:43 -0600 Subject: [PATCH 08/24] Update Readme, add failure probability dictionaries --Update Ontology readme to include up-to-date information on cables/cables --Included a dictionary with failure probabilities (currently empty) for each object --Added project function shell to update failure probability dictionaries based on Emma Slack's probability calculations --- famodel/OntologySample600m.yaml | 4 - famodel/cables/cable.py | 3 + famodel/cables/components.py | 3 + famodel/cables/dynamic_cable.py | 1 + famodel/cables/static_cable.py | 1 + famodel/mooring/anchor.py | 1 + famodel/mooring/connector.py | 4 + famodel/mooring/mooring.py | 19 +++ famodel/ontology/README.md | 223 ++++++++++++++++++------------- famodel/platform/platform.py | 1 + famodel/project.py | 13 ++ famodel/substation/substation.py | 3 + 12 files changed, 177 insertions(+), 99 deletions(-) diff --git a/famodel/OntologySample600m.yaml b/famodel/OntologySample600m.yaml index 45c32141..448f2dfa 100644 --- a/famodel/OntologySample600m.yaml +++ b/famodel/OntologySample600m.yaml @@ -1591,13 +1591,9 @@ cables: sections: # refers to a configuration in cable_configs # first entry is at end A, last entry is at end B - type: dynamic_lazy_wave1 - - connectorType: joint_1 - - type: static_1 - - connectorType: joint_1 - - type: dynamic_lazy_wave1 diff --git a/famodel/cables/cable.py b/famodel/cables/cable.py index 1c53af07..8c7e6b14 100644 --- a/famodel/cables/cable.py +++ b/famodel/cables/cable.py @@ -94,6 +94,9 @@ def __init__(self, id, d=None): for i in self.dd['cables']: # self.subcomponents: self.L += i.L + # failure probability + self.failure_probability = {} + def reposition(self): # reposition cable and set end points for the first and last cable sections (or the dynamic cable for a suspended cable) headingA = self.subcomponents[0].headingA + self.attached_to[0].phi diff --git a/famodel/cables/components.py b/famodel/cables/components.py index af6a7254..9f108339 100644 --- a/famodel/cables/components.py +++ b/famodel/cables/components.py @@ -32,6 +32,9 @@ def __init__(self,id, r=None,**kwargs): # MoorPy Point Object for Joint self.mpConn = None + # Dictionary for failure probability + self.failure_probability = {} + #this might be useful for the ends of dynamic cables def makeMoorPyConnector(self, ms): '''Create a MoorPy connector object in a MoorPy system diff --git a/famodel/cables/dynamic_cable.py b/famodel/cables/dynamic_cable.py index 73b4ebae..5aa78262 100644 --- a/famodel/cables/dynamic_cable.py +++ b/famodel/cables/dynamic_cable.py @@ -97,6 +97,7 @@ def __init__(self, id, dd=None, subsystem=None, anchor=None, rA=[0,0,0], rB=[0,0 self.loads = {} self.reliability = {} self.cost = {} + self.failure_probability = {} def makeCableType(self,cabDict): ''' diff --git a/famodel/cables/static_cable.py b/famodel/cables/static_cable.py index a3dbdfd1..b04ba8bf 100644 --- a/famodel/cables/static_cable.py +++ b/famodel/cables/static_cable.py @@ -59,6 +59,7 @@ def __init__(self, id, dd=None, subsystem=None, anchor=None, rA=[0,0,0], rB=[0,0 self.loads = {} self.reliability = {} self.cost = {} + self.failure_probability = {} def getLength(self): diff --git a/famodel/mooring/anchor.py b/famodel/mooring/anchor.py index 29732090..8de6b873 100644 --- a/famodel/mooring/anchor.py +++ b/famodel/mooring/anchor.py @@ -71,6 +71,7 @@ def __init__(self, dd=None, ms=None, r=[0,0,0], aNum=None,id=None): } ''' self.soilProps = {} + self.failure_probability = {} # self.cost = {} def makeMoorPyAnchor(self, ms): diff --git a/famodel/mooring/connector.py b/famodel/mooring/connector.py index 510363aa..0962fad1 100644 --- a/famodel/mooring/connector.py +++ b/famodel/mooring/connector.py @@ -38,6 +38,9 @@ def __init__(self,id, r=[0,0,0], **kwargs): # MoorPy Point Object for Connector self.mpConn = None + # dictionary of failure probabilities + self.failure_probability = {} + def makeMoorPyConnector(self, ms): '''Create a MoorPy connector object in a MoorPy system Parameters @@ -59,6 +62,7 @@ def makeMoorPyConnector(self, ms): self.mpConn.m = self['m'] self.mpConn.v = self['v'] self.mpConn.CdA = self['CdA'] + return(ms) diff --git a/famodel/mooring/mooring.py b/famodel/mooring/mooring.py index 1053874a..1d9d5f71 100644 --- a/famodel/mooring/mooring.py +++ b/famodel/mooring/mooring.py @@ -609,3 +609,22 @@ def addMarineGrowth(self, mgDict, project=None, idx=None): return(changeDepths,changePoints) + def addCorrosion(self,corrosion_mm=10): + ''' + Calculates MBL of chain line with corrosion included + + Parameters + ---------- + corrosion_mm : TYPE, optional + DESCRIPTION. The default is 10. + + Returns + ------- + None. + + ''' + for i,sec in enumerate(self.dd['sections']): + if sec['material']=='chain': + MBL_cor = sec['MBL']*( (sec['d_nom']-(corrosion_mm/1000))/sec['d_nom'] )**2 # corroded MBL + else: + MBL_cor = sec['MBL'] \ No newline at end of file diff --git a/famodel/ontology/README.md b/famodel/ontology/README.md index 6a96fbb7..08bc2dcf 100644 --- a/famodel/ontology/README.md +++ b/famodel/ontology/README.md @@ -29,6 +29,7 @@ better suit the scope and emphasis of floating wind arrays. The sections are as * [Array Layout ](#array-layout) * [Array Mooring ](#array-mooring) * [Array Cables ](#array-cables) +* [Substation ](#substation) * [Turbine(s) ](#turbines) * [Platform(s) ](#platforms) * [Mooring ](#mooring) @@ -38,10 +39,11 @@ better suit the scope and emphasis of floating wind arrays. The sections are as * [Mooring Connectors ](#mooring-connectors) * [Anchor types ](#anchor-types) * [Cables ](#cables) - * [Cables with Routing ](#cables-with-routing) - * [Dynamic Cable Configurations ](#dynamic-cable-configurations) - * [Cable Cross Sectional Properties](#cable-cross-sectional-properties) + * [Top Level Cables ](#top-level-cables) + * [Cable Configurations ](#cable-configurations) + * [Cable Cross Sectional Properties](#cable-types) * [Cable Appendages ](#cable-appendages) + * [Cable Joints ](#cable-joints) The following sections give an overview of the array ontology makeup with examples. @@ -263,22 +265,26 @@ array_mooring: This section provides a straightforward and compact way to define the power cables in the array. For each end (A and B) of the cable, it specifies the -turbine attached to, the -[dynamic cable configuration](#dynamic-cable-configurations) used, and the heading -of the dynamic cable. The type of the static cable is also specified. -This method does not consider routing, and would assume the static cable takes -a straight path from the ends of the dynamic cables. -For additional detail related to cable routing, the alternative [cable](#cables-with-routing) -section should be used. +turbine (matching and ID in the array table) or substation (matching an ID in the substation section) +attached to, the [Top Level Cables](#top-level-cables) +used, the heading of the cable at the attachment of each end, and length adjustment. +Additional detail related to subsections of the top level cable, joints, and any cable +routing are in the [Cables](#cables) section. +The CableID refers to an entry in the [Top Level Cables](#top-level-cables) section. ```yaml -array_cables: - keys: [ AttachA, AttachB, DynCableA, DynCableB, headingA, headingB, cableType] +array_cables: + keys: [ CableID, AttachA, AttachB, headingA, headingB, lengthAdjust] data: - - [ turbine1, turbine2, lazy_wave1, lazy_wave1, 180, 30, static_36] - - [ turbine2, turbine3, lazy_wave1, lazy_wave1, 150, 30, static_36] + - [ array_cable1, fowt1, f2, 180, 180, 0] + - [ suspended_cable1, f2, substation1, 0, 180, 0] ``` +## Substation(s) + +The substation section defines the substations used in the array. The substation key (name) must be unique +from the ID keys in the array layout table. + ## Turbine(s) The turbine section can contain either a single turbine or a list of turbines, @@ -375,7 +381,7 @@ There is also a True/False options for whether the section length is adjustable. Shared or suspended lines may also have an optional 'symmetric' input which, if set to true, signifies that the line is symmetric and only the first half of the line is provided in the 'sections' list. When loaded in to the project class, the mooring object will automatically be fully filled out by mirroring the first half of the line. If a connector is provided as the last item in the sections list for a symmetric line, it is assumed that the middle line is two identical lines with the given connector between, otherwise -the middle line (last line given in the list) is doubled in length in the mirroring process. For example, the 'shared_2_clump' config in the yaml below would produce a symmetric shared line with sections in the following order: +the middle line (last line given in the list) is doubled in length in the mirroring process. For example, the 'shared_2_clump' config in the yaml below would produce a symmetric shared line with sections in the following order an 80m section of poly_180, a clump weight, a 762 m section of poly_180 (note the doubled length), a clump weight, and finally an 80 m section of poly_180. @@ -508,100 +514,116 @@ anchor_types: This section describes the cables through the array including both static and dynamic portions of cables. At the top level, each array cable going -between a pair of turbines (or a turbine and a substation) is defined. -This definition can either occur in the [array_cables](#array-cables) -section or the [cables](#cables-with-routing) section. -The latter provides additional options for defining -the cable routing and burial depth. - -### Cables with Routing - -The cables section contains a list of every cable in the array. Here, a cable -is defined as the full assembly of electrical connection equipment between -two turbines or a turbine and a substation. Similar to the [array_cables](#array-cables) -section, 'type' links to the cross-section property description of the static -portion of the cable. endA and endB sections define what each end of the cable -is attached to, at what heading it comes off at, and what dynamic cable -profile it uses. Additional fields specify the routing of the static portion -of the cable and the burial depth as as function of cable length. +between a pair of turbines (or a turbine and a substation) is defined. Each +subsection of cable is then defined in the [Cable Configurations](#cable-configs) +section, including buoyancy module layout. + + +### Top Level Cables + +A top-level cableis defined as the full assembly of electrical connection equipment between +two turbines or a turbine and a substation. 'type' links to the cable configuration description +of the subsections of the cable, which can be dynamic or static cables. The 'connectorType' refers to +an entry in the [Cable Joints](#cable-joints) section. The first entry in the sections list is connected +to end A, while the last entry is connected to end B. ```yaml cables: - - name : array_cable1 # descriptive cable name - type : static_cable_80 # cable section type ID - - endA: - attachID: turbine_1 # FOWT/substation/junction ID - heading: 180 # [deg] heading of attachment at end A - dynamicID: dynamic_lazy_wave1 # ID of dynamic cable configuration at this end - - endB: - attachID: turbine_2 # FOWT/substation/junction ID - heading: 30 # [deg] heading of attachment at end B - dynamicID: dynamic_lazy_wave1 # ID of dynamic cable configuration at this end - - routing_x_y_r: # optional vertex points along the cable route. Nonzero radius wraps around a point at that radius. - - [1000, 1200, 20] - - [2000, 1500, 20] - - burial: # optional definition of cable burial depth over its length - station: [0, 1] # length along cable, normalized by first and last value - depth : [0.1, 0.2] # [m] burial depth + array_cable1: + name: array cable with lazy wave - static - lazy wave sections # descriptive cable name + # type : static_cable_80 # cable section type ID + + sections: # refers to a configuration in cable_configs + # first entry is at end A, last entry is at end B + - type: dynamic_lazy_wave1 + + - connectorType: joint_1 + + - type: static_1 + + - connectorType: joint_1 + + - type: dynamic_lazy_wave1 - - name : array_cable_2 # descriptive cable name - type : static_cable_80 # cable section type ID - ... + + suspended_cable1: + sections: + - type: dynamic_suspended_1 ``` -## Dynamic Cable Configurations +### Cable Configurations -This section lists the dynamic cable configurations used in the array design. -Similar to the mooring_line_configs section, it details the assembly of -cable section that make up a dynamic cable profile, with links to the cross -sectional cable properties. Dynamic cable configurations have some special -properties including specification of the voltage, and the option of -specifying 'appendages' along the cable length, which can represent [discrete -objects](#cable-appendages) like buoyancy modules. +This section lists the cable configurations used in the array design. +The 'type' listed in the entry is either 'static' or 'dynamic'. +The 'cableFamily' key is used when the cable cross-sectional information +will be imported from the cableProps_default yaml (in which case, an area A +must be provided in mm^2), and the value for cableFamily must match an entry in the cableProps_default yaml. +Alternatively, if the cable cross-sectional properties will be provided in the [Cable Cross Sectional Properties](#cable-cross-sectional-properties) +section, the key 'typeID' will be used in place of 'cableFamily', and will +refer to an entry in the Cable Cross Sectional Properties list. -```yaml - dynamic_cable_configs: +The sections list provides details on the layout of buoyancy modules and clump weights, including the +distance of the buoyancy section midpoint from end A, the number of modules, the spacing between modules, +and the volume. The volume is only needed if the buoyancy module properties will be imported +from the cableProps_defaul yaml. As with the cable properties, the 'type' in the sections list must refer to +an entry in either the [Cable Appendages](#cable-appendages) section or in the cableProps_default.yaml. + +Static cables can have routing information listed as vertex points along the cable route, and the radius of curve. +Static cable burial information can also be provided. - lazy_wave1 +Similar to mooring lines, the span refers to the end to end distance of the line in the x-y plane. + +```yaml + basic1: + name: basic cable configuration, essentially a straight line between platforms + span: 1600 # [m] + type: dynamic + zJTube: -30 # depth out of J-tube that cable starts in m + + cableFamily: dynamic_cable_33 + length: 1700 # [m] + A: 100 # cable conductor cross-sectional area [mm^2] (Required for types listed in cable props yaml) + + + + + dynamic_lazy_wave1: name: Lazy wave configuration 1 (simpler approach) voltage: 66 # [kV] - span : # [m] horizontal distance to end of dynamic cable + span : 600 # [m] horizontal distance to end of dynamic cable + zJTube: -20 # depth the cable comes out of the j-tube + type: dynamic # dynamic or static + A: 300 - sections: - - type: dynamic_cable_27 # ID of a cable section type1 - length: 200 # [m] length (unstretched) - - - type: dynamic_cable_27_w_buoy # (section properties including averaged effect of buoyancy modules) - length: 300 - - - type: dynamic_cable_27 - length: 200 - - attachment: - type: j-tube - coordinate: # relative location - - lazy_wave2 - name: Lazy wave configuration 1 (more detailed approach) - voltage: # [kV] - span : # [m] horizontal distance to end of dynamic cable + cableFamily: dynamic_cable_33 # ID of a cable section type1 + length: 900 # [m] length (unstretched) + + sections: + - type: Buoyancy_750m #_w_buoy # (section properties including averaged effect of buoyancy modules) + L_mid: 450 # [m] from end A + N_modules: 5 # number of modules in this buoyancy section + spacing: 21 # [m] + V: 2 # [m^2] + + + static_1: + name: Static cable configuration 1 + voltage: 66 # [kV] + span: 350 + type: static - sections: - - type: dynamic_cable_27 # ID of a cable section type1 - length: 200 # [m] length (unstretched) - appendages: - type: buoyancy_module_1 - locations: [10,12,13.5,15,18] - - attachment: - type: j-tube - coordinate: # relative location + typeID: static_cable_36 + length: 2200 + + routing_x_y_r: # optional vertex points along the cable route. Nonzero radius wraps around a point at that radius. + - [1000, 1200, 20] + - [2000, 1500, 20] + + burial: # optional definition of cable burial depth over its length + station: [0, 1] # length along cable, normalized by first and last value + depth : [0.1, 0.2] # [m] burial depth ``` ### Cable Cross Sectional Properties @@ -643,7 +665,7 @@ such as buoyancy modules or cable protection system components. Each entry is given an identifier and can have a variety of parameters that describe its lumped properties, such as mass, volume, and drag coefficient-area product. These appendages are used in the -[dynamic_cable_configs](#dynamic-cable-configurations) section. +[cable_configs](#cable-configurations) section. ```yaml cable_appendages: @@ -654,3 +676,14 @@ product. These appendages are used in the CdA: 3.8 # [m^2] product of cross-sectional area and drag coefficient length: 2.2 # [m] length taked up along cable ``` + +### Cable Joints + +This section lists any cable joints that might connect cable subsections. Each entry is given +and identifier and parameters to describe the joint. These joints are used in the [Top Level Cables] +(#top-level-cables) section. +```yaml +cable_joints: + joint_1: + mass: 100000 # TBD +``` diff --git a/famodel/platform/platform.py b/famodel/platform/platform.py index d91c14a8..ddf27aad 100644 --- a/famodel/platform/platform.py +++ b/famodel/platform/platform.py @@ -55,6 +55,7 @@ def __init__(self, id, r=[0,0], heading=0, mooring_headings=[60,180,300],rFair=N self.loads = {} self.reliability = {} self.cost = {} + self.failure_probability = {} def setPosition(self, r, heading=None, degrees=False): diff --git a/famodel/project.py b/famodel/project.py index bdc70300..de0d51d4 100644 --- a/famodel/project.py +++ b/famodel/project.py @@ -2090,6 +2090,19 @@ def getMarineGrowth(self,mgDict_start,lines='all',tol=2): # assign the newly created subsystem into the right place in the line list self.ms.lineList[ii] = self.mooringList[i].subsystem + def updateFailureProbability(self): + ''' + Function to populate (or update) failure probability dictionaries in each object + based on failure probability calculations developed by Emma Slack + + To be filled in... + + Returns + ------- + None. + + ''' + def getFromDict(dict, key, shape=0, dtype=float, default=None, index=None): ''' Function to streamline getting values from design dictionary from YAML file, including error checking. diff --git a/famodel/substation/substation.py b/famodel/substation/substation.py index a4174085..e15bd078 100644 --- a/famodel/substation/substation.py +++ b/famodel/substation/substation.py @@ -22,4 +22,7 @@ def __init__(self, dd, id): self.zFair = None self.point = None + # dictionary of failure probability + self.failure_probability = {} + # further functionality to be added later \ No newline at end of file From bdd35524a22a70cc1694039c012ef7332f963ebb Mon Sep 17 00:00:00 2001 From: Sirkis Date: Thu, 6 Jun 2024 09:09:30 -0600 Subject: [PATCH 09/24] Same as previous commit with turbine failure prob added --getCorrosion function was also added in last commit for mooring.py (specifically calculating corrosion for chains), pulled from --- famodel/turbine/turbine.py | 1 + 1 file changed, 1 insertion(+) diff --git a/famodel/turbine/turbine.py b/famodel/turbine/turbine.py index d242a7b7..0ae4aee1 100644 --- a/famodel/turbine/turbine.py +++ b/famodel/turbine/turbine.py @@ -30,6 +30,7 @@ def __init__(self, dd): self.loads = {} self.reliability = {} self.cost = {} + self.failure_probability = {} def makeRotor(self): From e1bdfddd09e7adc40d3876fec3673b11786c4f5c Mon Sep 17 00:00:00 2001 From: Sirkis Date: Thu, 6 Jun 2024 09:38:22 -0600 Subject: [PATCH 10/24] Same as previous commit with Mooring failure probability dict added --- famodel/mooring/mooring.py | 1 + 1 file changed, 1 insertion(+) diff --git a/famodel/mooring/mooring.py b/famodel/mooring/mooring.py index 1d9d5f71..33d6bf71 100644 --- a/famodel/mooring/mooring.py +++ b/famodel/mooring/mooring.py @@ -125,6 +125,7 @@ def __init__(self, dd=None, subsystem=None, anchor=None, rA=[0,0,0], rB=[0,0,0], self.loads = {} self.reliability = {} self.cost = {} + self.failure_probability = {} def setSectionLength(self, L, i): From e5b3c1a8b1f1bca0fc8f6821749f417d8a0e973d Mon Sep 17 00:00:00 2001 From: Sirkis Date: Thu, 6 Jun 2024 10:49:51 -0600 Subject: [PATCH 11/24] Minor fix to ontology Readme --- famodel/ontology/README.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/famodel/ontology/README.md b/famodel/ontology/README.md index 08bc2dcf..9efb17a8 100644 --- a/famodel/ontology/README.md +++ b/famodel/ontology/README.md @@ -12,7 +12,7 @@ is aligned with the work of IEA Wind Task 49, which focuses on integrated design of floating wind arrays. The ontology proposed here draws on elements from two established ontologies developed under a previous IEA Wind Task. Task 37 developed [plant-level and turbine-level ontologies](https://windio.readthedocs.io). -The current floating array ontology has a number of additions and differences that +The current floating array ontology has a number of additions and differences that better suit the scope and emphasis of floating wind arrays. The sections are as follows: * [Site](#site) @@ -369,7 +369,8 @@ mooring_systems: ### Mooring Line Configurations The mooring line configurations lists the segment lengths and line types that make up each mooring line. Each line has a name that can then be specified -as the MooringConfigID in the mooring systems section. The anchoring radius (also known as the span), fairlead radius, and fairlead depth are also specified for each line configuration. +as the MooringConfigID in the mooring systems section. The span is specified for each configuration, which represents the distance in the x-y plane between +the two connection points of the line - i.e. between fairlead and anchor, or for shared lines, fairlead and fairlead. Fairlead radius and fairlead depth are specified in the [Platform](#platforms) section. Each line contains a list of sections that details the line section type and length. The line type name connects to information in the mooring [line section properties](#mooring-line-section-properties). Additionally, before and after each line section has an optional input which can list the @@ -393,9 +394,7 @@ an 80m section of poly_180, a clump weight, a 762 m section of poly_180 (note th name: Taut polyester configuration 1 # descriptive name - anchoring_radius: 1131.37 - fairlead_radius: 40.5 - fairlead_depth: -20 + span: 800 # x-y distance between fairlead and anchor, or for shared lines, fairlead and fairlead sections: - connectorType: shackke # ID of a connector type (optional) @@ -415,9 +414,7 @@ an 80m section of poly_180, a clump weight, a 762 m section of poly_180 (note th name: Shared line with two clump weights symmetric: True - anchoring_radius: 1142 - fairlead_radius: 58 - fairlead_depth: -14 + span: 1142 sections: - type: poly_180 From 5f9d21b05be35acf8d5f33aa03f9bbe0f1a6c969 Mon Sep 17 00:00:00 2001 From: Sirkis Date: Thu, 6 Jun 2024 11:33:15 -0600 Subject: [PATCH 12/24] Remove redundancies in mooring --pass in zFair, rFair, span for mooring object through design dictionary instead of through init --calculate rad_Fair based on span and rFair --Assign rA and rB --- famodel/mooring/mooring.py | 10 +++++---- famodel/project.py | 44 +++++++++++++++++++++++++------------- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/famodel/mooring/mooring.py b/famodel/mooring/mooring.py index 33d6bf71..11403737 100644 --- a/famodel/mooring/mooring.py +++ b/famodel/mooring/mooring.py @@ -41,6 +41,8 @@ def __init__(self, dd=None, subsystem=None, anchor=None, rA=[0,0,0], rB=[0,0,0], ] span zAnchor + z_fair + rad_fair EndPositions: { endA, endB @@ -107,10 +109,10 @@ def __init__(self, dd=None, subsystem=None, anchor=None, rA=[0,0,0], rB=[0,0,0], self.heading = 0 # relative positions (variables could be renamed) - self.rad_anch = rad_anch - self.rad_fair = rad_fair - self.z_anch = z_anch - self.z_fair = z_fair + self.rad_anch = dd['span'] + dd['rad_fair'] + self.rad_fair = dd['rad_fair'] + self.z_anch = dd['zAnchor'] + self.z_fair = dd['z_fair'] self.adjuster = None # custom function that can adjust the mooring diff --git a/famodel/project.py b/famodel/project.py index de0d51d4..eb6605f1 100644 --- a/famodel/project.py +++ b/famodel/project.py @@ -309,13 +309,15 @@ def loadDesign(self, d): # I think we want to just store it as a dictionary # validate it, # then have it available for use when making Mooring objects and subsystems - def getMoorings(lineconfig): + def getMoorings(lineconfig,i): ''' Parameters ---------- lineconfig : string Line configuration type + i : int + Index in array table (essentially which platform) Returns ------- @@ -406,8 +408,9 @@ def getMoorings(lineconfig): m_config['zAnchor'] = -self.depth m_config['span'] = lineConfigs[lineconfig]['span'] m_config['name'] = lineconfig - # m_config['zFair'] = lineConfigs[lineconfig]['fairlead_depth'] - # m_config['rFair'] = lineConfigs[lineconfig]['fairlead_radius'] + # add fairlead radius and depth to dictionary + m_config['rad_fair'] = self.platformList[arrayInfo[i]['ID']].rFair + m_config['z_fair'] = self.platformList[arrayInfo[i]['ID']].zFair m_config['connectors'] = c_config # add connectors section to the mooring dict @@ -513,11 +516,13 @@ def getAnchors(lineAnch, mc=None,aNum=0): # lineconfig = mSystems[m_s]['data'][j][0] # create mooring and connector dictionary - m_config = getMoorings(lineconfig) + m_config = getMoorings(lineconfig,i) + # create mooring class instance as part of mooring list in the project class instance - mc = (Mooring(dd=m_config, rA=[m_config['span'],0,m_config['zAnchor']], rB=[self.platformList[arrayInfo[i]['ID']].rFair,0,self.platformList[arrayInfo[i]['ID']].zFair], - rad_anch=m_config['span'], z_anch=m_config['zAnchor'],rad_fair=self.platformList[arrayInfo[i]['ID']].rFair,z_fair=self.platformList[arrayInfo[i]['ID']].zFair,id=(arrayInfo[i]['ID'],mct))) + mc = (Mooring(dd=m_config, id=(arrayInfo[i]['ID'],mct))) + mc.rA = [m_config['span']+m_config['rad_fair'],0,m_config['zAnchor']] + mc.rB = [m_config['rad_fair'],0,m_config['z_fair']] # adjust end positions based on platform location and mooring and platform headings mc.reposition(r_center=self.platformList[arrayInfo[i]['ID']].r, heading=headings[j]+self.platformList[arrayInfo[i]['ID']].phi, project=self) # adjust anchor z location and rA based on location of anchor @@ -563,10 +568,6 @@ def getAnchors(lineAnch, mc=None,aNum=0): if arrayMooring: # get mooring line info for all lines for j in range(0, len(arrayMooring)): # run through each line - # get configuration for that line - lineconfig = arrayMooring[j]['MooringConfigID'] - # create mooring and connector dictionary for that line - m_config = getMoorings(lineconfig) PFNum = [] # platform ID(s) connected to the mooring line @@ -588,6 +589,7 @@ def getAnchors(lineAnch, mc=None,aNum=0): for k in range(0, len(arrayInfo)): if arrayInfo[k]['ID'] == PFNum[0]: rowB = arrayInfo[k] + Bnum = k elif arrayInfo[k]['ID'] == PFNum[1]: rowA = arrayInfo[k] # get headings (mooring heading combined with platform heading) @@ -596,9 +598,15 @@ def getAnchors(lineAnch, mc=None,aNum=0): # calculate fairlead locations (can't use reposition method because both ends need separate repositioning) Aloc = [rowA['x_location']+np.cos(headingA)*self.platformList[PFNum[1]].rFair, rowA['y_location']+np.sin(headingA)*self.platformList[PFNum[1]].rFair, self.platformList[PFNum[1]].zFair] Bloc = [rowB['x_location']+np.cos(headingB)*self.platformList[PFNum[0]].rFair, rowB['y_location']+np.sin(headingB)*self.platformList[PFNum[0]].rFair, self.platformList[PFNum[0]].zFair] + # get configuration for the line + lineconfig = arrayMooring[j]['MooringConfigID'] + # create mooring and connector dictionary for that line + m_config = getMoorings(lineconfig,Bnum) # create mooring class instance - mc = (Mooring(dd=m_config, rA=Aloc, rB=Bloc, rad_anch=m_config['span'], z_anch=m_config['zAnchor'],id=(PFNum[0],PFNum[1],mct))) + mc = (Mooring(dd=m_config, id=(PFNum[0],PFNum[1],mct))) mc.shared = 1 + mc.rA = Aloc + mc.rB = Bloc # add mooring object to project mooring list self.mooringList[(PFNum[0],PFNum[1],mct)] = mc # attach mooring object to platforms @@ -613,11 +621,17 @@ def getAnchors(lineAnch, mc=None,aNum=0): elif any(ids['ID'] == arrayMooring[j]['end A'] for ids in arrayAnchor): # end A is an anchor # get ID of platform connected to line PFNum.append(arrayMooring[j]['end B']) + for k in range(0,len(arrayInfo)): + if arrayInfo[k]['ID'] == PFNum[0]: + Bnum = k + # get configuration for that line + lineconfig = arrayMooring[j]['MooringConfigID'] + # create mooring and connector dictionary for that line + m_config = getMoorings(lineconfig,Bnum) # create mooring class instance - mc = (Mooring(dd=m_config, rA=[m_config['span'],0,m_config['zAnchor']], - rB=[self.platformList[PFNum[0]].rFair,0,self.platformList[PFNum[0]].zFair], - rad_anch=m_config['span'], rad_fair=self.platformList[PFNum[0]].rFair, - z_anch=m_config['zAnchor'], z_fair=self.platformList[PFNum[0]].zFair,id=(PFNum[0],mct))) + mc = (Mooring(dd=m_config, id=(PFNum[0],mct))) + mc.rA = [m_config['span']+self.platformList[PFNum[0]].rFair,0,m_config['zAnchor']] + mc.rB = [self.platformList[PFNum[0]].rFair,0,self.platformList[PFNum[0]].zFair] # adjust end positions based on platform location and mooring and platform heading mc.reposition(r_center=self.platformList[PFNum[0]].r, heading=np.radians(arrayMooring[j]['headingB'])+self.platformList[PFNum[0]].phi, project=self) From 0829b6d46505acd22d95f17cca6b39e63ec28d7c Mon Sep 17 00:00:00 2001 From: Sirkis Date: Thu, 6 Jun 2024 11:39:58 -0600 Subject: [PATCH 13/24] Small fix to mooring line inputs --- famodel/mooring/mooring.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/famodel/mooring/mooring.py b/famodel/mooring/mooring.py index 11403737..b56eeb19 100644 --- a/famodel/mooring/mooring.py +++ b/famodel/mooring/mooring.py @@ -15,8 +15,7 @@ class Mooring(Edge): Work in progress. Eventually will inherit from Edge. ''' - def __init__(self, dd=None, subsystem=None, anchor=None, rA=[0,0,0], rB=[0,0,0], - rad_anch=500, rad_fair=58, z_anch=-100, z_fair=-14, + def __init__(self, dd=None, subsystem=None, anchor=None, rho=1025, g=9.81,id=None): ''' Parameters @@ -104,8 +103,8 @@ def __init__(self, dd=None, subsystem=None, anchor=None, rA=[0,0,0], rB=[0,0,0], self.ss = subsystem # end point absolute coordinates, to be set later - self.rA = rA - self.rB = rB + self.rA = None + self.rB = None self.heading = 0 # relative positions (variables could be renamed) From a0828ecf9b52503f7964045eda48463d10520b29 Mon Sep 17 00:00:00 2001 From: Moreno Date: Thu, 6 Jun 2024 12:16:26 -0600 Subject: [PATCH 14/24] New code for drilled and grout piles in (weak) rock following the local approach which replaces the soil behaviour by a system of springs and allows the estimation of the deflections --- famodel/anchors/capacity_dandg.py | 369 ++++++++++++++++++++++++++++++ 1 file changed, 369 insertions(+) create mode 100644 famodel/anchors/capacity_dandg.py diff --git a/famodel/anchors/capacity_dandg.py b/famodel/anchors/capacity_dandg.py new file mode 100644 index 00000000..8ed2e99c --- /dev/null +++ b/famodel/anchors/capacity_dandg.py @@ -0,0 +1,369 @@ +# Copyright Asitha Senanayake 2020 + +import numpy as np +from matplotlib.pyplot import plot, draw, show +import matplotlib.pyplot as plt +from scipy.interpolate import interp1d +import inspect + +################################### +#### Pile Geometry and Loading #### +################################### + +def py_analysis(profile, L, D, t, E, + V, H, H_n, M, M_n, n=10, iterations=10, + print_output='Yes', convergence_tracker='Yes', loc=2): + ''' + Models a laterally loaded pile using the p-y method. The solution for + lateral displacements is obtained by solving the 4th order ODE, + EI*d4y/dz4 - F*d2y/dz2 + ky = 0 using the finite difference method. + + Assumes that EI remains constant with respect to curvature i.e. pile + material remains in the elastic region. + + Input: + ----- + Su_profile - A 2D array of depths (m) and corresponding undrained shear strength(Pa) + Eg: array([[z1,Su1],[z2,Su2],[z3,Su3]...]) + Use small values for Su (eg: 0.001) instead of zeros to avoid divisions by zero but always start z at 0.0 + Example of a valid data point at the mudline is [0.0, 0.001] + + L - Length of pile (m) + D - Outer diameter of pile (m) + t - Wall thickness of pile (m) + E - Elastic modulus of pile material (Pa) + F - Axial force at pile head (N), vertically downwards is postive. + V - Force at pile head (N), shear causing clockwise rotation of pile is positive. + M - Moment at pile head (N*m), moments causing tension on left side of pile is positive. + n - Number of elements (50 by default) + iterations - Number of iterations to repeat calculation in order obtain convergence of 'y' + (A better approach is to iterate until a predefined tolerance is achieved but this requires additional + coding so, I will implement this later.) + py_model - Select which p-y model to use, 'Matlock' or 'Jeanjean_2009', 'MM-1', or 'Jeajean_etal_2017'. + + Optional: + convergence_tracker - Track how k_secant converges to actual p-y curve at a selected node + loc - Node number at which k_secant to be tracked (2 to n+1) + + Output: + ------ + y - Lateral displacement at each node, length = n + 5, (n+1) real nodes and 4 imaginary nodes + z - Vector of node locations along pile + ''' + + # Extract optional keyword arguments + # ls = 'x' + + # Resistance factor + gamma_f = 1.3 + + # Convert L and D to floating point numbers to avoid rounding errors + L = float(L) + D = float(D) + + # Pile geometry + I = np.pi*(D**4 - (D - 2*t)**4)/64.0 + EI = E*I + h = L/n # Element size + N = (n + 1) + 4 # (n+1) Real + 4 Imaginary nodes + + # Array for displacements at nodes, including imaginary nodes. + y = np.ones(N)*(0.01*D) # An initial value of 0.01D was arbitrarily chosen + + # Initialize and assemble array/list of p-y curves at each real node + z = np.zeros(N) + py_funs = [] + k_secant = np.zeros(N) + + for i in [0,1]: # Top two imaginary nodes + z[i] = (i-2)*h + py_funs.append(0) + k_secant[i] = 0.0 + + for i in range(2,n+3): # Real nodes + z[i] = (i - 2)*h + # Extract rock profile data + f_UCS, f_Em = rock_profile(profile) + UCS, Em = f_UCS(z[i])/gamma_f, f_Em(z[i])/gamma_f + py_funs.append(py_Reese(z[i], D, UCS, Em)) + k_secant[i] = py_funs[i](y[i])/y[i] + + for i in [n+3,n+4]: # Bottom two imaginary nodes + z[i] = (i - 2)*h + py_funs.append(0) + k_secant[i] = 0.0 + + # Track k_secant and current displacements + if convergence_tracker == 'Yes': + y1 = np.linspace(-2.*D,2.*D,500) + plt.plot(y1,py_funs[loc](y1)) + plt.xlabel('y (m)'), plt.ylabel('p (N/m)') + plt.grid(True) + + for j in range(iterations): + # if j == 0: print 'FD Solver started!' + y = fd_solver(n, N, h, EI, V, H, H_n, M, M_n, k_secant) + + if convergence_tracker == 'Yes': + plt.plot(y[loc], k_secant[loc]*y[loc]) + + for i in range(2,n+3): + k_secant[i] = py_funs[i](y[i])/y[i] + + if print_output == 'Yes': + print(f'y_0 = {y[2]:.3f} m') + + return y[2:-2], z[2:-2] + +################# +#### Solvers #### +################# + +def fd_solver(n, N, h, EI, V, H, H_n, M, M_n, k_secant): + ''' + Solves the finite difference equations from 'py_analysis_1'. This function should be run iteratively for + non-linear p-y curves by updating 'k_secant' using 'y'. A single iteration is sufficient if the p-y curves + are linear. + + Input: + ----- + n - Number of elements + N - Total number of nodes + h - Element size + EI - Flexural rigidity of pile + V - Axial force at pile head + H - Shear at pile head/tip + M - Moment at pile head/tip + k_secant - Secant stiffness from p-y curves + + Output: + ------ + y_updated - Lateral displacement at each node + ''' + + from scipy import linalg + + # Initialize and assemble matrix + X = np.zeros((N,N)) + + # (n+1) finite difference equations for (n+1) real nodes + for i in range(0,n+1): + X[i,i] = 1.0 + X[i,i+1] = -4.0 + V*h**2/EI + X[i,i+2] = 6.0 - 2*V*h**2/EI + k_secant[i+2]*h**4/EI + X[i,i+3] = -4.0 + V*h**2/EI + X[i,i+4] = 1.0 + + # Curvature at pile head + X[n+1,1] = 1.0 + X[n+1,2] = -2.0 + X[n+1,3] = 1.0 + + # Shear at pile head + X[n+2,0] = -1.0 + X[n+2,1] = 2.0 - V*h**2/EI + X[n+2,2] = 0.0 + X[n+2,3] = -2.0 + V*h**2/EI + X[n+2,4] = 1.0 + + # Curvature at pile tip + X[n+3,-2] = 1.0 + X[n+3,-3] = -2.0 + X[n+3,-4] = 1.0 + + # Shear at pile tip + X[n+4,-1] = 1.0 + X[n+4,-2] = -2.0 + V*h**2/EI + X[n+4,-3] = 0.0 + X[n+4,-4] = 2.0 - V*h**2/EI + X[n+4,-5] = -1.0 + + # Initialize vector q + q = np.zeros(N) + + # Populate q with boundary conditions + q[-1] = 2*H_n*h**3 # Shear at pile tip + q[-2] = M_n*h**2 # Moment at pile tip + q[-3] = 2*H*h**3 # Shear at pile head + q[-4] = M*h**2 # Moment at pile head + + y = linalg.solve(EI*X,q) + + return y + +############################### +#### P-Y Curve Definitions #### +############################### + +def py_Reese(z, D, UCS, Em, z_0=0.0, RQD=69, print_curves='Yes'): + ''' + Returns an interp1d interpolation function which represents the Reese (1997) p-y curve at the depth of interest. + + Important: Make sure to import the interp1 function by running 'from scipy.interpolate import interp1d' in the main program. + + Input: + ----- + z - Depth relative to pile head (m) + D - Pile diameter (m) + UCS - Undrained shear strength (Pa) + Em - Effectve vertical stress (Pa) + z_0 - Load eccentricity above the mudline or depth to mudline relative to the pile head (m) + RQD - Strain at half the strength as defined by Matlock (1970). + Typically ranges from 0.005 (stiff clay) to 0.02 (soft clay). + + Output: + ------ + Returns an interp1d interpolation function which represents the p-y curve at the depth of interest. + 'p' (N/m) and 'y' (m). + ''' + + from scipy.interpolate import interp1d + global var_Reese + + Dref = 0.305; nhu = 0.3; E = 200e9 + I = np.pi*(D**4 - (D - 2*t)**4)/64.0 + EI = E*I + alpha = -0.00667*RQD + 1 + krm = 0.0005 + + if z < 3*D: + p_ur = alpha*UCS*D*(1 + 1.4*z/D) + #kir = (100 +400*z/(3*D)) + else: + p_ur = 5.2*alpha*UCS*D + #kir = 500 + + kir = (D/Dref)*2**(-2*nhu)*(EI/(Em*D**4))**0.284 + Kir = kir*Em + y_rm = krm*D + y_a = (p_ur/(2*y_rm**0.25*Kir))**1.333 + + N = 20 + y = np.concatenate((-np.logspace(1,-3,N),[0],np.logspace(-3,1,N))) + + p=[]; P=[]; + for i in range (len(y)): + if abs(y[i]) < y_a: + P = np.sign(y[i])*Kir*y[i] + elif abs(y[i]) > y_a: + P = min((p_ur/2)*(abs(y[i])/y_rm)**0.25,p_ur) + p.append(P) + + p = np.array(p).squeeze() + for j in range(len(y)): + if y[j] < 0: + p[j] = -1*p[j] + elif y[j] > 0: + p[j] = p[j] + + var_Reese = inspect.currentframe().f_locals + + f = interp1d(y,p) # Interpolation function for p-y curve + + if print_curves == 'Yes': + plt.plot(y,p), + plt.xlabel('y (m)'), + plt.ylabel('p (kN/m)'), + plt.title('PY Curves - Reese (1997)') + plt.grid(True) + plt.xlim([-0.03*D,0.03*D]) + plt.ylim([min(p),max(p)]) + + return f # This is f (linear interpolation of y-p) + +####################### +#### Rock Profile ##### +####################### + +def rock_profile(profile,plot_profile='No'): + ''' + Define the (weak) rock profile used by the p-y analyzer. Outputs 'interp1d' functions containing + UCS and Em profiles to be used by the p-y curve functions. + + Input: + ----- + profile - A 2D tuple in the following format: ([depth (m), UCS (MPa), Em (MPa), py-model]) + The soil profile should be defined relative to the pile/tower head (i.e. point of lateral load application) + so that any load eccentricities can be taken into account. An example soil profile is shown below. + Eg: array([[z0,UCS0,Em0, 'Matlock', 0.02], + [z1,UCS1,Em1, 'Matlock', 0.01], + [z2,UCS2,Em2, 'Jeanjean', 550], + ...]) + *The current program cannot define layers with different p-y models. But it will added in the future. + + plot_profile - Plot Su vs depth profile. Choose 'Yes' to plot. + + Output: + ------ + z0 - Depth of mudline relative to the pile head (m) + f_UCS - 'interp1d' function containing undrained shear strength profile (Pa) + f_Em - 'interp1d' function containing effective vertical stress profile (Pa) + ''' + + from scipy.interpolate import interp1d + global var_rock_profile + + # Depth of mudline relative to pile head + z0 = profile[0,0].astype(float) + + # Extract data from soil_profile array and zero strength virtual soil layer + # from the pile head down to the mudline + depth = np.concatenate([np.array([z0]),profile[:,0].astype(float)]) # m + UCS = np.concatenate([np.array([0]),profile[:,1].astype(float)]) # MPa + Em = np.concatenate([np.array([0]),profile[:,2].astype(float)]) # MPa + + if plot_profile == 'Yes': + # Plot UCS vs z profile for confirmation + #fig2, ax2 = plt.subplots(1,1) + plt.plot(UCS,depth,'-',label=r'$S_u$',color='blue') + plt.legend(loc='lower left') + plt.xlabel('Uncompressed confined strength (MPa)'), + plt.ylabel('Depth below the pile head (m)'), plt.grid(True) + # Plot mudline/ground surface + plt.plot([-0.5*max(UCS),max(UCS)],[z0,z0],'--',color='red') + plt.text(-0.5*max(UCS),0.95*z0,'Mudline',color='red') + ax = plt.gca(); ax.invert_yaxis(), + ax.xaxis.tick_top(), ax.xaxis.set_label_position('top') + + # Define interpolation functions + f_UCS = interp1d(depth, UCS*1e6, kind='linear') # Pa + f_Em = interp1d(depth, Em*1e6, kind='linear') # Pa + + var_rock_profile = inspect.currentframe().f_locals + + return f_UCS, f_Em + +if __name__ == '__main__': + + # depth UCS Em p-y model + profile = np.array([[0.0, 3.5, 190., 'Name of p-y model'], + [3.0, 3.5, 280., 'Name of p-y model'], + [8.0, 3.5, 400., 'Name of p-y model'], + [15.0, 3.5, 450., 'Name of p-y model']]) + + f_UCS, f_Em = rock_profile(profile,plot_profile='No') + + #Pile dimensions + L = 15.0 # Pile length (m) + D = 2.8 # Pile diameter (m) + t = 0.075 # Pile wall thickness (m) + E = 200e9 # Elastic modulus of steel (Pa) + + #Pile head loads + H = 54e6 # Horizontal load on pile head (N) + V = 10e6 # Vertical load on pile head (N) + M = 0 # Moment on pile head (N*m) + H_n = 0 # Horizontal load on pile tip (N) + M_n = 0 # Moment on pile tip (N*m) + + y,z = py_analysis(profile, L=L, D=D, t=t, E=E, + V=V, H=H, H_n=H_n, M=M, M_n=M_n) + + #Plot deflection profile of pile + fig, ax = plt.subplots(figsize=(3,5)) + ax.plot(y,z,'r') + ax.set_xlabel('Displacement [m]') + ax.set_ylabel('Depth below pile head [m]') + ax.set_ylim([L + 2,-2]) + ax.set_xlim([-0.03*D,0.03*D]) + ax.grid(ls='--') \ No newline at end of file From d122ed821826356126140355edadccfb992036b1 Mon Sep 17 00:00:00 2001 From: EmmaSlackGraph Date: Thu, 6 Jun 2024 14:28:33 -0600 Subject: [PATCH 15/24] Convert to failureGraph class and comment functions --- failureGraphs.py | 805 ++++++++++++++++++++++++++++++----------------- 1 file changed, 520 insertions(+), 285 deletions(-) diff --git a/failureGraphs.py b/failureGraphs.py index 4e3b5b0c..3c14515d 100644 --- a/failureGraphs.py +++ b/failureGraphs.py @@ -5,289 +5,524 @@ from failure.failureProbabilities import * from failure.twoTurbineCaseStudy import * from famodel.project import Project - -def couldClash(G, failure, a1, a2, reverse): - if a1 == a2 or (failure + "\n" + str(a1.id) + str(a2.id) in list(G.nodes) or failure + "\n" + str(a2.id) + str(a1.id) in list(G.nodes)): - return False - a1_pnt1 = np.array(a1.rA[:2]) - a1_pnt2 = np.array(a1.rB[:2]) - a2_pnt1 = np.array(a2.rA[:2]) - a2_pnt2 = np.array(a2.rB[:2]) - # print('\n', a1_pnt1, a1_pnt2) - # print(a2_pnt1, a2_pnt2) - a1MaxX, a1MinX, a1MaxY, a1MinY = get_min_max_vals(a1_pnt1, a1_pnt2, angle_radians, reverse) - a2MaxX, a2MinX, a2MaxY, a2MinY = get_min_max_vals(a2_pnt1, a2_pnt2, angle_radians, False) - - overlap = False - for corner_x in [a1MaxX,a1MinX,]: - for corner_y in [a1MaxY, a1MinY]: - if (corner_x <= a2MaxX and corner_x >= a2MinX) and (corner_y <= a2MaxY and corner_y >= a2MinY): - overlap = True - # print("TRUE!!!!! ------------->") - # print(corner_x < a2MaxX, corner_x > a2MinX) - # print([corner_x, corner_y], a2MaxX, a2MinX, a2MaxY, a2MinY) - # print(a1MaxX, a1MinX, a1MaxY, a1MinY, '\n') - return overlap - -def addMoreEdges(nearby_platforms, G, Array, node, node_id, failures_c, failures_p, ids): - for child in failures_c[node]: - for id in ids: - if child + "\n" + str(id) in G.nodes: - G.add_edge(node + "\n" + str(node_id), child + "\n" + str(id), weight=0.001) - for parent in failures_p[node]: - for id in ids: - if parent + "\n" + str(id) in G.nodes: - G.add_edge(parent + "\n" + str(id), node + "\n" + str(node_id), weight=0.001) - return G - -def edgeReweight(G, edge): - new_weight = input("Enter weight for (" + str(edge[0].replace("\n", " ")) + ", " + str(edge[1].replace("\n", " ")) + ") (press \'Enter\' for default value)") - if new_weight=='': - new_weight=0.01 - G[edge[0]][edge[1]]['weight']=float(new_weight) - return G - -def get_y_Value(point1, point2, x): - return ((point2[1]-point1[1])/(point2[0]-point1[0]))*(x-point1[0])+point1[1] - -def get_min_max_vals(pnt2, pnt1, angle_radians, reverse): - if reverse: - pnt_hold = pnt1 - pnt1 = pnt2 - pnt2 = pnt_hold - vector = pnt2 - pnt1 - # print('vect', vector) - length = math.sqrt(vector[0]**2 + vector[1]**2) - if vector[0] == 0.0 and vector[1] > 0: angle = math.pi/2 - elif vector[0] == 0.0 and vector[1] < 0: angle = 3*math.pi/2 - elif vector[0] == 0.0 and vector[1] == 0: angle = 0.0 - else: angle = math.atan(vector[1]/vector[0]) - if angle == 0 and vector[0] < 0: - angle = math.pi - if (angle > -math.pi*0.5 and angle < math.pi*0.5) and vector[0] < 0: - angle += math.pi - # print('angle', angle) - new_vector1 = np.array([length * math.cos(angle - angle_radians), length * math.sin(angle - angle_radians)]) + np.array(pnt1) - new_vector2 = np.array([length * math.cos(angle + angle_radians), length * math.sin(angle + angle_radians)]) + np.array(pnt1) - if angle == math.pi and angle_radians==0: - new_vector1 = np.array([length * math.cos(angle - angle_radians), 0]) + np.array(pnt1) - new_vector2 = np.array([length * math.cos(angle + angle_radians), 0]) + np.array(pnt1) - max_x = max([new_vector1[0], new_vector2[0], pnt1[0], pnt2[0]]) - min_x = min([new_vector1[0], new_vector2[0], pnt1[0], pnt2[0]]) - max_y = max([new_vector1[1], new_vector2[1], pnt1[1], pnt2[1]]) - min_y = min([new_vector1[1], new_vector2[1], pnt1[1], pnt2[1]]) - if max_x == min_x: - if max_x < 0: max_x = 0 - elif min_x > 0: min_x = 0 - return max_x, min_x, max_y, min_y - -# Create project class instance from yaml file -Array = Project(file='famodel/OntologySample600m.yaml') - -# Create adjacency matrix from failure matrix -df = pd.read_excel("failure/failureData.xlsx", sheet_name="bMMatch") -arr = df.to_numpy()[:,1:] -nodeNames = df.to_numpy()[:, 0].flatten() - -# Get initial failure probabilities for each failure mode and effect -probabilities, array_of_probs = getProbabilities("failure/failureProbabilities.xlsx", "Sheet3") - - -# Determine angle of clashing we are interested in -# angle_degree = float(input("What angle do you want to use? (in degrees) ")) -# angle_radians = angle_degree/360 * math.pi * 2 -angle_radians = 30/360 * math.pi * 2 - -# Initialize and create the dictionaries of the children, parents, and probabilities for each failure -failures_c = {} -failures_p = {} -probability_dict = {} - -for i in range(arr.shape[0]): - node_children = [] - node_parents = [] - for j in range(arr.shape[1]): - if arr[i,j] > 0: - node_children.append(nodeNames[j]) - if arr[j,i] > 0: - node_parents.append(nodeNames[j]) - failures_c.update({nodeNames[i]: node_children}) - failures_p.update({nodeNames[i]: node_parents}) - probability_dict.update({nodeNames[i]: probabilities[i]}) - -# Create dictionary for each subsystem of failures -turbine = [0,1,2,3,4,5,26,27,28,29] -platform = [6,7,8,9,10,11,30,31,32] -mooringmooring = [12] -mooringcable = [13,14] -rope = [35] -polyester = [34] -chain = [33] -mooring = [15,16,17] -connector = [36] -clump_weight = [37] -anchor = [19,20,38] -cable = [21,22,23,40, 41,42, 45] -dynamic = [43] -static = [44] -grid = [24,25] -joints = [46] -buoyancy = [40] -sharedmooring = [15,16,18] -sharedanchor = [19,20,39] -systems = {'turbine':nodeNames[turbine], 'platform':nodeNames[platform], 'mooringmooring':nodeNames[mooringmooring], - 'rope':nodeNames[rope], 'chain':nodeNames[chain],'polyester':nodeNames[polyester],'mooringcable':nodeNames[mooringcable], - 'cablemooring':nodeNames[mooringcable], 'mooring':nodeNames[mooring], 'connector':nodeNames[connector], - 'weight':nodeNames[clump_weight], 'anchor':nodeNames[anchor], 'cable':nodeNames[cable], 'grid':nodeNames[grid], - 'dynamic':nodeNames[dynamic], 'static':nodeNames[static], 'buoyancy':nodeNames[buoyancy], 'cablecable': [], - 'sharedmooring':nodeNames[sharedmooring], 'sharedmooringcable':nodeNames[mooringcable], 'joints': nodeNames[joints], - 'cablesharedmooring':nodeNames[mooringcable], 'sharedmooringmooring':nodeNames[mooringmooring], - 'mooringsharedmooring':nodeNames[mooringmooring], 'sharedanchor': nodeNames[sharedanchor]} - - -# Initialize graph, boolean for plotting, and list of probabilities -G = nx.DiGraph() -plot = False - -# FIRST DEGREE NODES ------------------------------------------------------------------------------------------- -for platform in Array.platformList: - # print(platform, Array.platformList[platform].r) - attachments = Array.platformList[platform].attachments - nearby_platforms = [] - mooring_clashes = [] - cable_clashes = [] - num_cables = [] - - # Create platform failure nodes - for platform_failure in systems['platform']: - G.add_node(platform_failure + "\n" + str(platform)) - G = addMoreEdges(nearby_platforms, G, Array, platform_failure, platform, failures_c, failures_p, [platform]) - - # Create failure nodes - for turbine_failure in systems['turbine']: - G.add_node(turbine_failure + "\n" + str(platform)) - G = addMoreEdges(nearby_platforms, G, Array, turbine_failure, platform, failures_c, failures_p, [platform]) - - # FIRST DEGREE EDGES ------------------------------------------------------------------------------------------- - for attach1 in attachments.keys(): - attach1_name = str(attachments[attach1]['id']) - attach1_type = '' - if 'mooring' in str(type(attachments[attach1]['obj'])): - if attachments[attach1]['obj'].shared: attach1_type = 'sharedmooring' - else: attach1_type = 'mooring' - elif 'cable' in str(type(attachments[attach1]['obj'])): - attach1_type = 'cable' - num_cables.append(attachments[attach1]['obj'].id) - - # Create moroing/cable failure nodes - for attach1_failure in systems[attach1_type]: - original_name = attach1_name - if 'connect' in attach1_failure: attach1_name = platform + attach1_name - G.add_node(attach1_failure + "\n" + attach1_name) - G = addMoreEdges(nearby_platforms, G, Array, attach1_failure, attach1_name, failures_c, failures_p, [platform, attach1_name]) - attach1_name = original_name - - # Create clashing failure nodes - for attach2 in attachments.keys(): - attach2_name = str(attachments[attach2]['id']) - attach2_type = '' - clash_name = str(attach1_name)+str(attach2_name) - if 'mooring' in str(type(attachments[attach2]['obj'])): attach2_type = 'mooring' - elif 'cable' in str(type(attachments[attach2]['obj'])): attach2_type = 'cable' - for clash_failure in systems[(str(attach1_type)+str(attach2_type))]: - if 'shared' in attach1_type and all(abs(np.array(attachments[attach1]['obj'].rB[:2]) - np.array(attachments[attach2]['obj'].rB[:2])) < 100): reverse = True - else: reverse = False - # print('could clash --', couldClash(G, clash_failure, attachments[attach1]['obj'], attachments[attach2]['obj']), '--', attach1_name, attach2_name) - if couldClash(G, clash_failure, attachments[attach1]['obj'], attachments[attach2]['obj'], reverse): - G.add_node(clash_failure + "\n" + clash_name) - G = addMoreEdges([attach1_name, attach2_name], G, Array, clash_failure, clash_name, failures_c, failures_p, [platform, attach1_name, attach2_name, clash_name]) - - if attach1_type == 'mooring' and attach2_type == attach1_type: mooring_clashes.append(clash_failure + "\n" + clash_name) - elif ('shared' not in attach1_type) and ('shared' not in attach2_type): cable_clashes.append(clash_failure + "\n" + clash_name) - - # SUBNODES AND SUBEDGES ------------------------------------------------------------------------------------ - subcomponents = attachments[attach1]['obj'].subcomponents - component_num = 0 - for component in subcomponents: - component_num += 1 - if 'mooring' in attach1_type: - if 'type' in component.keys(): - # Create clump weight failure nodes - if 'str' in str(type(component['type'])) and 'weight' in component['type']: - component_type = 'weight' - component_name = str(attach1_name + ' ' + component['type'] + ' ' + str(component_num)) - - # Create mooring material failure nodes - if 'dict' in str(type(component['type'])): - if 'polyester' in component['type']['material']: component_type = 'polyester' - elif 'chain' in component['type']['material']: component_type = 'chain' - elif 'rope' in component['type']['material']: component_type = 'rope' - component_name = attach1_name + ' ' + str(component['type']['name']) - - # Create connector failure nodes - else: - component_type = 'connector' - component_name = attach1_name + ' connector' - - elif 'cable' in attach1_type: - # Create dynamic cable section failure nodes - if 'dynamic' in str(type(component)): - component_type = 'dynamic' - component_name = str(component.id) + + +class failureGraph(): + def __init__(self, project_file): + # Create project class instance from yaml file + self.Array = Project(file=project_file) + self.G = nx.DiGraph() + + + def create_failureGraph(self, matrix_file, matrix_sheet, probabilities_file, probability_sheet): + '''Create a graph of failures based on the FAModel Project object + Parameters + ---------- + matrix_file : string + Failure matrix file that encodes interaction between failure modes and effects + matrix_sheet : string + Name of sheet in Excel file to pull the failure matrix from + probabilities_file : string + File name for list of failure rates (iniital probabilities) of all failures + probability_sheet : string + Name of sheet in Excel file to pull the failure probabilities from + ''' + + print("\nBegin generating failure graph...") + # Create adjacency matrix from failure matrix + df = pd.read_excel(matrix_file, sheet_name=matrix_sheet) + arr = df.to_numpy()[:,1:] + nodeNames = df.to_numpy()[:, 0].flatten() + + # Get initial failure probabilities for each failure mode and effect + probabilities, array_of_probs = getProbabilities(probabilities_file, probability_sheet) + init_prob_dict = {} + for prob_index in range(len(probabilities)): + init_prob_dict.update({nodeNames[prob_index]: probabilities[prob_index]}) + + # Determine angle of clashing we are interested in + angle_degree = float(input("What angle do you want to use? (in degrees) ")) + self.angle_radians = angle_degree/360 * math.pi * 2 + + # Initialize and create the dictionaries of the children and parents + self.failures_c = {} + self.failures_p = {} + for i in range(arr.shape[0]): + node_children = [] + node_parents = [] + for j in range(arr.shape[1]): + if arr[i,j] > 0: + node_children.append(nodeNames[j]) + if arr[j,i] > 0: + node_parents.append(nodeNames[j]) + self.failures_c.update({nodeNames[i]: node_children}) + self.failures_p.update({nodeNames[i]: node_parents}) + + systems = self.get_systems(nodeNames) + + # Initialize graph, boolean for plotting, and list of probabilities + self.G = nx.DiGraph() + + # FIRST DEGREE NODES ------------------------------------------------------------------------------------------- + for platform in self.Array.platformList: + attachments = self.Array.platformList[platform].attachments + failure_probabilities = self.Array.platformList[platform].failure_probability + platform_obj = self.Array.platformList[platform] + nearby_platforms = [] + mooring_clashes = [] + cable_clashes = [] + num_cables = [] + + # Create platform failure nodes + for platform_failure in systems['platform']: + if platform_failure in failure_probabilities.keys(): fail_prob = failure_probabilities[platform_failure] + else: fail_prob = init_prob_dict[platform_failure] + self.G.add_node(platform_failure + "\n" + str(platform), probability=fail_prob, obj = [platform_obj]) + self.G = self.addMoreEdges(platform_failure, platform, [platform]) + + # Create failure nodes + for turbine_failure in systems['turbine']: + if turbine_failure in failure_probabilities.keys(): fail_prob = failure_probabilities[turbine_failure] + else: fail_prob = init_prob_dict[turbine_failure] + self.G.add_node(turbine_failure + "\n" + str(platform), probability=fail_prob, obj = [platform_obj]) + self.G = self.addMoreEdges(turbine_failure, platform, [platform]) + + # FIRST DEGREE EDGES ------------------------------------------------------------------------------------------- + for attach1 in attachments.keys(): + attach1_name = str(attachments[attach1]['id']) + attach1_type = '' + if 'mooring' in str(type(attachments[attach1]['obj'])): + if attachments[attach1]['obj'].shared: attach1_type = 'sharedmooring' + else: attach1_type = 'mooring' + elif 'cable' in str(type(attachments[attach1]['obj'])): + attach1_type = 'cable' + num_cables.append(attachments[attach1]['obj']) + + # Create moroing/cable failure nodes + for attach1_failure in systems[attach1_type]: + original_name = attach1_name + if 'connect' in attach1_failure: attach1_name = platform + attach1_name + if attach1_failure in attachments[attach1]['obj'].failure_probability.keys(): fail_prob = failure_probabilities[attach1_failure] + else: fail_prob = init_prob_dict[attach1_failure] + self.G.add_node(attach1_failure + "\n" + attach1_name, probability=fail_prob, obj = [attachments[attach1]['obj']]) + self.G = self.addMoreEdges(attach1_failure, attach1_name, [platform, attach1_name]) + attach1_name = original_name + + # Create clashing failure nodes + for attach2 in attachments.keys(): + attach2_name = str(attachments[attach2]['id']) + attach2_type = '' + clash_name = str(attach1_name)+str(attach2_name) + if 'mooring' in str(type(attachments[attach2]['obj'])): attach2_type = 'mooring' + elif 'cable' in str(type(attachments[attach2]['obj'])): attach2_type = 'cable' + for clash_failure in systems[(str(attach1_type)+str(attach2_type))]: + if 'shared' in attach1_type and all(abs(np.array(attachments[attach1]['obj'].rB[:2]) - np.array(attachments[attach2]['obj'].rB[:2])) < 100): reverse = True + else: reverse = False + if self.couldClash(clash_failure, attachments[attach1]['obj'], attachments[attach2]['obj'], reverse): + if clash_failure in attachments[attach1]['obj'].failure_probability.keys(): fail_prob = failure_probabilities[clash_failure] + else: fail_prob = init_prob_dict[clash_failure] + self.G.add_node(clash_failure + "\n" + clash_name, probability=fail_prob, obj = [attachments[attach1]['obj'], attachments[attach2]['obj']]) + self.G = self.addMoreEdges(clash_failure, clash_name, [platform, attach1_name, attach2_name, clash_name]) + if attach1_type == 'mooring' and attach2_type == attach1_type: mooring_clashes.append(clash_failure + "\n" + clash_name) + elif ('shared' not in attach1_type) and ('shared' not in attach2_type): cable_clashes.append(clash_failure + "\n" + clash_name) + + # SUBNODES AND SUBEDGES ------------------------------------------------------------------------------------ + subcomponents = attachments[attach1]['obj'].subcomponents + component_num = 0 + for component in subcomponents: + component_num += 1 + if 'mooring' in attach1_type: + if 'type' in component.keys(): + # Create clump weight failure nodes + if 'str' in str(type(component['type'])) and 'weight' in component['type']: + component_type = 'weight' + component_name = str(attach1_name + ' ' + component['type'] + ' ' + str(component_num)) + + # Create mooring material failure nodes + if 'dict' in str(type(component['type'])): + if 'polyester' in component['type']['material']: component_type = 'polyester' + elif 'chain' in component['type']['material']: component_type = 'chain' + elif 'rope' in component['type']['material']: component_type = 'rope' + component_name = attach1_name + ' ' + str(component['type']['name']) + + # Create connector failure nodes + else: + component_type = 'connector' + component_name = attach1_name + ' connector' + + elif 'cable' in attach1_type: + # Create dynamic cable section failure nodes + if 'dynamic' in str(type(component)): + component_type = 'dynamic' + component_name = str(component.id) + + # Create static cable section failure nodes + elif 'static' in str(type(component)): + component_type = 'static' + component_name = str(component.id) + + # Create offshore Joints failure nodes + elif 'joint' in str(type(component)).lower(): + component_type = 'joints' + component_name = attach1_name + ' ' + str(component.id) + + for component_failure in systems[component_type]: + if component_failure in component.failure_probability.keys(): fail_prob = failure_probabilities[component_failure] + else: fail_prob = init_prob_dict[component_failure] + self.G.add_node(component_failure + "\n" + component_name, probability=fail_prob, obj = [component]) + self.G = self.addMoreEdges(component_failure, component_name, [platform, attach1]) + + + # SECOND ORDER NODES ------------------------------------------------------------------------------------------- + attached_to = attachments[attach1]['obj'].attached_to + for attach1A in attached_to: + attach1A_name = str(attach1A.id) + + # Create anchor failure nodes + if 'anchor' in str(type(attach1A)): + attach1A_type = 'anchor' + if len(attach1A.attachments) > 1: attach1A_type = 'sharedanchor' + for anchor_failure in systems[attach1A_type]: + if anchor_failure in attach1A.failure_probability.keys(): fail_prob = failure_probabilities[anchor_failure] + else: fail_prob = init_prob_dict[anchor_failure] + self.G.add_node(anchor_failure + "\n" + attach1A_name, probability=fail_prob, obj = [attach1A]) + self.G = self.addMoreEdges(anchor_failure, attach1A_name, [platform, attach1_name, attach1A_name]) + + # Create edges between platforms + elif 'platform' in str(type(attach1A)): + attach1A_type = 'platform' + attach1A_name = attach1A.id + for platform_failure in systems['platform']: + self.G = self.addMoreEdges(platform_failure, platform, [platform, attach1A_name]) + + # Create substation/grid failure nodes + elif 'substation' in str(type(attach1A)): + attach1A_type = 'substation' + for grid_failure in systems['grid']: + if grid_failure in attach1A.failure_probability.keys(): fail_prob = failure_probabilities[grid_failure] + else: fail_prob = init_prob_dict[grid_failure] + self.G.add_node(grid_failure + "\n" + attach1A_name, probability=fail_prob, obj = [attach1A]) + self.G = self.addMoreEdges(grid_failure, attach1A_name, [platform, attach1_name, attach1A_name]) + + # Create mooring-mooring clashing failure node if no two mooring lines likely to clash + if len(mooring_clashes) < 1: + if systems['mooringmooring'][0] in failure_probabilities.keys(): fail_prob = failure_probabilities[systems['mooringmooring'][0]] + else: fail_prob = init_prob_dict[systems['mooringmooring'][0]] + self.G.add_node(systems['mooringmooring'][0] + "\n" + str(platform), probability=fail_prob, obj= [platform_obj]) + self.G = self.addMoreEdges(systems['mooringmooring'][0], str(platform), [platform]) + + # Create cable-mooring clashing failure nodes if no cable and mooring pairing likely to clash + if len(cable_clashes) < 1: + for cable_num_obj in num_cables: + cable_num = cable_num_obj.id + for clashing_failure in systems['cablemooring']: + if clashing_failure in failure_probabilities.keys(): fail_prob = failure_probabilities[clashing_failure] + else: fail_prob = init_prob_dict[clashing_failure] + self.G.add_node(clashing_failure + "\n" + str(platform) + ' ' + str(cable_num), probability=fail_prob, obj = [platform_obj, cable_num]) + self.G = self.addMoreEdges(clashing_failure, str(platform) + ' ' + str(cable_num), [platform]) + return self.G + + + + def get_systems(self, nodeNames): + '''Create dictionary for each subsystem of failures + Parameters + ---------- + nodeNames : list + List of all the failure names to use to create dictionary of subsystems + ''' + # Systems and indices of corresponding failures in nodeNames + turbine = [0,1,2,3,4,5,26,27,28,29] + platform = [6,7,8,9,10,11,30,31,32] + mooringmooring = [12] + mooringcable = [13,14] + rope = [35] + polyester = [34] + chain = [33] + mooring = [15,16,17] + connector = [36] + clump_weight = [37] + anchor = [19,20,38] + cable = [21,22,23,40, 41,42, 45] + dynamic = [43] + static = [44] + grid = [24,25] + joints = [46] + buoyancy = [40] + sharedmooring = [15,16,18] + sharedanchor = [19,20,39] + + # Dictionary of systems and their failures + systems = {'turbine':nodeNames[turbine], 'platform':nodeNames[platform], 'mooringmooring':nodeNames[mooringmooring], 'rope':nodeNames[rope], + 'chain':nodeNames[chain],'polyester':nodeNames[polyester],'mooringcable':nodeNames[mooringcable], 'cablemooring':nodeNames[mooringcable], + 'mooring':nodeNames[mooring], 'connector':nodeNames[connector], 'weight':nodeNames[clump_weight], 'anchor':nodeNames[anchor], 'cable':nodeNames[cable], + 'grid':nodeNames[grid], 'dynamic':nodeNames[dynamic], 'static':nodeNames[static], 'buoyancy':nodeNames[buoyancy], 'cablecable': [], + 'sharedmooring':nodeNames[sharedmooring], 'sharedmooringcable':nodeNames[mooringcable], 'joints': nodeNames[joints], 'cablesharedmooring':nodeNames[mooringcable], + 'sharedmooringmooring':nodeNames[mooringmooring], 'mooringsharedmooring':nodeNames[mooringmooring], 'sharedanchor': nodeNames[sharedanchor]} + return systems + + + def get_critical_node(self, param): + '''Identify and return the critical failure(s) of the failure graph + Parameters + ---------- + param : string + Measurement for criticality (either initial probability, degree [in, out, or total], susceptibility, or impact) + ''' + nodalNames = np.array(list(self.G.nodes)) + critical_prob = [0, []] + + # Find critical failure for critical indicating the maximum initial probability + if 'prob' in param: + nodal_probabilities = nx.get_node_attributes(self.G, "probability") + for failure_node in nodal_probabilities: + if nodal_probabilities[failure_node] > critical_prob[0]: + critical_prob[0] = nodal_probabilities[failure_node] + critical_prob[1] = [failure_node] + elif nodal_probabilities[failure_node] == critical_prob[0]: + critical_prob[1].append(failure_node) + + # Find the critical failure for crtitical refering to the maximum degree (either in-degree, out-degree, or total-degree) + elif 'deg' in param: + out_deg, in_deg, deg = max_degrees(nx.to_numpy_array(self.G), nodalNames, threshold = 0, name = True) + if 'in' in param: + critical_prob[0] = in_deg[1] + critical_prob[1] = list(in_deg[0]) + elif 'out' in param: + critical_prob[0] = out_deg[1] + critical_prob[1] = list(out_deg[0]) + else: + critical_prob[0] = deg[1] + critical_prob[1] = list(deg[0]) + + # Find the crtitical failure for ctitical refering to the susceptibility or impact of a node + elif 'sus' in param or 'impact' in param: + max_impact, max_sus = self.get_susceptibility_and_impact() + if 'impact' in param: critical_prob = max_impact + elif 'sus' in param: critical_prob = max_sus + else: return + return critical_prob + + + def get_susceptibility_and_impact(self): + '''Run Bayesian inference over the graph to find all conditional probabilities (probability of A given B for all failures A and B), then average to determine + the failure with the highest susceptibility and that with the highest impact + + Parameters + ---------- + None + ''' + print("\nStarting susceptibility and impact calculation... ") + # Create list of node names and adjacency matrix from the failure graph + nodeNamesArray = np.array(list(self.G.nodes)) + arr = nx.to_numpy_array(self.G) + + # If the user wants to input probabilities for specific edges, reweight edges based on the user's inputs + user_inputs = input("Would you like to input probabilities into adjacency matrix? ") + twoTurbine_calculationType = False + if (user_inputs == 'y' or user_inputs == 'yes') or user_inputs == 'True': + twoTurbine_calculationType = True + for i in range(len(self.G.edges)): + edge = list(self.G.edges)[i] + if ('rift off' in edge[0].replace("\n", " ") or 'ncreased' in edge[0].replace("\n", " ")) or 'ynamics' in edge[0].replace("\n", " "): self.G = self.edgeReweight(edge) + elif ('apsize' in edge[0].replace("\n", " ") or '-cable' in edge[0].replace("\n", " ")) or 'ing line non' in edge[0].replace("\n", " "): self.G = self.edgeReweight(edge) + elif ('ragging' in edge[0].replace("\n", " ") or 'hain' in edge[0].replace("\n", " ")) or 'ire rope' in edge[0].replace("\n", " "): self.G = self.edgeReweight(edge) + elif ('ynthetic' in edge[0].replace("\n", " ") or 'able profile' in edge[0].replace("\n", " ")) or 'ared line' in edge[0].replace("\n", " "): self.G = self.edgeReweight(edge) + elif ('load on cable' in edge[0].replace("\n", " ") or 'eight' in edge[0].replace("\n", " ")): self.G = self.edgeReweight(edge) + + # Ask user if they are ready to continue to Bayesian network calculations (if not, quit) + continue_input = input("Ready to continue? ") + if 'n' in continue_input.lower(): quit() + + # Bayesian network calculation + arr = nx.to_numpy_array(self.G) + nodeNames = np.reshape(np.array(list(self.G.nodes)), (len(list(self.G.nodes)), )) + all_probabilities = np.zeros(arr.shape) # Initialize a large array to put all the pairwise probabilities in + for start_component in range(1,arr.shape[0]+1): # Iterate through each failure mode/effect in turbine + a = arr.copy() + non = nodeNames + K, a, g, e, m, non = breadth_first_multi(a, nodeNames, [start_component], "child") # Generate tree for Bayesian network + prblts = [] # Initialize array of node probabilities (in order of appearance in graph) + for node in non: + prblts.append(self.G.nodes[node]['probability']) # Add nodes to array of node probabilities + prblts = np.array(prblts) + + probabilitiy_table = np.zeros((2, a.shape[0])) # Initialize table of inference probabilities + nodes = diagonal_nodes(a) # Diagonal matrix of node names (numerical +1) + a = make_binary(a, 0.5) # Binarize adjacency table + + nodeNamesArray = np.array(list(K.nodes)) # Initialize array of names of nodes + nodeNamesArray = np.reshape(nodeNamesArray, (len(nodeNamesArray),)) # Make numpy array + + # Interence----------------------------------------------------------------------- + for node in range(a.shape[0]): + pts_bool = nodes @ a[:, node] # vector of zeros and child names (numerical names) + pts = pts_bool[np.nonzero(pts_bool)] #list of just the child names (numerical names) + + if len(pts) < 1: # If no parents, add probability of failure happening to the probability table + probabilitiy_table[0][node] = self.G.nodes[list(self.G.nodes)[node]]['probability'] + probabilitiy_table[1][node] = 1 - self.G.nodes[list(self.G.nodes)[node]]['probability'] + continue - # Create static cable section failure nodes - elif 'static' in str(type(component)): - component_type = 'static' - component_name = str(component.id) - - # Create offshore Joints failure nodes - elif 'joint' in str(type(component)).lower(): - component_type = 'joints' - component_name = attach1_name + ' ' + str(component.id) - - for component_failure in systems[component_type]: - G.add_node(component_failure + "\n" + component_name) - G = addMoreEdges(nearby_platforms, G, Array, component_failure, component_name, failures_c, failures_p, [platform, attach1]) - - - # SECOND ORDER NODES ------------------------------------------------------------------------------------------- - attached_to = attachments[attach1]['obj'].attached_to - for attach1A in attached_to: - attach1A_name = str(attach1A.id) - - # Create anchor failure nodes - if 'anchor' in str(type(attach1A)): - attach1A_type = 'anchor' - if len(attach1A.attachments) > 1: attach1A_type = 'sharedanchor' - for anchor_failure in systems[attach1A_type]: - G.add_node(anchor_failure + "\n" + attach1A_name) - G = addMoreEdges(nearby_platforms, G, Array, anchor_failure, attach1A_name, failures_c, failures_p, [platform, attach1_name, attach1A_name]) - - # Create edges between platforms - elif 'platform' in str(type(attach1A)): - attach1A_type = 'platform' - attach1A_name = attach1A.id - for platform_failure in systems['platform']: - G = addMoreEdges(nearby_platforms, G, Array, platform_failure, platform, failures_c, failures_p, [platform, attach1A_name]) - - # Create substation/grid failure nodes - elif 'substation' in str(type(attach1A)): - attach1A_type = 'substation' - for grid_failure in systems['grid']: - G.add_node(grid_failure + "\n" + attach1A_name) - G = addMoreEdges(nearby_platforms, G, Array, anchor_failure, attach1A_name, failures_c, failures_p, [platform, attach1_name, attach1A_name]) - - if len(mooring_clashes) < 1: - G.add_node(systems['mooringmooring'][0] + "\n" + str(platform)) - G = addMoreEdges(nearby_platforms, G, Array, systems['mooringmooring'][0], str(platform), failures_c, failures_p, [platform]) - - if len(cable_clashes) < 1: - for cable_num in num_cables: - for clashing_failure in systems['cablemooring']: - G.add_node(clashing_failure + "\n" + str(platform) + ' ' + str(cable_num)) - G = addMoreEdges(nearby_platforms, G, Array, clashing_failure, str(platform) + ' ' + str(cable_num), failures_c, failures_p, [platform]) - - -print('\n') -for node in G.nodes: - print(node.replace('\n', ' ')) \ No newline at end of file + if twoTurbine_calculationType: + parents, our_table = twoTurbine_bayesian_table(a, arr, node + 1, nodeNames, non) # Calculate the probability distribution table + else: + parents, our_table = bayesian_table(a, node+1, True, nodeNames, True, prblts) + mlt_table = np.ones((our_table.shape[0],2)) # Initialize table for multiplying across rows of probability distribution table + + # Calculate Probability Table ------------------------------------------------------------ + for i in range(our_table.shape[0]): + for j in range(our_table.shape[1] - 2): + parent = int(parents[j]) + if our_table[i,j] == 0: + our_table[i,j] = probabilitiy_table[0][parent - 1] + if probabilitiy_table[0][parent - 1] == 0: + break + else: + our_table[i,j] = probabilitiy_table[1][parent - 1] + if (parent-1 == 0): # If the node's parent is the evidence, zero out the non-failing possibility + our_table[i,j] = 0 + mlt_table[i,0] *= our_table[i,j] # Multiply the probabilities across the probability distribution table + mlt_table[i,1] = mlt_table[i,0] * our_table[i, -1] # Multiple by the probability of event not failing given combination of parent failure + mlt_table[i,0] *= our_table[i, -2] # Multiple by the probability of event failing given combination of parent failure + sm_table = np.sum(mlt_table, axis = 0) #/np.sum(mlt_table) # Sum the products of probabilities across the columns + probabilitiy_table[0][node] = sm_table[0] # Update the inference probability table with the probabilites just calculated + probabilitiy_table[1][node] = sm_table[1] + + # Print and add probability of node to table + print(start_component, node, " --> Probability of ", nodeNamesArray[node].replace("\n", " "), "=", sm_table) + index2 = np.where(nodeNames == nodeNamesArray[node])[0][0] + all_probabilities[0 * arr.shape[0] + start_component - 1][0 * arr.shape[0] + index2] = sm_table[0]/np.sum(sm_table) + + # Calculate and return highest impact and susceptibility + mean_impact = np.mean(all_probabilities, axis=1) + max_impact = np.max(mean_impact) + max_impact_index = np.where(mean_impact == max_impact)[0][0] + + mean_susceptibility = np.mean(all_probabilities, axis=0) + max_susceptibility = np.max(mean_susceptibility, axis=0) + max_impact_susceptibility = np.where(mean_impact == max_impact)[0][0] + return [max_impact, nodeNamesArray[max_impact_index]], [max_susceptibility, nodeNamesArray[max_impact_susceptibility]] + + + + def couldClash(self, failure, a1, a2, reverse): + '''Determine if two lines (either mooring or cable) could clash with each other + Parameters + ---------- + failure : string + Name of failure (so that we can check if failure node already exists) + a1 : object + Either cable or mooring object for the first line + a2 : object + Either cable or mooring object for the second line + reverse : boolean + Determines orientation of the first line (True if orientation of vector needs to be reversed, False otherwise) + ''' + # If the failure node already exists (perhaps by a different name), return that the lines cannot clash + if a1 == a2 or (failure + "\n" + str(a1.id) + str(a2.id) in list(self.G.nodes) or failure + "\n" + str(a2.id) + str(a1.id) in list(self.G.nodes)): return False + + # Obtain the (x,y) coordinates of the start and end points of the lines + a1_pnt1 = np.array(a1.rA[:2]) + a1_pnt2 = np.array(a1.rB[:2]) + a2_pnt1 = np.array(a2.rA[:2]) + a2_pnt2 = np.array(a2.rB[:2]) + + # Determine the boundaries of movement of the lines (boundaries form a rectangle) + a1MaxX, a1MinX, a1MaxY, a1MinY = self.get_min_max_vals(a1_pnt1, a1_pnt2, self.angle_radians, reverse) + a2MaxX, a2MinX, a2MaxY, a2MinY = self.get_min_max_vals(a2_pnt1, a2_pnt2, self.angle_radians, False) + + # If the rectangles overlap, return TRUE (the lines can clash). Else, return FALSE (the lines cannot clash) + overlap = False + for corner_x in [a1MaxX,a1MinX,]: + for corner_y in [a1MaxY, a1MinY]: + if (corner_x <= a2MaxX and corner_x >= a2MinX) and (corner_y <= a2MaxY and corner_y >= a2MinY): overlap = True + return overlap + + + + def addMoreEdges(self, node, node_id, ids): + '''Adds edges between current node and any already created nodes + Parameters + ---------- + node : string + Name of current failure + node_id : string + Name of current component that we are creating the failure for + ids : list of strings + List of other component ids for components whose failure nodes have already been created + ''' + # Create edges from current node to nodes already created (if created node is child of current node in reference matrix) + for child in self.failures_c[node]: + for id in ids: + if child + "\n" + str(id) in self.G.nodes: self.G.add_edge(node + "\n" + str(node_id), child + "\n" + str(id), weight=0.001) + + # Create edges from nodes already created to current node (if created node is parent of current node in reference matrix) + for parent in self.failures_p[node]: + for id in ids: + if parent + "\n" + str(id) in self.G.nodes: self.G.add_edge(parent + "\n" + str(id), node + "\n" + str(node_id), weight=0.001) + return self.G + + + + def edgeReweight(self, edge): + '''Ask user for new weight and set edge weight to user's input + Parameters + ---------- + edge : list of node names + Edge that we want to reweight, comprised of the two nodes that it connects + ''' + new_weight = input("Enter weight for (" + str(edge[0].replace("\n", " ")) + ", " + str(edge[1].replace("\n", " ")) + ") (press \'Enter\' for default value)") + if new_weight=='': new_weight=0.01 + self.G[edge[0]][edge[1]]['weight']=float(new_weight) + return self.G + + + + def get_min_max_vals(self, pnt2, pnt1, angle_radians, reverse): + '''Determine boundary of movement for a line (either cable or mooring) + Parameters + ---------- + pnt2 : list of floats + Point (x,y) of beginning of line + pn1 : list of floats + Point (x,y) of end of line + angle_radians : float + Angle of movement (in radians) + reverse : boolean + Determines orientation of the first line (True if orientation of vector needs to be reversed, False otherwise) + ''' + # If the vector's orientation needs to be reversed, reverse the order of the points + if reverse: + pnt_hold = pnt1 + pnt1 = pnt2 + pnt2 = pnt_hold + + # Find the vector that represents the line and the vector's length + vector = pnt2 - pnt1 + length = math.sqrt(vector[0]**2 + vector[1]**2) + + # Find the angle (in polar coordinates) of the vector + if vector[0] == 0.0 and vector[1] > 0: angle = math.pi/2 + elif vector[0] == 0.0 and vector[1] < 0: angle = 3*math.pi/2 + elif vector[0] == 0.0 and vector[1] == 0: angle = 0.0 + else: angle = math.atan(vector[1]/vector[0]) + if angle == 0 and vector[0] < 0: angle = math.pi + if (angle > -math.pi*0.5 and angle < math.pi*0.5) and vector[0] < 0: angle += math.pi + + # Add and subtraact the angle of motion (while vector is in polar coordinates) to create two new vectors and then convert them back to rectangular coordinates + new_vector1 = np.array([length * math.cos(angle - angle_radians), length * math.sin(angle - angle_radians)]) + np.array(pnt1) + new_vector2 = np.array([length * math.cos(angle + angle_radians), length * math.sin(angle + angle_radians)]) + np.array(pnt1) + if angle == math.pi and angle_radians==0: # Handle the issue of sin(0) not equal to 0 in python + new_vector1 = np.array([length * math.cos(angle - angle_radians), 0]) + np.array(pnt1) + new_vector2 = np.array([length * math.cos(angle + angle_radians), 0]) + np.array(pnt1) + + # Determine the bounds of the smallest rectangle which contains the original vector and the two new vectors + max_x = max([new_vector1[0], new_vector2[0], pnt1[0], pnt2[0]]) + min_x = min([new_vector1[0], new_vector2[0], pnt1[0], pnt2[0]]) + max_y = max([new_vector1[1], new_vector2[1], pnt1[1], pnt2[1]]) + min_y = min([new_vector1[1], new_vector2[1], pnt1[1], pnt2[1]]) + if max_x == min_x: + if max_x < 0: max_x = 0 + elif min_x > 0: min_x = 0 + return max_x, min_x, max_y, min_y \ No newline at end of file From 83d9040c528378b72feedaf9257b369cadb750df Mon Sep 17 00:00:00 2001 From: EmmaSlackGraph Date: Mon, 10 Jun 2024 13:15:43 -0600 Subject: [PATCH 16/24] Add methods for updating the critical failures after original critical failures occur --- failureGraphs.py | 199 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 180 insertions(+), 19 deletions(-) diff --git a/failureGraphs.py b/failureGraphs.py index 3c14515d..691a599a 100644 --- a/failureGraphs.py +++ b/failureGraphs.py @@ -13,6 +13,11 @@ def __init__(self, project_file): self.Array = Project(file=project_file) self.G = nx.DiGraph() + self.critical_failures = None + self.criticality_type = None + self.imput_and_susceptibility_table = None + self.mean_is = None + def create_failureGraph(self, matrix_file, matrix_sheet, probabilities_file, probability_sheet): '''Create a graph of failures based on the FAModel Project object @@ -41,8 +46,9 @@ def create_failureGraph(self, matrix_file, matrix_sheet, probabilities_file, pro init_prob_dict.update({nodeNames[prob_index]: probabilities[prob_index]}) # Determine angle of clashing we are interested in - angle_degree = float(input("What angle do you want to use? (in degrees) ")) - self.angle_radians = angle_degree/360 * math.pi * 2 + angle_degree = input("What angle do you want to use? (in degrees) ") + if angle_degree == '': self.angle_radians = 0.0 + else: self.angle_radians = float(angle_degree)/360 * math.pi * 2 # Initialize and create the dictionaries of the children and parents self.failures_c = {} @@ -58,7 +64,9 @@ def create_failureGraph(self, matrix_file, matrix_sheet, probabilities_file, pro self.failures_c.update({nodeNames[i]: node_children}) self.failures_p.update({nodeNames[i]: node_parents}) + # Get the systems (groups of failures by component type) and list of nodes that could impact the FAModel systems = self.get_systems(nodeNames) + impacts = self.get_impact_nodes(nodeNames) # Initialize graph, boolean for plotting, and list of probabilities self.G = nx.DiGraph() @@ -77,20 +85,21 @@ def create_failureGraph(self, matrix_file, matrix_sheet, probabilities_file, pro for platform_failure in systems['platform']: if platform_failure in failure_probabilities.keys(): fail_prob = failure_probabilities[platform_failure] else: fail_prob = init_prob_dict[platform_failure] - self.G.add_node(platform_failure + "\n" + str(platform), probability=fail_prob, obj = [platform_obj]) + self.G.add_node(platform_failure + "\n" + str(platform), probability=fail_prob, obj=[platform_obj], impacts=impacts[platform_failure]) self.G = self.addMoreEdges(platform_failure, platform, [platform]) # Create failure nodes for turbine_failure in systems['turbine']: if turbine_failure in failure_probabilities.keys(): fail_prob = failure_probabilities[turbine_failure] else: fail_prob = init_prob_dict[turbine_failure] - self.G.add_node(turbine_failure + "\n" + str(platform), probability=fail_prob, obj = [platform_obj]) + self.G.add_node(turbine_failure + "\n" + str(platform), probability=fail_prob, obj=[platform_obj], impacts=impacts[turbine_failure]) self.G = self.addMoreEdges(turbine_failure, platform, [platform]) # FIRST DEGREE EDGES ------------------------------------------------------------------------------------------- for attach1 in attachments.keys(): attach1_name = str(attachments[attach1]['id']) attach1_type = '' + failure_probabilities = attachments[attach1]['obj'].failure_probability if 'mooring' in str(type(attachments[attach1]['obj'])): if attachments[attach1]['obj'].shared: attach1_type = 'sharedmooring' else: attach1_type = 'mooring' @@ -102,9 +111,9 @@ def create_failureGraph(self, matrix_file, matrix_sheet, probabilities_file, pro for attach1_failure in systems[attach1_type]: original_name = attach1_name if 'connect' in attach1_failure: attach1_name = platform + attach1_name - if attach1_failure in attachments[attach1]['obj'].failure_probability.keys(): fail_prob = failure_probabilities[attach1_failure] + if attach1_failure in failure_probabilities.keys(): fail_prob = failure_probabilities[attach1_failure] else: fail_prob = init_prob_dict[attach1_failure] - self.G.add_node(attach1_failure + "\n" + attach1_name, probability=fail_prob, obj = [attachments[attach1]['obj']]) + self.G.add_node(attach1_failure + "\n" + attach1_name, probability=fail_prob, obj=[attachments[attach1]['obj']], impacts=impacts[attach1_failure]) self.G = self.addMoreEdges(attach1_failure, attach1_name, [platform, attach1_name]) attach1_name = original_name @@ -119,9 +128,9 @@ def create_failureGraph(self, matrix_file, matrix_sheet, probabilities_file, pro if 'shared' in attach1_type and all(abs(np.array(attachments[attach1]['obj'].rB[:2]) - np.array(attachments[attach2]['obj'].rB[:2])) < 100): reverse = True else: reverse = False if self.couldClash(clash_failure, attachments[attach1]['obj'], attachments[attach2]['obj'], reverse): - if clash_failure in attachments[attach1]['obj'].failure_probability.keys(): fail_prob = failure_probabilities[clash_failure] + if clash_failure in failure_probabilities.keys(): fail_prob = failure_probabilities[clash_failure] else: fail_prob = init_prob_dict[clash_failure] - self.G.add_node(clash_failure + "\n" + clash_name, probability=fail_prob, obj = [attachments[attach1]['obj'], attachments[attach2]['obj']]) + self.G.add_node(clash_failure + "\n" + clash_name, probability=fail_prob, obj=[attachments[attach1]['obj'], attachments[attach2]['obj']], impacts=impacts[clash_failure]) self.G = self.addMoreEdges(clash_failure, clash_name, [platform, attach1_name, attach2_name, clash_name]) if attach1_type == 'mooring' and attach2_type == attach1_type: mooring_clashes.append(clash_failure + "\n" + clash_name) elif ('shared' not in attach1_type) and ('shared' not in attach2_type): cable_clashes.append(clash_failure + "\n" + clash_name) @@ -130,6 +139,7 @@ def create_failureGraph(self, matrix_file, matrix_sheet, probabilities_file, pro subcomponents = attachments[attach1]['obj'].subcomponents component_num = 0 for component in subcomponents: + failure_probabilities = component.failure_probability component_num += 1 if 'mooring' in attach1_type: if 'type' in component.keys(): @@ -167,9 +177,9 @@ def create_failureGraph(self, matrix_file, matrix_sheet, probabilities_file, pro component_name = attach1_name + ' ' + str(component.id) for component_failure in systems[component_type]: - if component_failure in component.failure_probability.keys(): fail_prob = failure_probabilities[component_failure] + if component_failure in failure_probabilities: fail_prob = failure_probabilities[component_failure] else: fail_prob = init_prob_dict[component_failure] - self.G.add_node(component_failure + "\n" + component_name, probability=fail_prob, obj = [component]) + self.G.add_node(component_failure + "\n" + component_name, probability=fail_prob, obj=[component], impacts=impacts[component_failure]) self.G = self.addMoreEdges(component_failure, component_name, [platform, attach1]) @@ -177,6 +187,7 @@ def create_failureGraph(self, matrix_file, matrix_sheet, probabilities_file, pro attached_to = attachments[attach1]['obj'].attached_to for attach1A in attached_to: attach1A_name = str(attach1A.id) + failure_probabilities = attach1A.failure_probability # Create anchor failure nodes if 'anchor' in str(type(attach1A)): @@ -185,7 +196,7 @@ def create_failureGraph(self, matrix_file, matrix_sheet, probabilities_file, pro for anchor_failure in systems[attach1A_type]: if anchor_failure in attach1A.failure_probability.keys(): fail_prob = failure_probabilities[anchor_failure] else: fail_prob = init_prob_dict[anchor_failure] - self.G.add_node(anchor_failure + "\n" + attach1A_name, probability=fail_prob, obj = [attach1A]) + self.G.add_node(anchor_failure + "\n" + attach1A_name, probability=fail_prob, obj=[attach1A], impacts=impacts[anchor_failure]) self.G = self.addMoreEdges(anchor_failure, attach1A_name, [platform, attach1_name, attach1A_name]) # Create edges between platforms @@ -199,16 +210,17 @@ def create_failureGraph(self, matrix_file, matrix_sheet, probabilities_file, pro elif 'substation' in str(type(attach1A)): attach1A_type = 'substation' for grid_failure in systems['grid']: - if grid_failure in attach1A.failure_probability.keys(): fail_prob = failure_probabilities[grid_failure] + if grid_failure in failure_probabilities.keys(): fail_prob = failure_probabilities[grid_failure] else: fail_prob = init_prob_dict[grid_failure] - self.G.add_node(grid_failure + "\n" + attach1A_name, probability=fail_prob, obj = [attach1A]) + self.G.add_node(grid_failure + "\n" + attach1A_name, probability=fail_prob, obj=[attach1A], impacts=impacts[grid_failure]) self.G = self.addMoreEdges(grid_failure, attach1A_name, [platform, attach1_name, attach1A_name]) # Create mooring-mooring clashing failure node if no two mooring lines likely to clash + failure_probabilities = self.Array.platformList[platform].failure_probability if len(mooring_clashes) < 1: if systems['mooringmooring'][0] in failure_probabilities.keys(): fail_prob = failure_probabilities[systems['mooringmooring'][0]] else: fail_prob = init_prob_dict[systems['mooringmooring'][0]] - self.G.add_node(systems['mooringmooring'][0] + "\n" + str(platform), probability=fail_prob, obj= [platform_obj]) + self.G.add_node(systems['mooringmooring'][0] + "\n" + str(platform), probability=fail_prob, obj=[platform_obj], impacts=impacts[systems['mooringmooring'][0]]) self.G = self.addMoreEdges(systems['mooringmooring'][0], str(platform), [platform]) # Create cable-mooring clashing failure nodes if no cable and mooring pairing likely to clash @@ -218,9 +230,8 @@ def create_failureGraph(self, matrix_file, matrix_sheet, probabilities_file, pro for clashing_failure in systems['cablemooring']: if clashing_failure in failure_probabilities.keys(): fail_prob = failure_probabilities[clashing_failure] else: fail_prob = init_prob_dict[clashing_failure] - self.G.add_node(clashing_failure + "\n" + str(platform) + ' ' + str(cable_num), probability=fail_prob, obj = [platform_obj, cable_num]) + self.G.add_node(clashing_failure + "\n" + str(platform) + ' ' + str(cable_num), probability=fail_prob, obj=[platform_obj, cable_num], impacts=impacts[clashing_failure]) self.G = self.addMoreEdges(clashing_failure, str(platform) + ' ' + str(cable_num), [platform]) - return self.G @@ -260,6 +271,26 @@ def get_systems(self, nodeNames): 'sharedmooring':nodeNames[sharedmooring], 'sharedmooringcable':nodeNames[mooringcable], 'joints': nodeNames[joints], 'cablesharedmooring':nodeNames[mooringcable], 'sharedmooringmooring':nodeNames[mooringmooring], 'mooringsharedmooring':nodeNames[mooringmooring], 'sharedanchor': nodeNames[sharedanchor]} return systems + + + + def get_impact_nodes(self, nodeNames): + '''Create dictionary for each node that tells us if the node could impact the FAModel + Parameters + ---------- + nodeNames : list + List of all the failure names to use to create dictionary of subsystems + ''' + # List of nodes that could impact hte FAModel + could_impact = [2, 6, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 20, 21, 23] + impactful_nodes = nodeNames[could_impact] + + # Create dictionary of nodes that tells us if the node impacts the FAModel or not + impact_dict = {} + for node in nodeNames: + if node in impactful_nodes: impact_dict.update({node: True}) + else: impact_dict.update({node: False}) + return impact_dict def get_critical_node(self, param): @@ -270,6 +301,7 @@ def get_critical_node(self, param): Measurement for criticality (either initial probability, degree [in, out, or total], susceptibility, or impact) ''' nodalNames = np.array(list(self.G.nodes)) + self.criticality_type = param critical_prob = [0, []] # Find critical failure for critical indicating the maximum initial probability @@ -301,6 +333,8 @@ def get_critical_node(self, param): if 'impact' in param: critical_prob = max_impact elif 'sus' in param: critical_prob = max_sus else: return + + self.critical_failures = critical_prob return critical_prob @@ -338,7 +372,7 @@ def get_susceptibility_and_impact(self): arr = nx.to_numpy_array(self.G) nodeNames = np.reshape(np.array(list(self.G.nodes)), (len(list(self.G.nodes)), )) all_probabilities = np.zeros(arr.shape) # Initialize a large array to put all the pairwise probabilities in - for start_component in range(1,arr.shape[0]+1): # Iterate through each failure mode/effect in turbine + for start_component in range(1,len(list(self.G.nodes))+1): # Iterate through each failure mode/effect in turbine a = arr.copy() non = nodeNames K, a, g, e, m, non = breadth_first_multi(a, nodeNames, [start_component], "child") # Generate tree for Bayesian network @@ -394,6 +428,9 @@ def get_susceptibility_and_impact(self): index2 = np.where(nodeNames == nodeNamesArray[node])[0][0] all_probabilities[0 * arr.shape[0] + start_component - 1][0 * arr.shape[0] + index2] = sm_table[0]/np.sum(sm_table) + # Save the probabilities calcuated + self.imput_and_susceptibility_table = all_probabilities + # Calculate and return highest impact and susceptibility mean_impact = np.mean(all_probabilities, axis=1) max_impact = np.max(mean_impact) @@ -402,7 +439,9 @@ def get_susceptibility_and_impact(self): mean_susceptibility = np.mean(all_probabilities, axis=0) max_susceptibility = np.max(mean_susceptibility, axis=0) max_impact_susceptibility = np.where(mean_impact == max_impact)[0][0] - return [max_impact, nodeNamesArray[max_impact_index]], [max_susceptibility, nodeNamesArray[max_impact_susceptibility]] + + self.mean_is = np.hstack(np.reshape(mean_impact, (len(list(self.G.nodes)), 1)), np.reshape(mean_susceptibility, (len(list(self.G.nodes)), 1))) + return [max_impact, nodeNames[max_impact_index]], [max_susceptibility, nodeNames[max_impact_susceptibility]] @@ -525,4 +564,126 @@ def get_min_max_vals(self, pnt2, pnt1, angle_radians, reverse): if max_x == min_x: if max_x < 0: max_x = 0 elif min_x > 0: min_x = 0 - return max_x, min_x, max_y, min_y \ No newline at end of file + return max_x, min_x, max_y, min_y + + + def enact_failures(self): + use_double_random = True + # Ask user which failures they would like to enact + if len(self.critical_failures[1]) > 1: + user_input = input('\nThere are multiple ctitical failures. Would you like to enact all of them? (y/n) ') + if 'n' in user_input: + print('Which of the following critical failures would you like to enact? (type failures as seen below with commas seperating them and each without apostrophes)') + user_input2 = input('Critical Failures:' + str(self.critical_failures[1]) + '\n') + self.critical_failures[1] = list(user_input2.split(", ")) + for i in range(len(self.critical_failures[1])): + self.critical_failures[1][i] = self.critical_failures[1][i].replace('\\n', '\n') + + # Update FAModel + for cf_name in self.critical_failures[1]: + # if critical_failure['impacts']: + # If the critical failure moves the platform, move the platform & update mooring info + if cf_name[-5:len(cf_name)] in self.Array.platformList.keys(): + old_position = np.array(self.G.nodes[cf_name]['obj'][0].r[:2]) + print('old position', old_position) + if use_double_random: + rand_vector = np.random.rand(2) + rand_movement = np.random.rand()*800 + new_vector = rand_vector/(np.sqrt(rand_vector[0]**2 + rand_vector[1]**2)) * rand_movement + else: new_vector = [0, 0] + self.G.nodes[cf_name]['obj'][0].r[0] += int(new_vector[0]) + self.G.nodes[cf_name]['obj'][0].r[1] += int(new_vector[1]) + + attachments = self.G.nodes[cf_name]['obj'][0].attachments + for attach1 in attachments: + # print(attachments[attach1], type(attachments[attach1])) + # print('old', attachments[attach1]['obj'].rA) + # print('old', attachments[attach1]['obj'].rB) + # print(abs(np.array(attachments[attach1]['obj'].rA[:2]) - old_position), abs(attachments[attach1]['obj'].rA[:2] - old_position) < 100) + # print(abs(np.array(attachments[attach1]['obj'].rB[:2]) - old_position), abs(attachments[attach1]['obj'].rB[:2] - old_position) < 100) + if all(abs(attachments[attach1]['obj'].rA[:2] - old_position) < 100): + # print('rA') + attachments[attach1]['obj'].rA[:2] += new_vector + elif all(abs(attachments[attach1]['obj'].rB[:2] - old_position) < 100): + # print('rB') + attachments[attach1]['obj'].rB[:2] += new_vector + # print('new', attachments[attach1]['obj'].rA) + # print('new', attachments[attach1]['obj'].rB, '\n') + + # If the anchors move, move the anchors & update mooring info + # If line (mooring or cable) disconnects, disconnect in FAModel + # Check if any new mooring-mooring or mooring-cable clashes are applicable + # Update the failure graph with these new clashing failures + # Remove critical failure from the graph + + + def update_critical_node(self, criticality_stipulation): + '''Determine and return new critical failures (presumably after the previous critical failure(s) occur) + Parameters + ---------- + criticality_stipulation : string + Specifies how to determine the new critical failures + ''' + # If multiple critical nodes allowed, give the option to use both of the above methods to find critical nodes + if ('child' in criticality_stipulation.lower() and 'global' in criticality_stipulation.lower()) or 'both' in criticality_stipulation.lower(): + clist1 = self.get_child_critical_node() + clist2 = self.get_critical_node(self.criticality_type) + self.critical_failures[0] = (clist1[0] + clist2[0])/2 + self.critical_failures[1] = clist1[1] + clist2[1] + + # Find new critical failure (such that the new critical failure must be a direct effect of the previous one) + elif 'child' in criticality_stipulation.lower(): self.critical_failures = self.get_child_critical_node() + + # Find critical node such that it doesn't have to be a direct effect of the previous critical failure + elif 'global' in criticality_stipulation.lower(): + for critical_failure in self.critical_failures[1]: + self.G.remove_node(critical_failure) + self.critical_failures = self.get_critical_node(self.criticality_type) + + return self.critical_failures + + + def get_child_critical_node(self): + '''Get the critical nodes when user desires future critical nodes be children of original critical failure(s) + Parameters + ---------- + None + ''' + # Create list of direct effects from critical failure(s) + nodalNames = np.array(list(self.G.nodes)) + critical_prob = [0, []] + critical_successors = [] + for critical_failure in self.critical_failures[1]: + node_index = np.where(nodalNames == critical_failure)[0][0] + nodes = diagonal_nodes(nx.to_numpy_array(self.G)) + a = make_binary(nx.to_numpy_array(self.G), 0) + child_bool = nodes @ a[:, node_index] + children = child_bool[np.nonzero(child_bool)] + for child in children: + if not(nodalNames[int(child)] in critical_successors): critical_successors.append(nodalNames[int(child)]) + self.G.remove_node(critical_failure) + + # Find critical failure for critical indicating the maximum initial probability + if 'prob' in self.criticality_type: + nodal_probabilities = nx.get_node_attributes(self.G.subgraph(critical_successors), "probability") + for failure_node in nodal_probabilities: + if nodal_probabilities[failure_node] > critical_prob[0]: critical_prob = [nodal_probabilities[failure_node], [failure_node]] + elif nodal_probabilities[failure_node] == critical_prob[0]: critical_prob[1].append(failure_node) + + # Find the critical failure for crtitical refering to the maximum degree (either in-degree, out-degree, or total-degree) + elif 'deg' in self.criticality_type: + out_deg, in_deg, deg = max_degrees(nx.to_numpy_array(self.G), nodalNames, threshold = 0, name = True) + if 'in' in self.criticality_type: critical_prob = [in_deg[1], list(in_deg[0])] + elif 'out' in self.criticality_type: critical_prob = [out_deg[1], list(out_deg[0])] + else: critical_prob = [deg[1], list(deg[0])] + + # Find the crtitical failure for ctitical refering to the susceptibility or impact of a node + elif 'sus' in self.criticality_type or 'impact' in self.criticality_type: + if 'sus' in self.criticality_type: row_num = 1 + elif 'impact' in self.criticality_type: row_num = 0 + vals = np.zeros(len(critical_successors), 2) + for node in critical_successors: vals[0] = self.mean_is[np.where(nodalNames == node)[0][0]] + critical_prob = [np.max(vals), critical_successors[np.where(critical_successors == np.max(vals))]] + + self.critical_failures = critical_prob + return critical_prob \ No newline at end of file From c7c1cbde14ba3db302bebe5bde14bd4cafa96cae Mon Sep 17 00:00:00 2001 From: EmmaSlackGraph Date: Mon, 10 Jun 2024 13:19:12 -0600 Subject: [PATCH 17/24] Initialize method to enact failures (still work in progress) --- failureGraphs.py | 107 +++++++++++++++++++++++++---------------------- 1 file changed, 56 insertions(+), 51 deletions(-) diff --git a/failureGraphs.py b/failureGraphs.py index 691a599a..b5733770 100644 --- a/failureGraphs.py +++ b/failureGraphs.py @@ -565,56 +565,6 @@ def get_min_max_vals(self, pnt2, pnt1, angle_radians, reverse): if max_x < 0: max_x = 0 elif min_x > 0: min_x = 0 return max_x, min_x, max_y, min_y - - - def enact_failures(self): - use_double_random = True - # Ask user which failures they would like to enact - if len(self.critical_failures[1]) > 1: - user_input = input('\nThere are multiple ctitical failures. Would you like to enact all of them? (y/n) ') - if 'n' in user_input: - print('Which of the following critical failures would you like to enact? (type failures as seen below with commas seperating them and each without apostrophes)') - user_input2 = input('Critical Failures:' + str(self.critical_failures[1]) + '\n') - self.critical_failures[1] = list(user_input2.split(", ")) - for i in range(len(self.critical_failures[1])): - self.critical_failures[1][i] = self.critical_failures[1][i].replace('\\n', '\n') - - # Update FAModel - for cf_name in self.critical_failures[1]: - # if critical_failure['impacts']: - # If the critical failure moves the platform, move the platform & update mooring info - if cf_name[-5:len(cf_name)] in self.Array.platformList.keys(): - old_position = np.array(self.G.nodes[cf_name]['obj'][0].r[:2]) - print('old position', old_position) - if use_double_random: - rand_vector = np.random.rand(2) - rand_movement = np.random.rand()*800 - new_vector = rand_vector/(np.sqrt(rand_vector[0]**2 + rand_vector[1]**2)) * rand_movement - else: new_vector = [0, 0] - self.G.nodes[cf_name]['obj'][0].r[0] += int(new_vector[0]) - self.G.nodes[cf_name]['obj'][0].r[1] += int(new_vector[1]) - - attachments = self.G.nodes[cf_name]['obj'][0].attachments - for attach1 in attachments: - # print(attachments[attach1], type(attachments[attach1])) - # print('old', attachments[attach1]['obj'].rA) - # print('old', attachments[attach1]['obj'].rB) - # print(abs(np.array(attachments[attach1]['obj'].rA[:2]) - old_position), abs(attachments[attach1]['obj'].rA[:2] - old_position) < 100) - # print(abs(np.array(attachments[attach1]['obj'].rB[:2]) - old_position), abs(attachments[attach1]['obj'].rB[:2] - old_position) < 100) - if all(abs(attachments[attach1]['obj'].rA[:2] - old_position) < 100): - # print('rA') - attachments[attach1]['obj'].rA[:2] += new_vector - elif all(abs(attachments[attach1]['obj'].rB[:2] - old_position) < 100): - # print('rB') - attachments[attach1]['obj'].rB[:2] += new_vector - # print('new', attachments[attach1]['obj'].rA) - # print('new', attachments[attach1]['obj'].rB, '\n') - - # If the anchors move, move the anchors & update mooring info - # If line (mooring or cable) disconnects, disconnect in FAModel - # Check if any new mooring-mooring or mooring-cable clashes are applicable - # Update the failure graph with these new clashing failures - # Remove critical failure from the graph def update_critical_node(self, criticality_stipulation): @@ -686,4 +636,59 @@ def get_child_critical_node(self): critical_prob = [np.max(vals), critical_successors[np.where(critical_successors == np.max(vals))]] self.critical_failures = critical_prob - return critical_prob \ No newline at end of file + return critical_prob + + + def enact_failures(self): + '''Update the FAModel based on critical failures occurring + Parameters + ---------- + None + ''' + use_double_random = True + # Ask user which failures they would like to enact + if len(self.critical_failures[1]) > 1: + user_input = input('\nThere are multiple ctitical failures. Would you like to enact all of them? (y/n) ') + if 'n' in user_input: + print('Which of the following critical failures would you like to enact? (type failures as seen below with commas seperating them and each without apostrophes)') + user_input2 = input('Critical Failures:' + str(self.critical_failures[1]) + '\n') + self.critical_failures[1] = list(user_input2.split(", ")) + for i in range(len(self.critical_failures[1])): + self.critical_failures[1][i] = self.critical_failures[1][i].replace('\\n', '\n') + + # Update FAModel + for cf_name in self.critical_failures[1]: + # if critical_failure['impacts']: + # If the critical failure moves the platform, move the platform & update mooring info + if cf_name[-5:len(cf_name)] in self.Array.platformList.keys(): + old_position = np.array(self.G.nodes[cf_name]['obj'][0].r[:2]) + print('old position', old_position) + if use_double_random: + rand_vector = np.random.rand(2) + rand_movement = np.random.rand()*800 + new_vector = rand_vector/(np.sqrt(rand_vector[0]**2 + rand_vector[1]**2)) * rand_movement + else: new_vector = [0, 0] + self.G.nodes[cf_name]['obj'][0].r[0] += int(new_vector[0]) + self.G.nodes[cf_name]['obj'][0].r[1] += int(new_vector[1]) + + attachments = self.G.nodes[cf_name]['obj'][0].attachments + for attach1 in attachments: + # print(attachments[attach1], type(attachments[attach1])) + # print('old', attachments[attach1]['obj'].rA) + # print('old', attachments[attach1]['obj'].rB) + # print(abs(np.array(attachments[attach1]['obj'].rA[:2]) - old_position), abs(attachments[attach1]['obj'].rA[:2] - old_position) < 100) + # print(abs(np.array(attachments[attach1]['obj'].rB[:2]) - old_position), abs(attachments[attach1]['obj'].rB[:2] - old_position) < 100) + if all(abs(attachments[attach1]['obj'].rA[:2] - old_position) < 100): + # print('rA') + attachments[attach1]['obj'].rA[:2] += new_vector + elif all(abs(attachments[attach1]['obj'].rB[:2] - old_position) < 100): + # print('rB') + attachments[attach1]['obj'].rB[:2] += new_vector + # print('new', attachments[attach1]['obj'].rA) + # print('new', attachments[attach1]['obj'].rB, '\n') + + # If the anchors move, move the anchors & update mooring info + # If line (mooring or cable) disconnects, disconnect in FAModel + # Check if any new mooring-mooring or mooring-cable clashes are applicable + # Update the failure graph with these new clashing failures + # Remove critical failure from the graph \ No newline at end of file From 6924bad75f6fbecd474f1e26e12c1a714a4cdb22 Mon Sep 17 00:00:00 2001 From: EmmaSlackGraph Date: Wed, 12 Jun 2024 13:38:59 -0600 Subject: [PATCH 18/24] Change enact function --- failureGraphs.py | 72 +++++++++++------------------------------------- 1 file changed, 16 insertions(+), 56 deletions(-) diff --git a/failureGraphs.py b/failureGraphs.py index b5733770..be6d36f6 100644 --- a/failureGraphs.py +++ b/failureGraphs.py @@ -565,8 +565,23 @@ def get_min_max_vals(self, pnt2, pnt1, angle_radians, reverse): if max_x < 0: max_x = 0 elif min_x > 0: min_x = 0 return max_x, min_x, max_y, min_y + + def enact_failures(self, failure): + '''Update the FAModel based on failure(s) occurring + Parameters + ---------- + failure : object + Object of failure you would like to enact + ''' + # Run simulation without failure + without_failure = self.Array + # Run simulation with failure + with_failure = self.Array + + return without_failure, with_failure + def update_critical_node(self, criticality_stipulation): '''Determine and return new critical failures (presumably after the previous critical failure(s) occur) Parameters @@ -636,59 +651,4 @@ def get_child_critical_node(self): critical_prob = [np.max(vals), critical_successors[np.where(critical_successors == np.max(vals))]] self.critical_failures = critical_prob - return critical_prob - - - def enact_failures(self): - '''Update the FAModel based on critical failures occurring - Parameters - ---------- - None - ''' - use_double_random = True - # Ask user which failures they would like to enact - if len(self.critical_failures[1]) > 1: - user_input = input('\nThere are multiple ctitical failures. Would you like to enact all of them? (y/n) ') - if 'n' in user_input: - print('Which of the following critical failures would you like to enact? (type failures as seen below with commas seperating them and each without apostrophes)') - user_input2 = input('Critical Failures:' + str(self.critical_failures[1]) + '\n') - self.critical_failures[1] = list(user_input2.split(", ")) - for i in range(len(self.critical_failures[1])): - self.critical_failures[1][i] = self.critical_failures[1][i].replace('\\n', '\n') - - # Update FAModel - for cf_name in self.critical_failures[1]: - # if critical_failure['impacts']: - # If the critical failure moves the platform, move the platform & update mooring info - if cf_name[-5:len(cf_name)] in self.Array.platformList.keys(): - old_position = np.array(self.G.nodes[cf_name]['obj'][0].r[:2]) - print('old position', old_position) - if use_double_random: - rand_vector = np.random.rand(2) - rand_movement = np.random.rand()*800 - new_vector = rand_vector/(np.sqrt(rand_vector[0]**2 + rand_vector[1]**2)) * rand_movement - else: new_vector = [0, 0] - self.G.nodes[cf_name]['obj'][0].r[0] += int(new_vector[0]) - self.G.nodes[cf_name]['obj'][0].r[1] += int(new_vector[1]) - - attachments = self.G.nodes[cf_name]['obj'][0].attachments - for attach1 in attachments: - # print(attachments[attach1], type(attachments[attach1])) - # print('old', attachments[attach1]['obj'].rA) - # print('old', attachments[attach1]['obj'].rB) - # print(abs(np.array(attachments[attach1]['obj'].rA[:2]) - old_position), abs(attachments[attach1]['obj'].rA[:2] - old_position) < 100) - # print(abs(np.array(attachments[attach1]['obj'].rB[:2]) - old_position), abs(attachments[attach1]['obj'].rB[:2] - old_position) < 100) - if all(abs(attachments[attach1]['obj'].rA[:2] - old_position) < 100): - # print('rA') - attachments[attach1]['obj'].rA[:2] += new_vector - elif all(abs(attachments[attach1]['obj'].rB[:2] - old_position) < 100): - # print('rB') - attachments[attach1]['obj'].rB[:2] += new_vector - # print('new', attachments[attach1]['obj'].rA) - # print('new', attachments[attach1]['obj'].rB, '\n') - - # If the anchors move, move the anchors & update mooring info - # If line (mooring or cable) disconnects, disconnect in FAModel - # Check if any new mooring-mooring or mooring-cable clashes are applicable - # Update the failure graph with these new clashing failures - # Remove critical failure from the graph \ No newline at end of file + return critical_prob \ No newline at end of file From 222714d18a5f4baa6266bed846544b2aea62a0de Mon Sep 17 00:00:00 2001 From: Matt Hall <5151457+mattEhall@users.noreply.github.com> Date: Thu, 6 Jun 2024 16:21:49 -0600 Subject: [PATCH 19/24] Mooring update for Subsystem "span" update and including rA, rB defaults --- famodel/mooring/mooring.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/famodel/mooring/mooring.py b/famodel/mooring/mooring.py index b56eeb19..a507bc76 100644 --- a/famodel/mooring/mooring.py +++ b/famodel/mooring/mooring.py @@ -102,17 +102,17 @@ def __init__(self, dd=None, subsystem=None, anchor=None, # MoorPy subsystem that corresponds to the mooring line self.ss = subsystem - # end point absolute coordinates, to be set later - self.rA = None - self.rB = None - self.heading = 0 - # relative positions (variables could be renamed) self.rad_anch = dd['span'] + dd['rad_fair'] self.rad_fair = dd['rad_fair'] self.z_anch = dd['zAnchor'] self.z_fair = dd['z_fair'] + # end point absolute coordinates, to be set later + self.rA = np.array([-self.rad_anch, 0, self.z_anch]) + self.rB = np.array([-self.rad_fair, 0, self.z_fair]) + self.heading = 270 # compass heading from B to A [deg] + self.adjuster = None # custom function that can adjust the mooring self.shared = False # boolean for if the mooring line is a shared line @@ -168,8 +168,8 @@ def reposition(self, r_center=None, heading=None, project=None, degrees=False, * r_center The x, y coordinates of the platform (undisplaced) [m]. heading : float - The absolute heading of the mooring line [deg or rad] depending on - degrees parameter (True or False). + The absolute heading compass direeciton of the mooring line + [deg or rad] depending on degrees parameter (True or False). project : FAModel Project, optional A Project-type object for site-specific information used in custom mooring line adjustment functions (mooring.adjuster). @@ -180,12 +180,14 @@ def reposition(self, r_center=None, heading=None, project=None, degrees=False, * # Adjust heading if provided if not heading == None: if degrees: - self.heading = np.radians(heading) - else: self.heading = heading - + else: + self.heading = np.degrees(heading) + + phi = np.radians(90-self.heading) # heading in x-y radian convention [rad] + # heading 2D unit vector - u = np.array([np.cos(self.heading), np.sin(self.heading)]) + u = np.array([np.cos(phi), np.sin(phi)]) #print(u) r_center = np.array(r_center)[:2] # Set the updated fairlead location @@ -262,7 +264,9 @@ def createSubsystem(self, case=0): # check if a subsystem already exists if self.ss: print('A subsystem for this Mooring class instance already exists, this will be overwritten.') - self.ss=Subsystem(depth=-self.dd['zAnchor'], rho=self.rho, g=self.g, span=self.dd['span'],rAFair=self.rA, rBFair=self.rB) + self.ss=Subsystem(depth=-self.dd['zAnchor'], rho=self.rho, g=self.g, + span=self.dd['span'], rad_fair=self.rad_fair, + z_fair=self.z_fair) lengths = [] types = [] # run through each line section and collect the length and type @@ -280,7 +284,7 @@ def createSubsystem(self, case=0): suspended=case) self.ss.setEndPosition(self.rA,endB=0) self.ss.setEndPosition(self.rB,endB=1) - + breakpoint() # note: next bit has similar code/function as Connector.makeMoorPyConnector <<< @@ -629,4 +633,4 @@ def addCorrosion(self,corrosion_mm=10): if sec['material']=='chain': MBL_cor = sec['MBL']*( (sec['d_nom']-(corrosion_mm/1000))/sec['d_nom'] )**2 # corroded MBL else: - MBL_cor = sec['MBL'] \ No newline at end of file + MBL_cor = sec['MBL'] From 50cd49f7935abf31e935f7b969cd5ff70b05e093 Mon Sep 17 00:00:00 2001 From: Sirkis Date: Thu, 6 Jun 2024 17:26:51 -0600 Subject: [PATCH 20/24] Allow creation of Mooring object with subsystem instead of dd --Workaround to allow users to input a subsystem when creating a mooring object rather than a design dictionary --- famodel/cables/dynamic_cable.py | 4 ++-- famodel/mooring/mooring.py | 29 +++++++++++++++++------------ famodel/project.py | 2 +- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/famodel/cables/dynamic_cable.py b/famodel/cables/dynamic_cable.py index 5aa78262..745670ec 100644 --- a/famodel/cables/dynamic_cable.py +++ b/famodel/cables/dynamic_cable.py @@ -319,7 +319,7 @@ def createSubsystem(self, case=0): if self.ss: print('A subsystem for this Dynamic cable class instance already exists, this will be overwritten.') - self.ss=Subsystem(depth=-self.dd['zAnchor'], rho=self.rho, g=self.g, span=self.dd['span'], rBfair=self.rB) + self.ss=Subsystem(depth=-self.dd['zAnchor'], rho=self.rho, g=self.g, span=self.dd['span']) lengths = [] types = [] nsegs = [] @@ -334,7 +334,7 @@ def createSubsystem(self, case=0): elif nsegs[-1] < 3: nsegs[-1] = 3 # make the lines and set the points - self.ss.makeGeneric(lengths,types,suspended=case,nsegs=nsegs) + self.ss.makeGeneric(lengths,types,suspended=case) self.ss.setEndPosition(self.rA,endB=0) self.ss.setEndPosition(self.rB,endB=1) diff --git a/famodel/mooring/mooring.py b/famodel/mooring/mooring.py index a507bc76..bc42c06e 100644 --- a/famodel/mooring/mooring.py +++ b/famodel/mooring/mooring.py @@ -15,7 +15,7 @@ class Mooring(Edge): Work in progress. Eventually will inherit from Edge. ''' - def __init__(self, dd=None, subsystem=None, anchor=None, + def __init__(self, dd={}, subsystem=None, anchor=None, rho=1025, g=9.81,id=None): ''' Parameters @@ -54,18 +54,12 @@ def __init__(self, dd=None, subsystem=None, anchor=None, some additional manual setup of the mooring object after it is called. <<< - ''' - - # temporary hack to deal with duplicate parameters - if not 'zAnchor' in dd: - dd['zAnchor'] = z_anch - if not 'span' in dd: - dd['span'] = rad_anch - rad_fair - + ''' Edge.__init__(self, id) # initialize Edge base class # Design description dictionary for this Mooring self.dd = dd + # let's turn the dd into something that holds subdict objects of connectors and sections if self.dd: # >>> list of sections ? And optional of section types (e,g, chian, poly) @@ -101,6 +95,17 @@ def __init__(self, dd=None, subsystem=None, anchor=None, # MoorPy subsystem that corresponds to the mooring line self.ss = subsystem + # workaround for users who are making mooring objects based on pre-existing subsystems + if self.ss: + if not 'zAnchor' in dd: + dd['zAnchor'] = -self.ss.depth + if not 'span' in dd: + dd['span'] = self.ss.span + if not 'rad_fair' in dd: + dd['rad_fair'] = self.ss.rad_fair + if not 'z_fair' in dd: + dd['z_fair'] = self.ss.z_fair + # relative positions (variables could be renamed) self.rad_anch = dd['span'] + dd['rad_fair'] @@ -271,9 +276,9 @@ def createSubsystem(self, case=0): types = [] # run through each line section and collect the length and type for i, sec in enumerate(self.dd['sections']): - lengths.append(sec['length']) + lengths.append(deepcopy(sec['length'])) # points to existing type dict in self.dd for now - types.append(sec['type']) # list of type names + types.append(deepcopy(sec['type'])) # list of type names #types.append(sec['type']['name']) # list of type names #self.ss.lineTypes[i] = sec['type'] @@ -289,7 +294,7 @@ def createSubsystem(self, case=0): # note: next bit has similar code/function as Connector.makeMoorPyConnector <<< # add in connector info to subsystem points - if case == 0: # has an anchor - need to ignore connection for first point + if case == 0: # has an anchor - need to ignore connection for first point because anchor is a point itself so can't have a point attached to a point startNum = 1 else: # no anchor - need to include all connections startNum = 0 diff --git a/famodel/project.py b/famodel/project.py index eb6605f1..3216953d 100644 --- a/famodel/project.py +++ b/famodel/project.py @@ -1402,7 +1402,7 @@ def addCablesConnections(self,connDict): dd = {} cableType = connDict['props'][i][j] # running on assumption that each thing in props will have equivalent of a design dictionary # may need to add here to get the properties properly put in the design dictionary - + cable = cableType['name'] # create cable object self.cableList[(cable,i*j)] = SubseaCable((cable,i*j),dd=dd) # attach to platforms/substations From 324ca679b71370c7fa413cf60fda6f1918d5a88d Mon Sep 17 00:00:00 2001 From: Sirkis Date: Thu, 6 Jun 2024 17:43:05 -0600 Subject: [PATCH 21/24] Remove breakpoint in mooring.py --- famodel/mooring/mooring.py | 1 - 1 file changed, 1 deletion(-) diff --git a/famodel/mooring/mooring.py b/famodel/mooring/mooring.py index bc42c06e..ef7af488 100644 --- a/famodel/mooring/mooring.py +++ b/famodel/mooring/mooring.py @@ -289,7 +289,6 @@ def createSubsystem(self, case=0): suspended=case) self.ss.setEndPosition(self.rA,endB=0) self.ss.setEndPosition(self.rB,endB=1) - breakpoint() # note: next bit has similar code/function as Connector.makeMoorPyConnector <<< From 7bd1b2adcf2711e4b5b7ded02666e8dbe7dcc264 Mon Sep 17 00:00:00 2001 From: Sirkis Date: Fri, 7 Jun 2024 10:24:18 -0600 Subject: [PATCH 22/24] Minor mooring.py debug --- famodel/mooring/mooring.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/famodel/mooring/mooring.py b/famodel/mooring/mooring.py index ef7af488..bb04807a 100644 --- a/famodel/mooring/mooring.py +++ b/famodel/mooring/mooring.py @@ -15,7 +15,7 @@ class Mooring(Edge): Work in progress. Eventually will inherit from Edge. ''' - def __init__(self, dd={}, subsystem=None, anchor=None, + def __init__(self, dd=None, subsystem=None, anchor=None, rho=1025, g=9.81,id=None): ''' Parameters @@ -55,7 +55,6 @@ def __init__(self, dd={}, subsystem=None, anchor=None, called. <<< ''' - Edge.__init__(self, id) # initialize Edge base class # Design description dictionary for this Mooring self.dd = dd @@ -97,21 +96,22 @@ def __init__(self, dd={}, subsystem=None, anchor=None, self.ss = subsystem # workaround for users who are making mooring objects based on pre-existing subsystems if self.ss: - if not 'zAnchor' in dd: - dd['zAnchor'] = -self.ss.depth - if not 'span' in dd: - dd['span'] = self.ss.span - if not 'rad_fair' in dd: - dd['rad_fair'] = self.ss.rad_fair - if not 'z_fair' in dd: - dd['z_fair'] = self.ss.z_fair + self.dd = {} + if not 'zAnchor' in self.dd: + self.dd['zAnchor'] = -self.ss.depth + if not 'span' in self.dd: + self.dd['span'] = self.ss.span + if not 'rad_fair' in self.dd: + self.dd['rad_fair'] = self.ss.rad_fair + if not 'z_fair' in self.dd: + self.dd['z_fair'] = self.ss.z_fair # relative positions (variables could be renamed) - self.rad_anch = dd['span'] + dd['rad_fair'] - self.rad_fair = dd['rad_fair'] - self.z_anch = dd['zAnchor'] - self.z_fair = dd['z_fair'] + self.rad_anch = self.dd['span'] + self.dd['rad_fair'] + self.rad_fair = self.dd['rad_fair'] + self.z_anch = self.dd['zAnchor'] + self.z_fair = self.dd['z_fair'] # end point absolute coordinates, to be set later self.rA = np.array([-self.rad_anch, 0, self.z_anch]) From 84f5f870f189e712a2285222745e686800b3db11 Mon Sep 17 00:00:00 2001 From: Sirkis Date: Fri, 7 Jun 2024 13:23:32 -0600 Subject: [PATCH 23/24] Platform setPosition allow send in of project class instance --- famodel/platform/platform.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/famodel/platform/platform.py b/famodel/platform/platform.py index ddf27aad..bca5683e 100644 --- a/famodel/platform/platform.py +++ b/famodel/platform/platform.py @@ -39,7 +39,7 @@ def __init__(self, id, r=[0,0], heading=0, mooring_headings=[60,180,300],rFair=N self.body = None # body object in MoorPy associated with the platform - self.mooring_headings = [np.radians(mooring_headings)] # headings of mooring lines [rad] + self.mooring_headings = np.radians(mooring_headings) # headings of mooring lines [rad] self.n_mooring = len(mooring_headings) # number of mooring lines @@ -58,7 +58,7 @@ def __init__(self, id, r=[0,0], heading=0, mooring_headings=[60,180,300],rFair=N self.failure_probability = {} - def setPosition(self, r, heading=None, degrees=False): + def setPosition(self, r, heading=None, degrees=False,project=None): ''' Set the position/orientation of the platform as well as the associated anchor points. @@ -99,7 +99,7 @@ def setPosition(self, r, heading=None, degrees=False): heading_i = self.mooring_headings[i] + self.phi # Reposition the whole Mooring - self.attachments[mooring]['obj'].reposition(self.r, heading=heading_i) + self.attachments[mooring]['obj'].reposition(self.r, heading=heading_i,project=project) def mooringSystem(self,rotateBool=0,mList=None): ''' From 7a3e040058200867aae9236c982624e46458eedb Mon Sep 17 00:00:00 2001 From: EmmaSlackGraph Date: Wed, 12 Jun 2024 13:43:48 -0600 Subject: [PATCH 24/24] Add failure_probabilitites dict to Section class in connector.py --- famodel/mooring/connector.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/famodel/mooring/connector.py b/famodel/mooring/connector.py index 0962fad1..9c6192eb 100644 --- a/famodel/mooring/connector.py +++ b/famodel/mooring/connector.py @@ -76,6 +76,9 @@ def __init__(self,id, **kwargs): dict.__init__(self, **kwargs) # initialize dict base class Edge.__init__(self, id) # initialize Edge base class + + # Dictionary of failure probabilities + self.failure_probability = {} # <<< have a ['type'] entry that stores a type, which could be used for sizing...