diff --git a/README.md b/README.md index 45bcd35..e9c4a05 100644 --- a/README.md +++ b/README.md @@ -68,10 +68,10 @@ python -m atc138.cli ./examples/ICSB ./examples/ICSB/output --seed 12345 --force ### Imported via Python script -Ensure that the `src/` directory is on the path of the main script. Then: +Once installed, the package can also be imported in scripts: ```python -from src.atc138 import driver +from atc138 import driver example_dir = './examples/ICSB' output_dir = './examples/ICSB/output' @@ -216,7 +216,7 @@ To use Pelicun outputs, ensure `simulated_inputs.json` does not exist in your mo The assessment will automatically detect Pelicun files and convert them to the standard ATC-138 format. Use the same CLI or Python API as normal: ```python -from src.atc138 import driver +from atc138 import driver example_dir = './examples/RCSW_4story_pelicun' output_dir = './examples/RCSW_4story_pelicun/output' diff --git a/src/atc138/functionality/other_functionality_functions.py b/src/atc138/functionality/other_functionality_functions.py index 5442e92..1282f59 100644 --- a/src/atc138/functionality/other_functionality_functions.py +++ b/src/atc138/functionality/other_functionality_functions.py @@ -1540,7 +1540,8 @@ def fn_extract_recovery_metrics( tenant_unit_recovery_day, # Determine replacement cases - replace_cases = np.logical_not(np.isnan(simulated_replacement_time)) + sim_rt = np.array(simulated_replacement_time, dtype=float) + replace_cases = ~np.isnan(sim_rt) ''' Post process tenant-level recovery times Overwrite NaNs in tenant_unit_day_functional Only NaN where never had functional loss, therefore set to zero''' diff --git a/src/atc138/input_builder.py b/src/atc138/input_builder.py index 1918205..438407a 100644 --- a/src/atc138/input_builder.py +++ b/src/atc138/input_builder.py @@ -740,6 +740,7 @@ def convert_pelicun(model_dir): accepted_first_tag = ['cmp', 'dmg', 'loss'] assert any(item in accepted_first_tag for item in dv_tag_meta), "Missing meta-tag in index column (i.e. 'cmp/dmg/loss-loc-dir-ds' must be provided at minimum)" assert set(reqd_tags) <= set(dv_tag_meta), "Missing meta-tag in index column (i.e. 'cmp/dmg/loss-loc-dir-ds' must be provided at minimum)" + assert 'uid' not in dv_tag_meta, "uid found in DV input meta-tags. Duplicate cmp/dmg/loss-loc-dir-ds is not supported via uid; Condense duplicate cmp/dmg/loss-loc-dir-ds" DV_time = dvs.loc[:, dvs.columns.str.upper().str.startswith("TIME")] DV_cost = dvs.loc[:, dvs.columns.str.upper().str.startswith("COST")] diff --git a/src/atc138/red_tag.py b/src/atc138/red_tag.py index 18603f5..7a87644 100644 --- a/src/atc138/red_tag.py +++ b/src/atc138/red_tag.py @@ -54,7 +54,9 @@ def simulate_tagging(damage, comps, sc_ids, sc_thresholds, red_tag_options ): num_comps = np.array(comps['story'][s]['qty_dir_' + str(direc)]) # For each structural system - structural_systems = np.unique(np.array(damage['comp_ds_table']['structural_system'] + damage['comp_ds_table']['structural_system_alt'])) + structural_systems = np.unique(np.concatenate( + (np.array(damage['comp_ds_table']['structural_system']), np.array(damage['comp_ds_table']['structural_system_alt'])) + )) structural_systems = np.delete(structural_systems, 0) # do not include components not assigned to a structural system sys_tag = np.zeros([num_reals, len(structural_systems)]).astype(bool) @@ -69,30 +71,43 @@ def simulate_tagging(damage, comps, sc_ids, sc_thresholds, red_tag_options ): # Check damage among each series within this structural system series = np.unique(np.array(damage['comp_ds_table']['structural_series_id'])[ss_filt_ds]) - ser_dmg = np.zeros([num_reals,len(series)]) - ser_qty = np.zeros([1,len(series)]) + + # Allocate ONCE (outside the ser loop) + ser_dmg = np.zeros((num_reals, len(series))) # [num_reals x num_series] + ser_qty = np.zeros(len(series)) # [num_series] + for ser in range(len(series)): - ser_filt_ds = np.array(damage['comp_ds_table']['structural_series_id']) == series[ser] - ser_filt_comp = np.array(comps['comp_table']['structural_series_id']) == series[ser] - - # Total damage within this series and system - ser_dmg[:,ser] = np.sum(sc_dmg[:,ser_filt_ds & ss_filt_ds], axis=1) - - # Total number of components within this series and system - ser_qty[:,ser] = np.sum(num_comps[ser_filt_comp & ss_filt_comp]) + ser_filt_ds = np.array(damage['comp_ds_table']['structural_series_id']) == series[ser] + ser_filt_comp = np.array(comps['comp_table']['structural_series_id']) == series[ser] + + # Total damage within this series and system (per realization) + ser_dmg[:, ser] = np.sum(sc_dmg[:, (ser_filt_ds & ss_filt_ds)], axis=1) + + # Total number of components within this series and system (constant across realizations) + ser_qty[ser] = np.sum(num_comps[ser_filt_comp & ss_filt_comp]) # Check if this system is causing a red tag if structural_systems[sys] == 12 and bool(red_tag_options.get('tag_coupling_beams_over_height', False)): # HARED CODED CHECK FOR COUPLING BEAMS # just do counts for now instead of adding to red tag per story check coupling_beam_dmg[:, direc-1] = coupling_beam_dmg[:,direc-1] + np.nanmax(ser_dmg, axis = 1) - coupling_beam_qty[0, direc-1] = coupling_beam_qty[0,direc-1] + np.nanmax(ser_qty, axis = 1) + coupling_beam_qty[0, direc-1] = coupling_beam_qty[0, direc-1] + np.nanmax(ser_qty) red_tag_impact_cb_sc = np.fmax(red_tag_impact_cb_sc, ss_filt_ds.reshape(1,-1) * sc_filt.reshape(1,-1) * (sc_dmg>0)) else: - sys_dmg = np.nanmax(ser_dmg, axis = 1) - sys_qty = np.nanmax(ser_qty, axis = 1) - sys_ratio = sys_dmg / sys_qty - sys_tag[:,sys] = sys_ratio > sc_thresholds[sc] + empty_bin = (ser_dmg.size == 0 or ser_dmg.shape[1] == 0 or ser_qty.size == 0) + + if empty_bin: + sys_ratio = np.zeros((num_reals,), dtype=float) # implies "not tagged" for this bin + else: + sys_dmg = np.nanmax(ser_dmg, axis=1) # (num_reals,) + sys_qty = float(np.nanmax(ser_qty)) # scalar + + # avoid division by 0 / NaN issues + sys_ratio = np.zeros((num_reals,), dtype=float) + if np.isfinite(sys_qty) and sys_qty > 0: + ok = np.isfinite(sys_dmg) + sys_ratio[ok] = sys_dmg[ok] / sys_qty + sys_tag[:, sys] = sys_ratio > sc_thresholds[sc] '''Calculate the impact that each component has on red tag (boolean, 1 = affects red tag, 0 = does not affect) @@ -160,7 +175,8 @@ def simulate_tagging(damage, comps, sc_ids, sc_thresholds, red_tag_options ): # Account for global red tag cases - replace_case = np.logical_not(np.isnan(np.array(simulated_replacement_time))) + sim_rt = np.array(simulated_replacement_time, dtype=float) + replace_case = ~np.isnan(sim_rt) red_tag[replace_case] = 1 return red_tag, red_tag_impact, inspection_tag diff --git a/src/atc138/repair_schedule/other_repair_schedule_functions.py b/src/atc138/repair_schedule/other_repair_schedule_functions.py index 4a1aa41..a09d99c 100644 --- a/src/atc138/repair_schedule/other_repair_schedule_functions.py +++ b/src/atc138/repair_schedule/other_repair_schedule_functions.py @@ -813,7 +813,8 @@ def fn_format_gantt_chart_data( damage, systems, simulated_replacement_time): comps = np.unique(damage['comp_ds_table']['comp_id']) # Determine replacement cases - replace_cases = np.logical_not(np.isnan(simulated_replacement_time)) + sim_rt = np.array(simulated_replacement_time, dtype=float) + replace_cases = ~np.isnan(sim_rt) ## Reformat repair schedule data into various breakdowns # Per component