def test_invalid_instantiation(self): # x_series with pytest.raises(ValueError): invalid_arg_combos = [[args[0]] for args in self._valid_inputs] invalid_arg_combos[0] = [['x', 'y', 'z']] for invalid_args in list(itertools.product(*invalid_arg_combos)): SSV.create_vis(*invalid_args) # x_series_unit with pytest.raises(TypeError): invalid_arg_combos = [[args[0]] for args in self._valid_inputs] invalid_arg_combos[1] = [1, True] for invalid_args in itertools.product(*invalid_arg_combos): SSV.create_vis(*invalid_args) # svg_path with pytest.raises(FileNotFoundError): invalid_arg_combos = [[args[0]] for args in self._valid_inputs] invalid_arg_combos[2] = [''] for invalid_args in itertools.product(*invalid_arg_combos): SSV.create_vis(*invalid_args) # title with pytest.raises(TypeError): invalid_arg_combos = [[args[0]] for args in self._valid_inputs] invalid_arg_combos[3] = [1, True] for invalid_args in itertools.product(*invalid_arg_combos): SSV.create_vis(*invalid_args) # font_size with pytest.raises(TypeError): invalid_arg_combos = [[args[0]] for args in self._valid_inputs] invalid_arg_combos[4] = ['x'] for invalid_args in itertools.product(*invalid_arg_combos): SSV.create_vis(*invalid_args)
def run(): # Generate fake cutset data cutset_data = [['%LDHR','ACB-A-F','ACB-B-F','RCIC-F','HPCI-F'], ['%ATWS','SLC-F','RHR-F'], ['%LOSP','EDG-A-F','EDG-B-F','RCIC-F','HPCI-F'], ['%LDHR','DCB-A-F','DCB-B-F','LPCI-A-F','LPCI-B-F']] be_data = {'%LDHR':['Loss of Decay Heat Removal', 1.0E-2], '%LOSP':['Loss of Offsite Power', 1.0E-3], '%ATWS':['Anticipated Transient Without SCRAM', 1.0E-2], 'SLC-F':['Failure of Standby Liquid Coolant',1.0E-3], 'ACB-A-F':['Failure of AC Bus A',3.0E-3], 'ACB-B-F':['Failure of AC Bus B',3.0E-3], 'XFMR-F':['Failure of Main Transformer',2.0E-4], 'EDG-A-F':['Failure of Emergency Diesel Generator A',1.0E-4], 'EDG-B-F':['Failure of Emergency Diesel Generator B',1.0E-4], 'DCB-A-F':['Failure of DC Bus A',2.0E-2], 'DCB-B-F':['Failure of DC Bus B',2.0E-2], 'RHR-F':['Failure of Residual Heat Removal',1.0E-4], 'RCIC-F':['Failure of Reactor Core Isolation Cooling',4.0E-3], 'LPCI-A-F':['Failure of Low Pressure Coolant Injection A',5.0E-2], 'LPCI-B-F':['Failure of Low Pressure Coolant Injection B',5.0E-2], 'HPCI-F':['Failure of High Pressure Cooland Injection',1.0E-3]} cutset_freqs = [np.prod([be_data[be][1] for be in cutset]) for cutset in cutset_data] be_list = be_data.keys() be_map = {be:[True if be in cutset else False for cutset in cutset_data] for be in be_list} report = [[[i] + be_data[i] for i in cutset] for cutset in cutset_data] # Initiate and hook up SSV model ssv_model = SSV.create_vis(cutset_freqs, 'per year', os.path.join('examples', 'example_4', 'example_4.svg'), title="Cutset Visualization", font_size=8) # Add tablular data ssv_model.add_element('table', 'cutset-report', tabular_data=report, headers=['Basic Event', 'Description', 'Probability']) # Wire up svg elements svg_id = '' for eq in be_list: if "%" in eq: svg_id = eq[1:] else: svg_id = eq[:-2] el = ssv_model.add_element('cell', svg_id.lower()) el.add_condition('logical', data=be_map[eq], true_color='#F44336', false_color='#FFFFFF') ssv_model.save_visualization(os.path.join('examples', 'example_4', 'example_4')) return True
def run(): # Let's use the freely available CFAST code from NIST to generate data # http://www.nist.gov/el/fire_research/cfast.cfm # This file is from the sample problem provided with the code data_f = os.path.join('examples', 'example_2', 'standard_n.csv') data = pd.read_csv(data_f, skiprows=[2], header=[0, 1]) data.columns = [', '.join(col) if i > 0 else col[0] for i, col in enumerate(data.columns)] # Initiate and hook up SSV model ssv_model = SSV.create_vis(data['Time'], 'seconds', os.path.join('examples', 'example_2', 'example_2.svg'), title="CFAST Example", font_size=8) gas_color_scale = ['#fd8d3c','#fc4e2a','#e31a1c','#bd0026','#800026'] gas_color_levels = np.linspace(min(data['Upper Layer Temperature, Comp 2']), max(data['Upper Layer Temperature, Comp 3']), 5) # Add nodes for i in range(1,4): data['Layer Height Upper, Comp %d' % i] = 3.0 node = ssv_model.add_element('cell', 'node-%d' % i, 'Node %d' % i, cell_report_id='node-%d-report' % i) node.add_condition('zonaly', description='Zone Heights', unit='m', data=data[['Layer Height, Comp %d' % i, 'Layer Height Upper, Comp %d' % i]], data_dynamic=data[ ['Lower Layer Temperature, Comp %d' % i, 'Upper Layer Temperature, Comp %d' % i]], color_scale=gas_color_scale, color_levels=gas_color_levels, max_height=3, description_dynamic='Zone Temperatures', unit_dynamic='C', min_height=0, section_label='Zone') node.add_condition('info', data=data[['Pressure, Comp %d' % i]], description='Pressure', unit='Pa') if i == 1: node.add_condition('info', data=data[['HRR, bunsen']], description='HRR', unit='W') elif i == 2: node.add_condition('info', data=data[['HRR, Wood_Wall']], description='HRR', unit='W') # Add fires fire_1 = ssv_model.add_element('toggle', 'fire-1', 'Fire 1') fire_1.add_condition('showhide', data=data['HRR, bunsen']) fire_2 = ssv_model.add_element('toggle', 'fire-2', 'Fire 2') fire_2.add_condition('showhide', data=data['HRR, Wood_Wall']) # Show gas color scale ssv_model.add_element('legend', 'color-scale', 'Gas Temperature (C)', color_scale=gas_color_scale, color_levels=gas_color_levels) ssv_model.save_visualization(os.path.join('examples', 'example_2', 'example_2')) return True
def run(): # Let's build assume our core flux distribution takes the form of: # Φ(r,z) = J0(2.405r/R) cos(πz/H) # # Where: # r is the radius from the center of the core # z is the height from the bottom of the core # J0 is the bessel function of the first kind, zero order # R is the total radius of the core # H is the total height of the core # # Let's also assume the temperature distribution in the core matches this profile. # Build profile function for entire core def get_temp(rv, zv, peak_temp): # Core height and radius, in m core_h = 3.7 core_r = 1.7 # Assume there is a ratio of core reflector dimensions to active core dimensions refl_ratio = 0.5 temp = peak_temp * ((special.jv(0, 2.405 * rv / core_r * refl_ratio) * np.cos(np.pi * zv / core_h / 2 * refl_ratio))) # Reflect r and z axis to build full core # Start with r axis temp_reflect = np.fliplr(temp[:, :, :]) temp = np.append(temp_reflect, temp, 1) # Flip z axis temp_reflect = np.flipud(temp[:, :, :]) temp = np.append(temp_reflect, temp, 0) return temp # Raise peak core temperature from 500 K to 800 K over 24 hours # Nodalize core into 5 radial regions x 30 axial regions (15 above center and 15 below center) # Take data point every 15 minutes r = np.linspace(0, 1.7, 5) z = np.linspace(0, 3.7 / 2, 15) t = np.linspace(0, 24, 96) rv, zv, tz = np.meshgrid(r, z, t, indexing='ij') temp_initial = 500 temp_final = 800 peak_temp = temp_initial + (temp_final - temp_initial) * (t / t[-1]) core_temp = get_temp(rv, zv, peak_temp) # Transpose data core_temp = core_temp.transpose((2, 1, 0)) # Initiate and hook up SSV model ssv_model = SSV.create_vis(t, 'hours', os.path.join('examples', 'example_3', 'example_3.svg'), title="Core Heatmap Example", font_size=8) core_color_scale = ['#ffffb2', '#fecc5c', '#fd8d3c', '#f03b20', '#bd0026'] core_color_levels = np.linspace(300, 800, 5) node = ssv_model.add_element('heatmap', 'core', 'Core') node.add_condition('rect', description='Core Heatmap', unit='K', color_data=core_temp, color_scale=core_color_scale, color_levels=core_color_levels) # Use outer vertical rows of heatmap to represent temperature in vessel wall walls = ssv_model.add_element('line', ['wall-left', 'wall-right'], 'Vessel Wall') walls.add_condition('equaly', description='Vessel Wall Temp', unit='K', color_data=core_temp[:, :, 0], color_scale=core_color_scale, color_levels=core_color_levels) # Track average and max core temperature core_temp_avg = core_temp.mean(axis=2).mean(axis=1) core_temp_max = core_temp.max(axis=2).max(axis=1) report = ssv_model.add_element('report', 'report-1', 'Core Metrics') report.add_condition('info', description='Avg Temp', unit='F', data=core_temp_avg) report.add_condition('info', description='Max Temp', unit='F', data=core_temp_max) core_temp_legend = ssv_model.add_element('legend', 'core-color-scale', 'Core Temperature (K)') core_temp_legend.add_condition("colorscale", core_color_scale, core_color_levels) ssv_model.save_visualization( os.path.join('examples', 'example_3', 'example_3')) return True
def test_element_id_collision(self): with pytest.raises(ValueError): vis = SSV.create_vis(*[i[0] for i in self._valid_inputs]) vis.add_element('cell', 'id_1') vis.add_element('heatmap', 'id_1')
def test_bad_svg_(self): with pytest.raises(ET.ParseError): SSV.create_vis([0], 'Title', os.path.join('tests', 'data', 'bad_svg.svg'))
def test_valid_instantiation(self): for args in list(itertools.product(*self._valid_inputs)): SSV.create_vis(*args)
def run(): # Let's build assume our core flux distribution takes the form of: # Φ(r,z) = J0(2.405r/R) cos(πz/H) # # Where: # r is the radius from the center of the core # z is the height from the bottom of the core # J0 is the bessel function of the first kind, zero order # R is the total radius of the core # H is the total height of the core # # Let's also assume the temperature distribution in the core matches this profile. # Build profile function for entire core def get_temp(rv, zv, peak_temp): # Core height and radius, in m core_h = 3.7 core_r = 1.7 # Assume there is a ratio of core reflector dimensions to active core dimensions refl_ratio = 0.5 temp = peak_temp * ((special.jv(0, 2.405 * rv / core_r * refl_ratio) * np.cos(np.pi * zv / core_h / 2 * refl_ratio))) # Reflect r and z axis to build full core # Start with r axis temp_reflect = np.fliplr(temp[:,:,:]) temp = np.append(temp_reflect, temp, 1) # Flip z axis temp_reflect = np.flipud(temp[:,:,:]) temp = np.append(temp_reflect, temp, 0) return temp # Raise peak core temperature from 500 K to 800 K over 24 hours # Nodalize core into 5 radial regions x 30 axial regions (15 above center and 15 below center) # Take data point every 15 minutes r = np.linspace(0, 1.7, 5) z = np.linspace(0, 3.7/2, 15) t = np.linspace(0,24,96) rv,zv,tz = np.meshgrid(r,z,t,indexing='ij') temp_initial = 500 temp_final = 800 peak_temp = temp_initial + (temp_final - temp_initial) * (t / t[-1]) core_temp = get_temp(rv, zv, peak_temp) # Transpose data core_temp = core_temp.transpose((2,1,0)) # Initiate and hook up SSV model ssv_model = SSV.create_vis(t, 'hours', os.path.join('examples', 'example_3', 'example_3.svg'), title="Core Heatmap Example", font_size=8) core_color_scale = ['#ffffb2','#fecc5c','#fd8d3c','#f03b20','#bd0026'] core_color_levels = np.linspace(300,800,5) node = ssv_model.add_element('heatmap', 'core', 'Core') node.add_condition('rect', description='Core Heatmap', unit='K', data=core_temp, color_scale=core_color_scale, color_levels=core_color_levels) # Use outer vertical rows of heatmap to represent temperature in vessel wall walls = ssv_model.add_element('line', ['wall-left', 'wall-right'], 'Vessel Wall') walls.add_condition('equaly', description='Vessel Wall Temp', unit='K', data=core_temp[:,:,0], color_scale=core_color_scale, color_levels=core_color_levels) # Track average and max core temperature core_temp_avg = core_temp.mean(axis=2).mean(axis=1) core_temp_max = core_temp.max(axis=2).max(axis=1) report = ssv_model.add_element('report', 'report-1', 'Core Metrics') report.add_condition('info', description='Avg Temp', unit='F', data=core_temp_avg) report.add_condition('info', description='Max Temp', unit='F', data=core_temp_max) ssv_model.add_element('legend', 'core-color-scale', 'Core Temperature (K)', color_scale=core_color_scale, color_levels=core_color_levels) ssv_model.save_visualization(os.path.join('examples', 'example_3', 'example_3')) return True
def run(): # Let's use the freely available CFAST code from NIST to generate data # http://www.nist.gov/el/fire_research/cfast.cfm # This file is from the sample problem provided with the code data_f = os.path.join('examples', 'example_2', 'standard_n.csv') data = pd.read_csv(data_f, skiprows=[2], header=[0, 1]) data.columns = [ ', '.join(col) if i > 0 else col[0] for i, col in enumerate(data.columns) ] # Initiate and hook up SSV model ssv_model = SSV.create_vis(data['Time'], 'seconds', os.path.join('examples', 'example_2', 'example_2.svg'), title="CFAST Example", font_size=8) gas_color_scale = ['#fd8d3c', '#fc4e2a', '#e31a1c', '#bd0026', '#800026'] gas_color_levels = np.linspace( min(data['Upper Layer Temperature, Comp 2']), max(data['Upper Layer Temperature, Comp 3']), 5) # Add nodes for i in range(1, 4): data['Layer Height Upper, Comp %d' % i] = 3.0 node = ssv_model.add_element('cell', 'node-%d' % i, 'Node %d' % i, report_id='node-%d-report' % i) node.add_condition('zonaly', description='Zone Heights', unit='m', level_data=data[[ 'Layer Height, Comp %d' % i, 'Layer Height Upper, Comp %d' % i ]], color_data=data[[ 'Lower Layer Temperature, Comp %d' % i, 'Upper Layer Temperature, Comp %d' % i ]], color_scale=gas_color_scale, color_levels=gas_color_levels, max_height=3, color_data_description='Zone Temperatures', color_data_unit='C', min_height=0, section_label='Zone') node.add_condition('info', data=data[['Pressure, Comp %d' % i]], description='Pressure', unit='Pa') if i == 1: node.add_condition('info', data=data[['HRR, bunsen']], description='HRR', unit='W') elif i == 2: node.add_condition('info', data=data[['HRR, Wood_Wall']], description='HRR', unit='W') # Add fires fire_1 = ssv_model.add_element('toggle', 'fire-1', 'Fire 1') fire_1.add_condition('showhide', data=data['HRR, bunsen']) fire_2 = ssv_model.add_element('toggle', 'fire-2', 'Fire 2') fire_2.add_condition('showhide', data=data['HRR, Wood_Wall']) # Show gas color scale gas_temp_legend = ssv_model.add_element('legend', 'color-scale', 'Gas Temperature (C)') gas_temp_legend.add_condition("colorscale", gas_color_scale, gas_color_levels) ssv_model.save_visualization( os.path.join('examples', 'example_2', 'example_2')) return True
def run(): # Load steam tables and build interpolation functions for several features sat_tables = pd.read_excel(os.path.join('examples', 'example_1', 'steam_tables.xls')) f_press = interp1d(sat_tables['vg'], sat_tables['Mpa']) f_temp = interp1d(sat_tables['vg'], sat_tables['deg C']) f_wtr_sp_vol = interp1d(sat_tables['vg'], sat_tables['vf']) f_wtr_enthalpy = interp1d(sat_tables['vg'], sat_tables['hf']) f_vap_enthalpy = interp1d(sat_tables['vg'], sat_tables['hg']) f_lat_heat = interp1d(sat_tables['vg'], sat_tables['hfg']) # Function to get properties of saturated water+vapor given vapor density def get_props_from_vap_density(density): props_out = {} sp_vol = 1 / density props_out['press'] = float(f_press(sp_vol)) props_out['temp'] = float(f_temp(sp_vol) + 273.15) props_out['wtr_density'] = float(1 / f_wtr_sp_vol(sp_vol)) props_out['wtr_enthalpy'] = float(f_wtr_enthalpy(sp_vol)) props_out['vap_enthalpy'] = float(f_vap_enthalpy(sp_vol)) props_out['lat_heat'] = float(f_lat_heat(sp_vol)) return props_out def th_run(initial_wtr_mass, initial_wtr_level, downstream_height, steam_flow, discharge_press, time_step, end_time): """ Simple Thermal-Hydraulic analysis code for water between 2 compartments. Assume downstream is water/steam compartment always at saturated conditions. Assume upstream is saturated steam environment that discharges to downstream. Heat loss to environment and Condensation is assumed to be negligible. Inputs: initial_wtr_mass = initial downstream water mass in kg. initial_wtr_level = initial downstream water level in m. Note that the downstream volume is assumed to increase linearly with height based on this input. downstream_height = total height of downstream compartment (m) steam_flow = 4 x n array of n occurences of upstream to downstream steam flow. Row 1 is start time of flow in S Row 2 is end time of flow in S Row 3 is specific enthalpy of flow in kJ/kg Row 4 is total mass of flow in kg discharge_press = pressure control point of downstream compartment in MPa. Downstream compartment is modeled as instantly opening and relieving conditions down to atmospheric over a single time step. time_step = time step of analysis in S. end_time = analysis end time is S. Returns downstream pressure, temperature, water level, and logical states of relief valve and steam discharge as a function of time. """ # Assign initial conditions vap_density = 0.59 wtr_mass = initial_wtr_mass wtr_lvl = initial_wtr_level wtr_lvl_vol_slope = wtr_lvl / wtr_mass # Determine downstream conditions using steam tables - assume saturated conditions props = get_props_from_vap_density(vap_density) press = props['press'] temp = props['temp'] wtr_enthalpy = props['wtr_enthalpy'] wtr_density = props['wtr_density'] vap_enthalpy = props['vap_enthalpy'] lat_heat = props['lat_heat'] wtr_vol = wtr_mass / wtr_density total_vol = wtr_vol * downstream_height / wtr_lvl vap_vol = total_vol - wtr_vol vap_mass = vap_density * vap_vol lvl_vol_slope = wtr_lvl / wtr_vol # Cast steam_flow as numpy array steam_flow = np.array(steam_flow) # Flag for relief valve rv_flag = False # Record conditons at t=0 conditions_out = {'press':[press], 'temp':[temp], 'wtr_lvl':[wtr_lvl], 'rv':[rv_flag], 'disch':[False], 'time':[0]} # Run through time span, calculating conditions at each step for t in np.arange(1, end_time+time_step, time_step): # Check if current time span is within or includes any steam_flow entry # and calculate integrated enthalpy addition time_mask = ((steam_flow[0] >= t) & (steam_flow[0] < t + time_step)) | ((steam_flow[0] < t) & (steam_flow[1] > t)) start_times = steam_flow[0][time_mask] start_times[start_times < t] = t end_times = steam_flow[1][time_mask] end_times[end_times > t + time_step] = t + time_step time_deltas = end_times - start_times upstream_enthalpy = steam_flow[2][time_mask] flow_mass = steam_flow[3][time_mask] * time_deltas # Calculate vaporized water mass excess_enthalpy = (upstream_enthalpy - wtr_enthalpy) * flow_mass vaporized_wtr_mass = (excess_enthalpy / lat_heat).sum() # Update water mass and vapor mass and density wtr_mass += flow_mass.sum() - vaporized_wtr_mass vap_mass += vaporized_wtr_mass vap_density = vap_mass / (total_vol * (1 - wtr_vol/total_vol)) # If we are at relief pressure reset to saturated conditions and calculate # change in water mass if press > discharge_press: vap_density = 0.59 props = get_props_from_vap_density(vap_density) wtr_enthalpy_new = props['wtr_enthalpy'] lat_heat = props['lat_heat'] wtr_mass -= (wtr_enthalpy - wtr_enthalpy_new) * wtr_mass / lat_heat rv_flag = True else: rv_flag = False # Calculate new properties # Assume water density has negligible change between time steps props = get_props_from_vap_density(vap_density) press = props['press'] temp = props['temp'] wtr_density = props['wtr_density'] wtr_enthalpy = props['wtr_enthalpy'] lat_heat = props['lat_heat'] wtr_lvl = lvl_vol_slope * wtr_mass / wtr_density vap_mass = vap_density * (total_vol * (1 - wtr_vol/total_vol)) # Record new properties conditions_out['time'].append(t) conditions_out['press'].append(press) conditions_out['temp'].append(temp) conditions_out['wtr_lvl'].append(wtr_lvl) conditions_out['disch'].append(flow_mass.sum()) conditions_out['rv'].append(rv_flag) return conditions_out # Run the code initial_wtr_mass = 1000000 # kg ~ 2200000 lbm initial_wtr_level = 5 # m ~ 16.405 ft downstream_height = 10 # m ~ 32.81 ft steam_flow = [[2,15,45,65], # S - Steam discharge start [10,40,60,90], # S - Steam discharge end [2650,2650,2650,2650], # kJ/kg - steam at ~2000 psi or 14 MPa [50,100,150,150]] # kg/s - flowrate discharge_press = 0.79 # Mpa ~ 100 psig time_step = 1 # Seconds end_time = 100 # Seconds sim_data = th_run(initial_wtr_mass, initial_wtr_level, downstream_height, steam_flow, discharge_press, time_step, end_time) # Initiate and hook up SSV model ssv_model = SSV.create_vis(sim_data['time'], 'seconds', os.path.join('examples', 'example_1', 'example_1.svg'), title="Steam Quench Tank Simulation", font_size=8) water_color_scale = ['#0570b0', '#3690c0', '#74a9cf'] water_color_levels = np.linspace(min(sim_data['temp']), max(sim_data['temp']), len(water_color_scale)) gas_color_scale = ['#fdd49e','#fdbb84','#fc8d59'] gas_color_levels = np.linspace(min(sim_data['temp']), max(sim_data['temp']), len(gas_color_scale)) # Wire up svg elements tank = ssv_model.add_element('cell', 'tank-1', 'Quench Tank', cell_report_id='tank-1-report') tank.add_condition('background', description='Vapor Temp', unit='K', data=sim_data['temp'], color_scale=gas_color_scale, color_levels=gas_color_levels) tank.add_condition('dynamiclevel', description='Water Level', unit='m', data=sim_data['wtr_lvl'], data_dynamic=sim_data['temp'], color_scale=water_color_scale, color_levels=water_color_levels, max_height=10, description_dynamic='Water Temp', unit_dynamic='K', overlay='bubbles', min_height=0) tank.add_condition('info', data=sim_data['press'], description='Press', unit='MPa') tank.add_popover("sparkline", sim_data['wtr_lvl'], label='Tank #1 Wtr Lvl') relief_valve = ssv_model.add_element('cell', 'relief-valve', 'Relief Valve') relief_valve.add_condition('logical', data=sim_data['rv'], true_color='#4CAF50', false_color='#F44336') steam_discharge = ssv_model.add_element('cell', 'steam-discharge', 'Steam Discharge', cell_report_id='disch-report') steam_discharge.add_condition('logical', description='Flowrate', data=sim_data['disch'], true_color='#4CAF50', false_color='#F44336', unit='kg/s') steam_toggle = ssv_model.add_element('toggle', 'steam-toggle', 'Steam Toggle') steam_toggle.add_condition('showhide', data=sim_data['disch']) relief_toggle = ssv_model.add_element('toggle', 'relief-toggle', 'Relief Toggle') relief_toggle.add_condition('showhide', data=sim_data['rv']) ssv_model.add_element('legend', 'color-scale-water', 'Water Temperature (F)', color_scale=water_color_scale, color_levels=water_color_levels) x = ssv_model.add_element('legend', 'color-scale-gas', 'Gas Temperature (F)', color_scale=gas_color_scale, color_levels=gas_color_levels) ssv_model.save_visualization(os.path.join('examples', 'example_1', 'example_1')) return True
def run(): # Load steam tables and build interpolation functions for several features sat_tables = pd.read_excel( os.path.join('examples', 'example_1', 'steam_tables.xls')) f_press = interp1d(sat_tables['vg'], sat_tables['Mpa']) f_temp = interp1d(sat_tables['vg'], sat_tables['deg C']) f_wtr_sp_vol = interp1d(sat_tables['vg'], sat_tables['vf']) f_wtr_enthalpy = interp1d(sat_tables['vg'], sat_tables['hf']) f_vap_enthalpy = interp1d(sat_tables['vg'], sat_tables['hg']) f_lat_heat = interp1d(sat_tables['vg'], sat_tables['hfg']) # Function to get properties of saturated water+vapor given vapor density def get_props_from_vap_density(density): props_out = {} sp_vol = 1 / density props_out['press'] = float(f_press(sp_vol)) props_out['temp'] = float(f_temp(sp_vol) + 273.15) props_out['wtr_density'] = float(1 / f_wtr_sp_vol(sp_vol)) props_out['wtr_enthalpy'] = float(f_wtr_enthalpy(sp_vol)) props_out['vap_enthalpy'] = float(f_vap_enthalpy(sp_vol)) props_out['lat_heat'] = float(f_lat_heat(sp_vol)) return props_out def th_run(initial_wtr_mass, initial_wtr_level, downstream_height, steam_flow, discharge_press, time_step, end_time): """ Simple Thermal-Hydraulic analysis code for water between 2 compartments. Assume downstream is water/steam compartment always at saturated conditions. Assume upstream is saturated steam environment that discharges to downstream. Heat loss to environment and Condensation is assumed to be negligible. Inputs: initial_wtr_mass = initial downstream water mass in kg. initial_wtr_level = initial downstream water level in m. Note that the downstream volume is assumed to increase linearly with height based on this input. downstream_height = total height of downstream compartment (m) steam_flow = 4 x n array of n occurences of upstream to downstream steam flow. Row 1 is start time of flow in S Row 2 is end time of flow in S Row 3 is specific enthalpy of flow in kJ/kg Row 4 is total mass of flow in kg discharge_press = pressure control point of downstream compartment in MPa. Downstream compartment is modeled as instantly opening and relieving conditions down to atmospheric over a single time step. time_step = time step of analysis in S. end_time = analysis end time is S. Returns downstream pressure, temperature, water level, and logical states of relief valve and steam discharge as a function of time. """ # Assign initial conditions vap_density = 0.59 wtr_mass = initial_wtr_mass wtr_lvl = initial_wtr_level wtr_lvl_vol_slope = wtr_lvl / wtr_mass # Determine downstream conditions using steam tables - assume saturated conditions props = get_props_from_vap_density(vap_density) press = props['press'] temp = props['temp'] wtr_enthalpy = props['wtr_enthalpy'] wtr_density = props['wtr_density'] vap_enthalpy = props['vap_enthalpy'] lat_heat = props['lat_heat'] wtr_vol = wtr_mass / wtr_density total_vol = wtr_vol * downstream_height / wtr_lvl vap_vol = total_vol - wtr_vol vap_mass = vap_density * vap_vol lvl_vol_slope = wtr_lvl / wtr_vol # Cast steam_flow as numpy array steam_flow = np.array(steam_flow) # Flag for relief valve rv_flag = False # Record conditons at t=0 conditions_out = { 'press': [press], 'temp': [temp], 'wtr_lvl': [wtr_lvl], 'rv': [rv_flag], 'disch': [False], 'time': [0] } # Run through time span, calculating conditions at each step for t in np.arange(1, end_time + time_step, time_step): # Check if current time span is within or includes any steam_flow entry # and calculate integrated enthalpy addition time_mask = ((steam_flow[0] >= t) & (steam_flow[0] < t + time_step)) | ( (steam_flow[0] < t) & (steam_flow[1] > t)) start_times = steam_flow[0][time_mask] start_times[start_times < t] = t end_times = steam_flow[1][time_mask] end_times[end_times > t + time_step] = t + time_step time_deltas = end_times - start_times upstream_enthalpy = steam_flow[2][time_mask] flow_mass = steam_flow[3][time_mask] * time_deltas # Calculate vaporized water mass excess_enthalpy = (upstream_enthalpy - wtr_enthalpy) * flow_mass vaporized_wtr_mass = (excess_enthalpy / lat_heat).sum() # Update water mass and vapor mass and density wtr_mass += flow_mass.sum() - vaporized_wtr_mass vap_mass += vaporized_wtr_mass vap_density = vap_mass / (total_vol * (1 - wtr_vol / total_vol)) # If we are at relief pressure reset to saturated conditions and calculate # change in water mass if press > discharge_press: vap_density = 0.59 props = get_props_from_vap_density(vap_density) wtr_enthalpy_new = props['wtr_enthalpy'] lat_heat = props['lat_heat'] wtr_mass -= (wtr_enthalpy - wtr_enthalpy_new) * wtr_mass / lat_heat rv_flag = True else: rv_flag = False # Calculate new properties # Assume water density has negligible change between time steps props = get_props_from_vap_density(vap_density) press = props['press'] temp = props['temp'] wtr_density = props['wtr_density'] wtr_enthalpy = props['wtr_enthalpy'] lat_heat = props['lat_heat'] wtr_lvl = lvl_vol_slope * wtr_mass / wtr_density vap_mass = vap_density * (total_vol * (1 - wtr_vol / total_vol)) # Record new properties conditions_out['time'].append(t) conditions_out['press'].append(press) conditions_out['temp'].append(temp) conditions_out['wtr_lvl'].append(wtr_lvl) conditions_out['disch'].append(flow_mass.sum()) conditions_out['rv'].append(rv_flag) return conditions_out # Run the code initial_wtr_mass = 1000000 # kg ~ 2200000 lbm initial_wtr_level = 5 # m ~ 16.405 ft downstream_height = 10 # m ~ 32.81 ft steam_flow = [ [2, 15, 45, 65], # S - Steam discharge start [10, 40, 60, 90], # S - Steam discharge end [2650, 2650, 2650, 2650], # kJ/kg - steam at ~2000 psi or 14 MPa [50, 100, 150, 150] ] # kg/s - flowrate discharge_press = 0.79 # Mpa ~ 100 psig time_step = 1 # Seconds end_time = 100 # Seconds sim_data = th_run(initial_wtr_mass, initial_wtr_level, downstream_height, steam_flow, discharge_press, time_step, end_time) # Initiate and hook up SSV model ssv_model = SSV.create_vis(sim_data['time'], 'seconds', os.path.join('examples', 'example_1', 'example_1.svg'), title="Steam Quench Tank Simulation", font_size=8) water_color_scale = ['#0570b0', '#3690c0', '#74a9cf'] water_color_levels = np.linspace(min(sim_data['temp']), max(sim_data['temp']), len(water_color_scale)) gas_color_scale = ['#fdd49e', '#fdbb84', '#fc8d59'] gas_color_levels = np.linspace(min(sim_data['temp']), max(sim_data['temp']), len(gas_color_scale)) # Wire up svg elements tank = ssv_model.add_element('cell', 'tank-1', 'Quench Tank', report_id='tank-1-report') tank.add_condition('background', description='Vapor Temp', unit='K', color_data=sim_data['temp'], color_scale=gas_color_scale, color_levels=gas_color_levels) tank.add_condition('dynamiclevel', description='Water Level', unit='m', level_data=sim_data['wtr_lvl'], color_data=sim_data['temp'], color_scale=water_color_scale, color_levels=water_color_levels, max_height=10, color_data_description='Water Temp', color_data_unit='K', overlay='bubbles', min_height=0) tank.add_condition('info', data=sim_data['press'], description='Press', unit='MPa') tank.add_popover("sparkline", sim_data['wtr_lvl'], label='Tank #1 Wtr Lvl') relief_valve = ssv_model.add_element('cell', 'relief-valve', 'Relief Valve') relief_valve.add_condition('logical', data=sim_data['rv'], true_color='#4CAF50', false_color='#F44336') steam_discharge = ssv_model.add_element('cell', 'steam-discharge', 'Steam Discharge', report_id='disch-report') steam_discharge.add_condition('logical', description='Flowrate', data=sim_data['disch'], true_color='#4CAF50', false_color='#F44336', unit='kg/s') steam_toggle = ssv_model.add_element('toggle', 'steam-toggle', 'Steam Toggle') steam_toggle.add_condition('showhide', data=sim_data['disch']) relief_toggle = ssv_model.add_element('toggle', 'relief-toggle', 'Relief Toggle') relief_toggle.add_condition('showhide', data=sim_data['rv']) water_temp_legend = ssv_model.add_element('legend', 'color-scale-water', 'Water Temperature (F)') water_temp_legend.add_condition("colorscale", water_color_scale, water_color_levels) gas_temp_legend = ssv_model.add_element('legend', 'color-scale-gas', 'Gas Temperature (F)') gas_temp_legend.add_condition("colorscale", gas_color_scale, gas_color_levels) ssv_model.save_visualization( os.path.join('examples', 'example_1', 'example_1')) return True