def calculate_custom_limit_state_w_conversion( self, variables: dict, inventory_type: str = "building"): """Computes limit state probabilities. Args: variables (dict): A dictionary of variables. inventory_type (str): A type of inventory. Returns: dict: Limit state probabilities for custom expression fragilities. """ output = FragilityCurveSet._initialize_limit_states(inventory_type) limit_state = list(output.keys()) index = 0 if len(self.fragility_curves) <= 4: for fragility_curve in self.fragility_curves: probability = fragility_curve.compute_custom_limit_state_probability( variables) output[limit_state[index]] = AnalysisUtil.update_precision( probability) # round to default digits index += 1 else: raise ValueError( "We can only handle fragility curves with less than 4 limit states." ) return output
def calculate_limit_state(self, hazard_values: dict = {}, inventory_type: str = "building", **kwargs): """WIP computation of limit state probabilities accounting for custom expressions. Args: hazard_values (dict): A dictionary with hazard values to compute probability. inventory_type (str): An inventory type. **kwargs: Keyword arguments. Returns: OrderedDict: Limit state probabilities. """ output = FragilityCurveSet._initialize_limit_states(inventory_type) limit_state = list(output.keys()) index = 0 if len(self.fragility_curves) <= 4: for fragility_curve in self.fragility_curves: probability = fragility_curve.solve_curve_expression( hazard_values, self.curve_parameters, **kwargs) output[limit_state[index]] = AnalysisUtil.update_precision( probability) # round to default digits index += 1 else: raise ValueError( "We can only handle fragility curves with less than 4 limit states." ) return output
def _4ls_to_5ds(limit_states): """ Args: limit_states (dict): Limit states. Returns: dict: Damage states. """ limit_states = AnalysisUtil.float_dict_to_decimal(limit_states) damage_states = AnalysisUtil.float_dict_to_decimal({ "DS_0": 0.0, "DS_1": 0.0, "DS_2": 0.0, "DS_3": 0.0, "DS_4": 0.0 }) small_overlap = FragilityCurveSet.is_there_small_overlap(limit_states) if small_overlap: ds_overlap = FragilityCurveSet.adjust_for_small_overlap( small_overlap, limit_states, damage_states) damage_states['DS_0'] = ds_overlap[0] damage_states['DS_1'] = ds_overlap[1] damage_states['DS_2'] = ds_overlap[2] damage_states['DS_3'] = ds_overlap[3] damage_states['DS_4'] = ds_overlap[4] else: damage_states['DS_0'] = 1 - limit_states["LS_0"] damage_states['DS_1'] = limit_states["LS_0"] - limit_states["LS_1"] damage_states['DS_2'] = limit_states["LS_1"] - limit_states["LS_2"] damage_states['DS_3'] = limit_states["LS_2"] - limit_states["LS_3"] damage_states['DS_4'] = limit_states["LS_3"] return damage_states
def _1ls_to_4ds(limit_states): """ Args: limit_states (dict): Limit states. Returns: dict: Damage states. """ limit_states = AnalysisUtil.float_dict_to_decimal(limit_states) damage_states = dict() damage_states['DS_0'] = 1 - limit_states["LS_0"] damage_states['DS_1'] = 0 damage_states['DS_2'] = 0 damage_states['DS_3'] = limit_states["LS_0"] return damage_states
def calculate_limit_state_w_conversion(self, hazard, period: float = 0.0, std_dev: float = 0.0, inventory_type: str = "building", **kwargs): """Computes limit state probabilities. Args: hazard (float): Hazard value to compute probability for. period (float): Period of the structure, if applicable. std_dev (float): Standard deviation. inventory_type (str): A type of inventory. **kwargs: Keyword arguments. Returns: dict: Limit state probabilities. """ output = FragilityCurveSet._initialize_limit_states(inventory_type) limit_state = list(output.keys()) index = 0 if len(self.fragility_curves) <= 4: for fragility_curve in self.fragility_curves: probability = fragility_curve.solve_curve_expression( hazard, period, std_dev, **kwargs) output[limit_state[index]] = AnalysisUtil.update_precision( probability) # round to default digits index += 1 else: raise ValueError( "We can only handle fragility curves with less than 4 limit states." ) return output
def test_get_exposure_from_hazard_values(hazard_vals, hazard_type, expected): hazard_exposure = AnalysisUtil.get_exposure_from_hazard_values(hazard_vals, hazard_type) assert hazard_exposure == expected
def get_damage(self, node_dataset, link_dataset, tornado_dataset, tornado_id): """ Args: node_dataset (obj): Node dataset. link_dataset (obj): Link dataset. tornado_dataset (obj): Tornado dataset. tornado_id (str): Tornado id. """ self.set_tornado_variables(tornado_dataset) self.set_node_variables(node_dataset) # get fragility curves set - tower for transmission, pole for distribution fragility_set_tower = FragilityCurveSet( self.fragilitysvc.get_dfr3_set(self.fragility_tower_id)) assert fragility_set_tower.id == self.fragility_tower_id fragility_set_pole = FragilityCurveSet( self.fragilitysvc.get_dfr3_set(self.fragility_pole_id)) assert fragility_set_pole.id == self.fragility_pole_id # network test node_id_validation = NetworkUtil.validate_network_node_ids( node_dataset, link_dataset, self.fromnode_fld_name, self.tonode_fld_name, self.nodenwid_fld_name) if node_id_validation is False: print( "ID in from or to node field doesn't exist in the node dataset" ) os.exit(0) # getting network graph and node coordinates is_directed_graph = True graph, node_coords = NetworkUtil.create_network_graph_from_field( link_dataset, self.fromnode_fld_name, self.tonode_fld_name, is_directed_graph) # reverse the graph to acculate the damage to next to node graph = nx.DiGraph.reverse(graph, copy=True) # check the connection as a list connection_sets = [] if is_directed_graph: connection_sets = list(nx.weakly_connected_components(graph)) else: connection_sets = list(nx.connected_components(graph)) # check the first node of the each network line, this first node should lead each separated network # also convert connection set to list first_node_list = [] connection_list = [] for c in connection_sets: connection_list.append(list(c)) first_node_list.append(list(c)[0]) intersection_list = [] poly_list = [] totalcost2repair = [] totalpoles2repair = [] totaltime2repair = [] # construct guid field guid_list = [] nodenwid_list = [] for node_feature in node_dataset: # get guid colum guid_fld_val = '' if self.guid_fldname.lower() in node_feature['properties']: guid_fld_val = node_feature['properties'][ self.guid_fldname.lower()] elif self.guid_fldname in node_feature['properties']: guid_fld_val = node_feature['properties'][self.guid_fldname] guid_list.append(guid_fld_val) # get nodenwid colum nodenwid_fld_val = '' if self.nodenwid_fld_name.lower() in node_feature['properties']: nodenwid_fld_val = int( node_feature['properties'][self.nodenwid_fld_name.lower()]) elif self.nodenwid_fld_name in node_feature['properties']: nodenwid_fld_val = int( node_feature['properties'][self.nodenwid_fld_name]) nodenwid_list.append(nodenwid_fld_val) for z in range(self.nmcs): nodedam = [ 0 ] * self.nnode # placeholder for recording number of damaged pole for each node noderepair = [ 0 ] * self.nnode # placeholder for recording repair cost for each node poles2repair = [ 0 ] * self.nnode # placeholder for recording total number of poles to repair cost2repairpath = [ 0 ] * self.nnode # placeholder for recording total repair cost for the network time2repairpath = [ 0 ] * self.nnode # placeholder for recording total repair time for the network nodetimerep = [0] * self.nnode hazardval = [[ 0 ]] * self.nnode # placeholder for recording hazard values demandtypes = [[ "" ]] * self.nnode # placeholder for recording demand types demandunits = [[ "" ]] * self.nnode # placeholder for recording demand units # iterate link for line_feature in link_dataset: ndamage = 0 # number of damaged poles in each link repaircost = 0 # repair cost value repairtime = 0 # repair time value to_node_val = "" linetype_val = "" tor_hazard_values = [0] # random wind speed in EF demand_types = [""] demand_units = [""] if self.tonode_fld_name.lower() in line_feature['properties']: to_node_val = line_feature['properties'][ self.tonode_fld_name.lower()] elif self.tonode_fld_name in line_feature['properties']: to_node_val = line_feature['properties'][ self.tonode_fld_name] if self.linetype_fld_name in line_feature['properties']: linetype_val = line_feature['properties'][ self.linetype_fld_name] elif self.linetype_fld_name.lower( ) in line_feature['properties']: linetype_val = line_feature['properties'][ self.linetype_fld_name.lower()] line = shape(line_feature['geometry']) # iterate tornado for tornado_feature in tornado_dataset: resistivity_probability = 0 # resistivity value at the point of windSpeed random_resistivity = 0 # random resistivity value between 0 and one sim_fld_val = "" ef_fld_val = "" # get EF rating and simulation number column if self.tornado_sim_field_name.lower( ) in tornado_feature['properties']: sim_fld_val = int(tornado_feature['properties'][ self.tornado_sim_field_name.lower()]) elif self.tornado_sim_field_name in tornado_feature[ 'properties']: sim_fld_val = int(tornado_feature['properties'][ self.tornado_sim_field_name]) if self.tornado_ef_field_name.lower( ) in tornado_feature['properties']: ef_fld_val = tornado_feature['properties'][ self.tornado_ef_field_name.lower()] elif self.tornado_ef_field_name in tornado_feature[ 'properties']: ef_fld_val = tornado_feature['properties'][ self.tornado_ef_field_name] if sim_fld_val == "" or ef_fld_val == "": print( "unable to convert tornado simulation field value to integer" ) sys.exit(0) # get Tornado EF polygon # assumes that the polygon is not a multipolygon poly = shape(tornado_feature['geometry']) poly_list.append(poly) # loop for ef ranges for f in range(self.tornado_ef_rate): npoles = 0 # number of poles in tornado ef box poleresist = 0 # pole's resistance value # setting EF rate value string to match in the tornado dataset's attribute table ef_content = "EF" + str(f) # compute the intersections between link line and ef polygon # also figure out the length of the line that ovelapped with EF box # compute the intersection between tornado polygon and line if sim_fld_val == z and ef_fld_val.lower( ) == ef_content.lower(): if poly is not None and line is not None: if poly.intersects(line): intersection = poly.intersection(line) any_point = None intersection_length = intersection.length if intersection.length > 0: # print(intersection.__class__.__name__) # calculate the length of intersected line # since this is a geographic, it has to be projected to meters to be calcuated inter_length_meter = GeoUtil.calc_geog_distance_from_linestring( intersection) if isinstance(intersection, MultiLineString): intersection_list.append( intersection) for inter_line in intersection.geoms: any_point = inter_line.centroid break elif isinstance( intersection, LineString): intersection_list.append( intersection) any_point = intersection.centroid # also, random point can be possible # by changing the following lines value 0.5 # any_point = intersection.interpolate(0.5, normalized=True) if any_point is not None: # check if any_point is in the polygon if poly.contains(any_point) is False: # this is very hardly happen but should be needed just in case any_point = poly.centroid # check if the line is tower or transmission if linetype_val.lower( ) == self.line_transmission: fragility_set_used = fragility_set_tower else: fragility_set_used = fragility_set_pole values_payload = [{ "demands": [ x.lower() for x in fragility_set_used.demand_types ], "units": [ x.lower() for x in fragility_set_used.demand_units ], "loc": str(any_point.coords[0][1]) + "," + str(any_point.coords[0][0]) }] h_vals = self.hazardsvc.post_tornado_hazard_values( tornado_id, values_payload, self.get_parameter('seed')) tor_hazard_values = AnalysisUtil.update_precision_of_lists( h_vals[0]["hazardValues"]) demand_types = h_vals[0]["demands"] demand_units = h_vals[0]["units"] hval_dict = dict() j = 0 for d in h_vals[0]["demands"]: hval_dict[d] = tor_hazard_values[j] j += 1 if isinstance( fragility_set_used. fragility_curves[0], DFR3Curve): inventory_args = fragility_set_used.construct_expression_args_from_inventory( tornado_feature) resistivity_probability = \ fragility_set_used.calculate_limit_state( hval_dict, inventory_type=fragility_set_used.inventory_type, **inventory_args) else: raise ValueError( "One of the fragilities is in deprecated format. This should not happen. " "If you are seeing this please report the issue." ) # randomly generated capacity of each poles ; 1 m/s is 2.23694 mph poleresist = resistivity_probability.get( 'LS_0') * 2.23694 npoles = int( round(inter_length_meter / self.pole_distance)) repairtime_list = [] for k in range(npoles): repair_time = 0 random_resistivity = random.uniform( 0, 1) if random_resistivity <= poleresist: ndamage += 1 # following codes can't be converted from matlab to python # however, the cross product <=3 or == 24 almost doesn't happen # since the time and cost differs when it is pole or tower, # this could be changed by see if it is tower or pole # if numpy.cross(k, z) <= 3 or numpy.cross(k, z) == 24: if linetype_val.lower( ) == self.line_transmission: mu = self.mut sigma = self.sigmat tmu = self.tmut tsigma = self.tsigmat else: mu = self.mud sigma = self.sigmad tmu = self.tmud tsigma = self.tsigmad repairtime_list.append( numpy.random.normal( tmu, tsigma)) for k in range(ndamage): repaircost += numpy.random.lognormal( mu, sigma) # max of the repair time among different poles is taken # as the repair time for that line if len(repairtime_list) > 0: repairtime = max(repairtime_list) noderepair[to_node_val - 1] = repaircost nodedam[to_node_val - 1] = ndamage nodetimerep[to_node_val - 1] = repairtime hazardval[to_node_val - 1] = tor_hazard_values demandtypes[to_node_val - 1] = demand_types demandunits[to_node_val - 1] = demand_units # Calculate damage and repair cost based on network for i in range(len(first_node_list)): for j in range(len(connection_list[i])): # print(connection_list[i][j], first_node_list[i]) pathij = list( nx.all_simple_paths(graph, connection_list[i][j], first_node_list[i])) poler = 0 coster = 0 timer = [] # print(pathij) if len(pathij) > 0: for k in range(len(pathij)): for var1 in range(len(pathij[k])): poler = poler + nodedam[pathij[k][var1]] coster = coster + noderepair[pathij[k][var1]] # max of the time for different lines is taken as the repair time for that path. # -- path is constituted of different lines. timer.append(nodetimerep[pathij[k][var1]]) poles2repair[connection_list[i][j]] = poler cost2repairpath[connection_list[i][j]] = coster if len(timer) > 0: time2repairpath[connection_list[i][j]] = max(timer) else: time2repairpath[connection_list[i][j]] = 0 totalcost2repair.append(cost2repairpath) totalpoles2repair.append(poles2repair) totaltime2repair.append(time2repairpath) # create guid field from node dataset # calculate mean and standard deviation meanpoles = numpy.mean(numpy.asarray(totalpoles2repair), axis=0) stdpoles = numpy.std(numpy.asarray(totalpoles2repair), axis=0) meancost = numpy.mean(numpy.asarray(totalcost2repair), axis=0) stdcost = numpy.std(numpy.asarray(totalcost2repair), axis=0) meantime = numpy.mean(numpy.asarray(totaltime2repair), axis=0) stdtime = numpy.std(numpy.asarray(totaltime2repair), axis=0) # create result ds_results = [] damage_results = [] for i in range(len(meanpoles)): ds_result = dict() damage_result = dict() ds_result['guid'] = guid_list[i] ds_result["meanpoles"] = meanpoles[i] ds_result["stdpoles"] = stdpoles[i] ds_result["meancost"] = meancost[i] ds_result["stdcost"] = stdcost[i] ds_result["meantime"] = meantime[i] ds_result["stdtime"] = stdtime[i] ds_result[ 'haz_expose'] = AnalysisUtil.get_exposure_from_hazard_values( hazardval[i], "tornado") damage_result['guid'] = guid_list[i] damage_result["fragility_tower_id"] = self.fragility_tower_id damage_result["fragility_pole_id"] = self.fragility_pole_id damage_result["hazardtype"] = "Tornado" damage_result['hazardvals'] = hazardval[i] damage_result['demandtypes'] = demandtypes[i] damage_result['demandunits'] = demandunits[i] ds_results.append(ds_result) damage_results.append(damage_result) return ds_results, damage_results