def test_run(): result = script.RESULT_FAIL pv_1 = pv_2 = None try: # initialize pv simulation pv_1 = pvsim.pvsim_init(ts, '1') ts.log('PV 1: %s' % pv_1.info()) pv_2 = pvsim.pvsim_init(ts, '2') ts.log('PV 2: %s' % pv_2.info()) result = script.RESULT_PASS except script.ScriptFail as e: reason = str(e) if reason: ts.log_error(reason) finally: if pv_1: pv_1.close() if pv_2: pv_2.close() return result
def test_run(): result = script.RESULT_FAIL daq = None try: # initialize data acquisition system ts.log('result_id = %s' % (ts.result_id())) ts.log('result_dir = %s' % (ts.result_dir())) grid = gridsim.gridsim_init(ts) pv = pvsim.pvsim_init(ts) daq = das.das_init(ts) pv.irradiance_set(ts.param_value('profile.irr_start')) # pv.profile_load(ts.param_value('profile.profile_name')) # pv.profile_start() pv.power_on() ts.log('Running capture 2') daq.data_capture_start() ts.sleep(8) daq.data_capture_stop() ds = daq.data_capture_dataset() ds.to_csv(ts.result_file('capture_2.csv')) result = script.RESULT_COMPLETE except script.ScriptFail, e: reason = str(e) if reason: ts.log_error(reason)
def test_run(): result = script.RESULT_FAIL pv = None try: # initialize pv simulation pv = pvsim.pvsim_init(ts) ts.log(pv.info()) pv.power_set(5000) #pv.irradiance_set() pv.power_on() proc = pv.profile_load('STPsIrradiance') #pv.profile_start() #pv.power_set(1000) ''' for i in range(2): ts.sleep(2) pv.power_set(1000+100*i) ts.log("%d" % i) ''' #pv.power_off() result = script.RESULT_PASS except script.ScriptFail, e: reason = str(e) if reason: ts.log_error(reason)
def test_run(): result = script.RESULT_PASS # initialize pv simulation pv = pvsim.pvsim_init(ts) pv.info() pv.irradiance_set(ts.param_value('profile.irr_start')) pv.power_on() return result
def test_run(): result = script.RESULT_FAIL pv = None try: # initialize pv simulation pv = pvsim.pvsim_init(ts) ts.log(pv.info()) result = script.RESULT_PASS except script.ScriptFail, e: reason = str(e) if reason: ts.log_error(reason)
def volt_var_mode_imbalanced_grid(imbalance_resp, vv_curves, vv_response_time): result = script.RESULT_FAIL daq = None v_nom = None p_rated = None grid = None pv = None eut = None chil = None result_summary = None dataset_filename = None try: #cat = ts.param_value('eut.cat') #cat2 = ts.param_value('eut.cat2') #sink_power = ts.param_value('eut.sink_power') p_rated = ts.param_value('eut.p_rated') #p_rated_prime = ts.param_value('eut.p_rated_prime') var_rated = ts.param_value('eut.var_rated') s_rated = ts.param_value('eut.s_rated') #absorb_enable = ts.param_value('eut.abs_enabled') # DC voltages v_in_nom = ts.param_value('eut.v_in_nom') #v_min_in = ts.param_value('eut.v_in_min') #v_max_in = ts.param_value('eut.v_in_max') # AC voltages v_nom = ts.param_value('eut.v_nom') v_min = ts.param_value('eut.v_low') v_max = ts.param_value('eut.v_high') p_min = ts.param_value('eut.p_min') p_min_prime = ts.param_value('eut.p_min_prime') phases = ts.param_value('eut.phases') pf_response_time = ts.param_value('vv.test_imbalanced_t_r') # Pass/fail accuracies pf_msa = ts.param_value('eut.pf_msa') # According to Table 3-Minimum requirements for manufacturers stated measured and calculated accuracy MSA_Q = 0.05 * s_rated MSA_P = 0.05 * s_rated MSA_V = 0.01 * v_nom imbalance_fix = ts.param_value('vv.imbalance_fix') """ A separate module has been create for the 1547.1 Standard """ lib_1547 = p1547.module_1547(ts=ts, aif='VV', imbalance_angle_fix=imbalance_fix) ts.log_debug('1547.1 Library configured for %s' % lib_1547.get_test_name()) # Get the rslt parameters for plot result_params = lib_1547.get_rslt_param_plot() ''' a) Connect the EUT according to the instructions and specifications provided by the manufacturer. ''' # initialize HIL environment, if necessary chil = hil.hil_init(ts) if chil is not None: chil.config() # grid simulator is initialized with test parameters and enabled grid = gridsim.gridsim_init( ts) # Turn on AC so the EUT can be initialized if grid is not None: grid.voltage(v_nom) # pv simulator is initialized with test parameters and enabled pv = pvsim.pvsim_init(ts) pv.power_set(p_rated) pv.power_on() # Turn on DC so the EUT can be initialized # DAS soft channels # TODO : add to library 1547 das_points = { 'sc': ('Q_TARGET', 'Q_TARGET_MIN', 'Q_TARGET_MAX', 'Q_MEAS', 'V_TARGET', 'V_MEAS', 'event') } # initialize data acquisition system daq = das.das_init(ts, sc_points=das_points['sc']) if daq is not None: daq.sc['Q_TARGET'] = 100 daq.sc['Q_TARGET_MIN'] = 100 daq.sc['Q_TARGET_MAX'] = 100 daq.sc['V_TARGET'] = v_nom daq.sc['event'] = 'None' ts.log('DAS device: %s' % daq.info()) ''' b) Set all voltage trip parameters to the widest range of adjustability. Disable all reactive/active power control functions. ''' ''' c) Set all AC test source parameters to the nominal operating voltage and frequency. ''' if grid is not None: grid.voltage(v_nom) # open result summary file result_summary_filename = 'result_summary.csv' result_summary = open(ts.result_file_path(result_summary_filename), 'a+') ts.result_file(result_summary_filename) result_summary.write(lib_1547.get_rslt_sum_col_name()) ''' d) Adjust the EUT's available active power to Prated. For an EUT with an input voltage range, set the input voltage to Vin_nom. ''' if pv is not None: pv.iv_curve_config(pmp=p_rated, vmp=v_in_nom) pv.irradiance_set(1000.) ''' h) Once steady state is reached, begin the adjustment of phase voltages. ''' """ Test start """ for imbalance_response in imbalance_resp: for vv_curve in vv_curves: ''' e) Set EUT volt-watt parameters to the values specified by Characteristic 1. All other function be turned off. ''' v_pairs = lib_1547.get_params(curve=vv_curve) # it is assumed the EUT is on eut = der.der_init(ts) if eut is not None: vv_curve_params = { 'v': [ v_pairs['V1'] * (100 / v_nom), v_pairs['V2'] * (100 / v_nom), v_pairs['V3'] * (100 / v_nom), v_pairs['V4'] * (100 / v_nom) ], 'q': [ v_pairs['Q1'] * (100 / var_rated), v_pairs['Q2'] * (100 / var_rated), v_pairs['Q3'] * (100 / var_rated), v_pairs['Q4'] * (100 / var_rated) ], 'DeptRef': 'Q_MAX_PCT' } ts.log_debug('Sending VV points: %s' % vv_curve_params) eut.volt_var(params={ 'Ena': True, 'curve': vv_curve_params }) # ASK @Jay about this loop. Necessary here ? Could go inside your driver... for i in range(10): if not eut.volt_var()['Ena']: ts.log_error( 'EUT VV Enable register not set to True. Trying again...' ) eut.volt_var(params={'Ena': True}) ts.sleep(1) else: break # TODO autonomous vref adjustment to be included # eut.autonomous_vref_adjustment(params={'Ena': False}) ''' f) Verify volt-var mode is reported as active and that the correct characteristic is reported. ''' if eut is not None: ts.log_debug('Initial EUT VV settings are %s' % eut.volt_var()) ts.log_debug('curve points: %s' % v_pairs) ''' g) Wait for steady state to be reached. Every time a parameter is stepped or ramped, measure and record the time domain current and voltage response for at least 4 times the maximum expected response time after the stimulus, and measure or derive, active power, apparent power, reactive power, and power factor. ''' """ Test start """ step = 'Step G' daq.sc['event'] = step daq.data_sample() ts.log('Wait for steady state to be reached') ts.sleep(4 * vv_response_time[vv_curve]) ts.log(imbalance_resp) ts.log('Starting imbalance test with VV mode at %s' % (imbalance_response)) if imbalance_fix == "Yes": dataset_filename = 'VV_IMB_%s_FIX' % (imbalance_response) else: dataset_filename = 'VV_IMB_%s' % (imbalance_response) ts.log('------------{}------------'.format(dataset_filename)) # Start the data acquisition systems daq.data_capture(True) ''' h) For multiphase units, step the AC test source voltage to Case A from Table 24. ''' if grid is not None: step = 'Step H' ts.log( 'Voltage step: setting Grid simulator to case A (IEEE 1547.1-Table 24)(%s)' % step) q_initial = lib_1547.get_initial(daq=daq, step=step) lib_1547.set_grid_asymmetric(grid=grid, case='case_a') q_v_analysis = lib_1547.criteria( daq=daq, tr=vv_response_time[vv_curve], step=step, initial_value=q_initial, curve=vv_curve) result_summary.write( lib_1547.write_rslt_sum(analysis=q_v_analysis, step=step, filename=dataset_filename)) ''' w) For multiphase units, step the AC test source voltage to VN. ''' if grid is not None: # STD_CHANGE : This step is not following order step = 'Step W' ts.log( 'Voltage step: setting Grid simulator voltage to %s (%s)' % (v_nom, step)) q_initial = lib_1547.get_initial(daq=daq, step=step) grid.voltage(v_nom) q_v_analysis = lib_1547.criteria( daq=daq, tr=vv_response_time[vv_curve], step=step, initial_value=q_initial, curve=vv_curve, target=v_nom) result_summary.write( lib_1547.write_rslt_sum(analysis=q_v_analysis, step=step, filename=dataset_filename)) """ i) For multiphase units, step the AC test source voltage to Case B from Table 24. """ if grid is not None: step = 'Step I' ts.log( 'Voltage step: setting Grid simulator to case B (IEEE 1547.1-Table 24)(%s)' % step) q_initial = lib_1547.get_initial(daq=daq, step=step) lib_1547.set_grid_asymmetric(grid=grid, case='case_b') q_v_analysis = lib_1547.criteria( daq=daq, tr=vv_response_time[vv_curve], step=step, initial_value=q_initial, curve=vv_curve) result_summary.write( lib_1547.write_rslt_sum(analysis=q_v_analysis, step=step, filename=dataset_filename)) """ j) For multiphase units, step the AC test source voltage to VN """ if grid is not None: ts.log( 'Voltage step: setting Grid simulator voltage to %s (%s)' % (v_nom, step)) step = 'Step J' q_initial = lib_1547.get_initial(daq=daq, step=step) grid.voltage(v_nom) q_v_analysis = lib_1547.criteria( daq=daq, tr=vv_response_time[vv_curve], step=step, initial_value=q_initial, curve=vv_curve, target=v_nom) result_summary.write( lib_1547.write_rslt_sum(analysis=q_v_analysis, step=step, filename=dataset_filename)) ts.log('Sampling complete') dataset_filename = dataset_filename + ".csv" daq.data_capture(False) ds = daq.data_capture_dataset() ts.log('Saving file: %s' % dataset_filename) ds.to_csv(ts.result_file_path(dataset_filename)) result_params['plot.title'] = dataset_filename.split('.csv')[0] ts.result_file(dataset_filename, params=result_params) result = script.RESULT_COMPLETE except script.ScriptFail, e: reason = str(e) if reason: ts.log_error(reason)
def volt_var_mode_imbalanced_grid(imbalance_resp, vv_curves, vv_response_time): result = script.RESULT_FAIL daq = None v_nom = None p_rated = None grid = None pv = None eut = None chil = None result_summary = None dataset_filename = None try: #cat = ts.param_value('eut.cat') #cat2 = ts.param_value('eut.cat2') #sink_power = ts.param_value('eut.sink_power') p_rated = ts.param_value('eut.p_rated') #p_rated_prime = ts.param_value('eut.p_rated_prime') var_rated = ts.param_value('eut.var_rated') s_rated = ts.param_value('eut.s_rated') #absorb_enable = ts.param_value('eut.abs_enabled') # DC voltages v_in_nom = ts.param_value('eut.v_in_nom') #v_min_in = ts.param_value('eut.v_in_min') #v_max_in = ts.param_value('eut.v_in_max') # AC voltages v_nom = ts.param_value('eut.v_nom') v_min = ts.param_value('eut.v_low') v_max = ts.param_value('eut.v_high') p_min = ts.param_value('eut.p_min') p_min_prime = ts.param_value('eut.p_min_prime') phases = ts.param_value('eut.phases') pf_response_time = ts.param_value('vv.test_1_t_r') imbalance_fix = ts.param_value('vv.imbalance_fix') """ A separate module has been create for the 1547.1 Standard """ ActiveFunction = p1547.ActiveFunction(ts=ts, script_name='Volt-Var', functions=[VV], criteria_mode=[True, True, True]) ActiveFunction.set_imbalance_config(imbalance_angle_fix=imbalance_fix) ts.log_debug('1547.1 Library configured for %s' % ActiveFunction.get_script_name()) # Get the rslt parameters for plot result_params = ActiveFunction.get_rslt_param_plot() ''' a) Connect the EUT according to the instructions and specifications provided by the manufacturer. ''' # initialize HIL environment, if necessary chil = hil.hil_init(ts) if chil is not None: chil.config() # grid simulator is initialized with test parameters and enabled grid = gridsim.gridsim_init(ts, support_interfaces={'hil': chil}) # Turn on AC so the EUT can be initialized if grid is not None: grid.voltage(v_nom) # pv simulator is initialized with test parameters and enabled pv = pvsim.pvsim_init(ts, support_interfaces={'hil': chil}) if pv is not None: pv.power_set(p_rated) pv.power_on() # Turn on DC so the EUT can be initialized # DAS soft channels das_points = ActiveFunction.get_sc_points() # initialize data acquisition system daq = das.das_init(ts, sc_points=das_points['sc'], support_interfaces={'hil': chil}) if daq is not None: daq.sc['Q_TARGET'] = 100 daq.sc['Q_TARGET_MIN'] = 100 daq.sc['Q_TARGET_MAX'] = 100 daq.sc['V_TARGET'] = v_nom daq.sc['event'] = 'None' ts.log('DAS device: %s' % daq.info()) ''' b) Set all voltage trip parameters to the widest range of adjustability. Disable all reactive/active power control functions. ''' # it is assumed the EUT is on eut = der.der_init(ts, support_interfaces={'hil': chil}) if eut is not None: eut.config() ts.log_debug('If not done already, set L/HVRT and trip parameters to the widest range of adjustability.') ''' c) Set all AC test source parameters to the nominal operating voltage and frequency. ''' if grid is not None: grid.voltage(v_nom) # open result summary file result_summary_filename = 'result_summary.csv' result_summary = open(ts.result_file_path(result_summary_filename), 'a+') ts.result_file(result_summary_filename) result_summary.write(ActiveFunction.get_rslt_sum_col_name()) ''' d) Adjust the EUT's available active power to Prated. For an EUT with an input voltage range, set the input voltage to Vin_nom. ''' if pv is not None: pv.iv_curve_config(pmp=p_rated, vmp=v_in_nom) pv.irradiance_set(1000.) ''' h) Once steady state is reached, begin the adjustment of phase voltages. ''' """ Test start """ ts.log_debug(f'imbalance_resp={imbalance_resp}') for imbalance_response in imbalance_resp: #Default 2 time responses cycles #ActiveFunction.reset_time_settings(tr=pf_response_time) for vv_curve in vv_curves: ''' e) Set EUT volt-watt parameters to the values specified by Characteristic 1. All other function be turned off. ''' ts.log('Starting test with characteristic curve %s' % (vv_curve)) ActiveFunction.reset_curve(vv_curve) ActiveFunction.reset_time_settings(tr=vv_response_time[vv_curve], number_tr=2) v_pairs = ActiveFunction.get_params(function=VV, curve=vv_curve) ts.log_debug('v_pairs:%s' % v_pairs) #Setting up step label ActiveFunction.set_step_label(starting_label='G') # it is assumed the EUT is on if eut is not None: vv_curve_params = { 'v': [(v_pairs['V1'] / v_nom) , (v_pairs['V2'] / v_nom) , (v_pairs['V3'] / v_nom), (v_pairs['V4'] / v_nom) ], 'var': [(v_pairs['Q1'] / s_rated), (v_pairs['Q2'] / s_rated) , (v_pairs['Q3'] / s_rated) , (v_pairs['Q4'] / s_rated)], 'vref': 1.0, 'RmpPtTms': vv_response_time[vv_curve]} ts.log_debug('Sending VV points: %s' % vv_curve_params) eut.volt_var(params={'Ena': True, 'curve': vv_curve_params}) ''' f) Verify volt-var mode is reported as active and that the correct characteristic is reported. ''' if eut is not None: ts.log_debug('Initial EUT VV settings are %s' % eut.volt_var()) ts.log_debug('curve points: %s' % v_pairs) if chil is not None: ts.log('Start simulation of CHIL') chil.start_simulation() ''' g) Wait for steady state to be reached. Every time a parameter is stepped or ramped, measure and record the time domain current and voltage response for at least 4 times the maximum expected response time after the stimulus, and measure or derive, active power, apparent power, reactive power, and power factor. ''' step = ActiveFunction.get_step_label() daq.sc['event'] = step daq.data_sample() ts.log('Wait for steady state to be reached') ts.sleep(2 * vv_response_time[vv_curve]) ts.log(imbalance_resp) ts.log('Starting imbalance test with VV mode at %s' % (imbalance_response)) ActiveFunction.set_imbalance_config(imbalance_angle_fix=imbalance_fix) #ts.log_debug(f'{imbalance_response}') dataset_filename = f'VV_IMB_{imbalance_fix}_{imbalance_response}' ActiveFunction.reset_filename(filename=dataset_filename) ts.log('------------{}------------'.format(dataset_filename)) # Start the data acquisition systems daq.data_capture(True) ''' h) For multiphase units, step the AC test source voltage to Case A from Table 24. ''' if grid is not None: step_label = ActiveFunction.get_step_label() ts.log('Voltage step: setting Grid simulator to case A (IEEE 1547.1-Table 24)(%s)' % step) ActiveFunction.start(daq=daq, step_label=step_label) v_target = ActiveFunction.set_grid_asymmetric(grid=grid, case='case_a', imbalance_resp=imbalance_response) ts.log_debug(f'v_target={v_target}') step_dict = {'V': v_target} ActiveFunction.record_timeresponse(daq=daq) ActiveFunction.evaluate_criterias(daq=daq, step_dict=step_dict) result_summary.write(ActiveFunction.write_rslt_sum()) ''' i) For multiphase units, step the AC test source voltage to VN. ''' if grid is not None: step_label = ActiveFunction.get_step_label() v_target = v_nom ActiveFunction.start(daq=daq, step_label=step_label) ts.log('Voltage step: setting Grid simulator voltage to %s (%s)' % (v_nom, step)) grid.voltage(v_target) step_dict = {'V': v_target} ActiveFunction.record_timeresponse(daq=daq) ActiveFunction.evaluate_criterias(daq=daq, step_dict=step_dict) result_summary.write(ActiveFunction.write_rslt_sum()) """ j) For multiphase units, step the AC test source voltage to Case B from Table 24. """ if grid is not None: step_label = ActiveFunction.get_step_label() ActiveFunction.start(daq=daq, step_label=step_label) ts.log('Voltage step: setting Grid simulator to case B (IEEE 1547.1-Table 24)(%s)' % step) v_target = ActiveFunction.set_grid_asymmetric(grid=grid, case='case_b', imbalance_resp=imbalance_response) step_dict = {'V': v_target} ActiveFunction.record_timeresponse(daq=daq) ActiveFunction.evaluate_criterias(daq=daq, step_dict=step_dict) result_summary.write(ActiveFunction.write_rslt_sum()) """ k) For multiphase units, step the AC test source voltage to VN """ if grid is not None: step_label = ActiveFunction.get_step_label() v_target = v_nom ActiveFunction.start(daq=daq, step_label=step_label) ts.log('Voltage step: setting Grid simulator voltage to %s (%s)' % (v_nom, step)) grid.voltage(v_nom) step_dict = {'V': v_target} ActiveFunction.record_timeresponse(daq=daq) ActiveFunction.evaluate_criterias(daq=daq, step_dict=step_dict) result_summary.write(ActiveFunction.write_rslt_sum()) ts.log('Sampling complete') dataset_filename = dataset_filename + ".csv" daq.data_capture(False) ds = daq.data_capture_dataset() ts.log('Saving file: %s' % dataset_filename) ds.to_csv(ts.result_file_path(dataset_filename)) result_params['plot.title'] = dataset_filename.split('.csv')[0] ts.result_file(dataset_filename, params=result_params) result = script.RESULT_COMPLETE except script.ScriptFail as e: reason = str(e) if reason: ts.log_error(reason) except Exception as e: if dataset_filename is not None: dataset_filename = dataset_filename + ".csv" daq.data_capture(False) ds = daq.data_capture_dataset() ts.log('Saving file: %s' % dataset_filename) ds.to_csv(ts.result_file_path(dataset_filename)) result_params['plot.title'] = dataset_filename.split('.csv')[0] ts.result_file(dataset_filename, params=result_params) raise finally: if daq is not None: daq.close() if pv is not None: if p_rated is not None: pv.power_set(p_rated) pv.close() if grid is not None: if v_nom is not None: grid.voltage(v_nom) grid.close() if chil is not None: chil.close() if eut is not None: #eut.volt_var(params={'Ena': False}) #eut.volt_watt(params={'Ena': False}) eut.close() if result_summary is not None: result_summary.close() return result
def test_run(): result = script.RESULT_FAIL eut = grid = load = pv = daq = chil = None # result params result_params = { 'plot.title': ts.name, 'plot.x.title': 'Time (sec)', 'plot.x.points': 'TIME', 'plot.y.points': 'U1', 'plot.y.title': 'U1', 'plot.y2.points': 'I1', 'plot.y2.title': 'I1' } try: ''' This procedure uses the step function defined in Annex A. a) Connect the EUT according to the instructions and specifications provided by the manufacturer. b) Set all source parameters to the nominal operating conditions for the EUT. c) Set (or verify) all EUT parameters to the nominal operating settings. If the overvoltage trip time setting is adjustable, set it to the minimum. d) Record applicable settings. e) Set the source voltage to a value within 10% of, but not exceeding, the overvoltage trip point setting. The source shall be held at this voltage for period t_hold. At the end of this period, step the source voltage to a value that causes the unit to trip. Hold this value until the unit trips. For multiphase units, this test may be performed on one phase only. f) Record the trip time. g) Repeat steps d) through f) four times for a total of five tests. h) If the overvoltage time setting is adjustable, repeat steps d) through g) at the midpoint and maximum overvoltage time settings. ''' phases = ts.param_value('eut.phases') p_rated = ts.param_value('eut.p_rated') v_nom = ts.param_value('eut.v_nom') # volts v_msa = ts.param_value('eut.v_msa') t_msa = ts.param_value('eut.t_msa') t_trip = ts.param_value('eut.t_trip') P_T = ts.param_value('vrt.v_test') # percentage t_hold = ts.param_value('vrt.t_hold') n_r = ts.param_value('vrt.n_r') # P_T = trip voltage # P_b = starting voltage # P_U = test voltage # Parameter A shall be chosen so that P_U is at least 110% (90% for under value tests) of P_T P_T_volts = (P_T / 100) * v_nom if P_T_volts > v_nom: A = 0.1 * P_T_volts # volts else: A = -0.1 * P_T_volts # volts P_U = P_T_volts + A if P_U < 0: P_U = 0 # P_b = v_nom + (v_nom - v_msa)*0.9 # if the grid sim has a long slew rate P_b = v_nom # starting voltage of the test, in volts ''' Set all AC source parameters to the normal operating conditions for the EUT. ''' # initialize HIL environment, if necessary chil = hil.hil_init(ts) if chil is not None: chil.config() # grid simulator is initialized with test parameters and enabled grid = gridsim.gridsim_init(ts) #ts.log("here?") #return # pv simulator is initialized with test parameters and enabled pv = pvsim.pvsim_init(ts) pv.power_set(p_rated) pv.power_on() # initialize data acquisition daq = das.das_init(ts) ts.log('DAS device: %s' % daq.info()) ''' Turn on the EUT. It is permitted to set all L/HVRT limits and abnormal voltage trip parameters to the widest range of adjustability possible with the SPF enabled in order not to cross the must trip magnitude threshold during the test. ''' # it is assumed the EUT is on eut = der.der_init(ts) eut.config() # run data capture ts.log('Running capture 1') f_sample = 50 #wanbin wfm_config_params = { 'sample_rate': f_sample, 'pre_trigger': 0.5, 'post_trigger': t_trip + 1.5, 'timeout': 30 } if phases == 'Single Phase': wfm_config_params['channels'] = ['AC_V_1', 'AC_I_1', 'EXT'] else: wfm_config_params['channels'] = [ 'AC_V_1', 'AC_V_2', 'AC_V_3', 'AC_I_1', 'AC_I_2', 'AC_I_3', 'EXT' ] if chil is not None: wfm_config_params['channels'] = [ 'AC_V_1', 'AC_V_2', 'AC_V_3', 'AC_I_1', 'AC_I_2', 'AC_I_3' ] if P_T > v_nom: wfm_config_params['trigger_cond'] = 'Rising Edge' wfm_config_params['trigger_channel'] = 'AC_V_1' wfm_config_params['trigger_level'] = ( (P_U + P_b) / 2.) * np.sqrt(2) else: wfm_config_params['trigger_cond'] = 'Rising Edge' wfm_config_params[ 'trigger_channel'] = 'AC_I_1' # catch the current increase on v sag wfm_config_params['trigger_level'] = ( (p_rated / v_nom) / 3) * 1.05 * np.sqrt( 2) # trigger when current 5% above rated else: wfm_config_params['trigger_cond'] = 'Rising Edge' wfm_config_params['trigger_channel'] = 'EXT' wfm_config_params['trigger_level'] = 1 # 0-5 V signal daq.waveform_config(params=wfm_config_params) # open result summary file result_summary_filename = 'result_summary.csv' result_summary = open(ts.result_file_path(result_summary_filename), 'a+') ts.result_file(result_summary_filename) if phases == 'Single Phase': # single phase to be cleaned up result_summary.write( 'Result, Test Name, t_trip, t_trip_meas, Dataset File\n') else: result_summary.write( 'Result, Test Name, t_trip, t_trip_meas, Dataset File\n') # set phase tests that are enabled phase_tests = [] # set single phase test voltages and test labels if phases == 'Single Phase': phase_tests.append(((P_U, v_nom, v_nom), 'Phase 1 Fault Test', 'p1', (P_b, v_nom, v_nom))) if phases == '3-Phase 3-Wire' or phases == '3-Phase 4-Wire': if ts.param_value('vrt.phase_1') == 'Enabled': phase_tests.append(((P_U, v_nom, v_nom), 'Phase 1 Fault Test', 'p1', (P_b, v_nom, v_nom))) if ts.param_value('vrt.phase_2') == 'Enabled': phase_tests.append(((v_nom, P_U, v_nom), 'Phase 2 Fault Test', 'p2', (v_nom, P_b, v_nom))) if ts.param_value('vrt.phase_3') == 'Enabled': phase_tests.append(((v_nom, v_nom, P_U), 'Phase 3 Fault Test', 'p3', (v_nom, v_nom, P_b))) ts.log_debug('Phase Tests: %s' % phase_tests) for phase_test in phase_tests: if daq is not None: ts.log('Starting RMS data capture') daq.data_capture(True) v_1, v_2, v_3 = phase_test[0] v_1_init, v_2_init, v_3_init = phase_test[3] # generate step change profile profile = voltage_rt_profile(v_nom=100, t_t=t_trip, P_b=P_b, P_U=P_U) for i in range(n_r): filename = '%s_%s_%s.csv' % ('voltage_trip', phase_test[2], i + 1) # start trip time test profile_supported = False if profile_supported: # UNTESTED! daq.waveform_capture(True) # turn on daq waveform capture t_sleep = 2 # need time to prepare acquisition ts.log( 'Sleeping for %s seconds, to wait for the capture to prime.' % t_sleep) ts.sleep(t_sleep) grid.profile_load(profile=profile) # grid.profile_load(profile_name='VV Profile') ts.log_debug(profile) ts.log('Starting profile now!') grid.profile_start() # Provide GUI with countdown timer start_time = time.time() profile_time = profile[-1][0] ts.log('Profile duration is %s seconds' % profile_time) while (time.time() - start_time) < profile_time: remaining_time = profile_time - (time.time() - start_time) ts.log('Sleeping for another %0.1f seconds' % remaining_time) ts.sleep(5) grid.profile_stop() else: grid.voltage((v_nom, v_nom, v_nom)) ts.log( 'Setting voltage: v_1 = %s v_2 = %s v_3 = %s for %s seconds' % (v_nom, v_nom, v_nom, t_hold)) # Check that the EUT is functioning daq.data_sample() # Sample before the grid voltage change data = daq.data_capture_read() p1 = data.get('AC_P_1') p2 = data.get('AC_P_2') p3 = data.get('AC_P_3') ts.log( ' EUT powers before dwell: p_1 = %s p_2 = %s p_3 = %s' % (p1, p2, p3)) grid.voltage((v_nom, v_nom, v_nom)) countdown = 330 # Wanbin : 5 min for sma inverter while (p1 < (0.1 * p_rated) / 3 or p2 < (0.1 * p_rated) / 3 or p3 < (0.1 * p_rated) / 3) and countdown > 0: if countdown % 10 == 0: ts.log( ' Waiting for EUT to turn back on for another %s seconds. (p1=%s, p2=%s, p3=%s)' % (countdown, p1, p2, p3)) countdown -= 1 ts.sleep(1) data = daq.data_capture_read() p1 = data.get('AC_P_1') p2 = data.get('AC_P_2') p3 = data.get('AC_P_3') ts.sleep(5) # return to ~rated power forced = False #wanbin if forced: daq.waveform_force_trigger() ts.sleep(0.5) else: # Start Waveform Capture daq.waveform_capture( True) # turn on daq waveform capture t_sleep = 2 # need time to prepare acquisition ts.log( 'Sleeping for %s seconds, to wait for the capture to prime.' % t_sleep) ts.sleep(t_sleep) if v_1_init != v_nom or v_2_init != v_nom or v_3_init != v_nom: # Run Profile ts.log( ' Setting voltage: v_1 = %s v_2 = %s v_3 = %s for %s seconds' % (v_1_init, v_2_init, v_3_init, t_hold)) # v_1_init = v_1_init*(grid.v_nom/v_nom) # v_2_init = v_2_init*(grid.v_nom/v_nom) # v_3_init = v_3_init*(grid.v_nom/v_nom) grid.voltage(voltage=(v_1_init, v_2_init, v_3_init)) ts.sleep(t_hold) ts.log( ' Setting voltage: v_1 = %s v_2 = %s v_3 = %s for %s seconds' % (v_1, v_2, v_3, t_trip + 1)) # v_1 = v_1*(grid.v_nom/v_nom) # v_2 = v_2*(grid.v_nom/v_nom) # v_3 = v_3*(grid.v_nom/v_nom) grid.voltage((v_1, v_2, v_3)) ts.sleep(t_trip + 1) # get data from daq waveform capture done = False countdown = int(t_trip) + 20 while not done and countdown > 0: status = daq.waveform_status() if status == 'COMPLETE': ds = daq.waveform_capture_dataset() done = True elif status == 'INACTIVE': ts.log('Waveform capture inactive') raise script.ScriptFail('Waveform capture inactive') elif status == 'ACTIVE': ts.log('Waveform capture active, sleeping') ts.sleep(1) countdown -= 1 # save captured data set to capture file in SVP result directory if ds is not None: ds.to_csv(ts.result_file_path(filename)) testname = "%s test" % filename result_params['plot.title'] = testname # plot? ts.result_file(filename, params=result_params) #ts.result_file(filename) wf = waveform.Waveform(ts) wf.from_csv(ts.result_file_path(filename)) # wf.compute_rms_data(phase=1) # time_data = wf.rms_data['1'][0] # Time # voltage_data_1 = wf.rms_data['1'][1] # Voltage # current_data_1 = wf.rms_data['1'][2] # Current # plt.figure(1) # plt.plot(time_data, voltage_data_1) # plt.figure(2) # plt.plot(time_data, current_data_1) # plt.show() if phase_test[2] == 'p1': channel_data = [wf.channel_data[1], wf.channel_data[4]] elif phase_test[2] == 'p2': channel_data = [wf.channel_data[2], wf.channel_data[5]] else: channel_data = [wf.channel_data[3], wf.channel_data[6]] _, voltage_data_1 = waveform_analysis.calculateRmsOfSignal( data=channel_data[0], windowSize=40, # ms samplingFrequency=f_sample) #wanbin time_data, current_data_1 = waveform_analysis.calculateRmsOfSignal( data=channel_data[1], windowSize=40, # ms samplingFrequency=f_sample) #wanbin for i in range(10): ts.log("%.2f, %.2f" % (current_data_1[i], voltage_data_1[i])) # plt.figure(1) # plt.plot(time_data, voltage_data_1, 'r', time_data, current_data_1, 'b') # plt.show() v_window = 10 # v_window is the window around the nominal RMS voltage where the VRT test is started #wanbin # with low sampling rate, v_nom may not good ref volt_idx = [] if P_T < 100.0: volt_idx = [ idx for idx, i in enumerate(voltage_data_1) if (i <= (voltage_data_1[1] * P_T / 100.0 + 2)) ] else: volt_idx = [ idx for idx, i in enumerate(voltage_data_1) if (i >= (voltage_data_1[1] * P_T / 100.0 - 2)) ] if len(volt_idx) != 0: t_start = time_data[min(volt_idx)] else: t_start = 0 ts.log_warning( 'Voltage deviation started before the waveform capture.' ) ts.log_debug(voltage_data_1[1] * P_T / 100.0) ts.log_debug(P_T) ac_current_idx = [ idx for idx, i in enumerate(current_data_1) if i <= 0.1 * current_data_1[1] ] #wanbin p_rated -> current_data[0] if len(ac_current_idx) != 0: trip_time = time_data[min(ac_current_idx)] t_trip_meas = trip_time - t_start ts.log( 'Voltage change started at %s, EUT trip at %s. Total trip time: %s sec.' % (t_start, trip_time, t_trip_meas)) # Determine pass/fail if t_trip_meas <= t_trip: passfail = 'Pass' else: passfail = 'Fail' else: ts.log_warning('No waveform data collected') raise result_summary.write('%s, %s, %s, %s, %s \n' % (passfail, ts.config_name(), t_trip, t_trip_meas, filename)) result = script.RESULT_COMPLETE except script.ScriptFail, e: reason = str(e) if reason: ts.log_error(reason)
def test_run(): result = script.RESULT_FAIL grid = None pv = p_rated = None daq = None eut = None rs = None phil = None result_summary = None step = None q_initial = None dataset_filename = None try: sink_power = ts.param_value('eut.sink_power') p_rated = ts.param_value('eut.p_rated') p_rated_prime = ts.param_value('eut.p_rated_prime') s_rated = ts.param_value('eut.s_rated') var_rated = ts.param_value('eut.var_rated') # DC voltages v_nom_in_enabled = ts.param_value('cpf.v_in_nom') v_min_in_enabled = ts.param_value('cpf.v_in_min') v_max_in_enabled = ts.param_value('cpf.v_in_max') v_nom_in = ts.param_value('eut.v_in_nom') v_min_in = ts.param_value('eut_cpf.v_in_min') v_max_in = ts.param_value('eut_cpf.v_in_max') # AC voltages v_nom = ts.param_value('eut.v_nom') v_min = ts.param_value('eut.v_low') v_max = ts.param_value('eut.v_high') f_nom = ts.param_value('eut.f_nom') p_min = ts.param_value('eut.p_min') p_min_prime = ts.param_value('eut.p_min_prime') phases = ts.param_value('eut.phases') # Pass/fail accuracies pf_msa = ts.param_value('eut.pf_msa') # EUI Absorb capabilities absorb = {} absorb['ena'] = ts.param_value('eut_cpf.sink_power') absorb['p_rated_prime'] = ts.param_value('eut_cpf.p_rated_prime') absorb['p_min_prime'] = ts.param_value('eut_cpf.p_min_prime') # initialize HIL environment, if necessary ts.log_debug(15 * "*" + "HIL initialization" + 15 * "*") phil = hil.hil_init(ts) if phil is not None: # return self.ts.param_value(self.group_name + '.' + GROUP_NAME + '.' + name) open_proj = phil._param_value('hil_config_open') compilation = phil._param_value('hil_config_compile') stop_sim = phil._param_value('hil_config_stop_sim') load = phil._param_value('hil_config_load') execute = phil._param_value('hil_config_execute') model_name = phil._param_value('hil_config_model_name') phil.config() ''' RTLab OpWriteFile Math using worst case scenario of 160 seconds, 14 signals and Ts = 40e-6 Duration of acquisition in number of points: Npoints = (Tend-Tstart)/(Ts*dec) = (350)/(0.000040*25) = 1350e3 Acquisition frame duration: Tframe = Nbss * Ts * dec = 1000*0.000040*250 = 10 sec Number of buffers to be acquired: Nbuffers = Npoints / Nbss = (Tend - Tstart) / Tframe = 16 Minimum file size: MinSize= Nbuffers x SizeBuf = [(Tend - Tstart) / Ts ] * (Nsig+1) * 8 * Nbss = (160/40e-6)*(14+1)*8*1000 = 4.8e11 SizeBuf = 1/Nbuffers * {[(Tend - Tstart) / Ts ]*(Nsig+1)*8*Nbss} = [(160/0.000040)*(14+1)*8*1e3]/16 = 30e9 Size of one buffer in bytes (SizeBuf) = (Nsig+1) * 8 * Nbss (Minimum) = (14+1)*8*1000 = 120e3 ''' """ Configure settings in 1547.1 Standard module for the Frequency Ride Through Tests """ pwr = float(ts.param_value('frt.high_pwr_value')) repetitions = ts.param_value('frt.repetitions') if ts.param_value('frt.wav_ena') == "Yes": wav_ena = True else: wav_ena = False if ts.param_value('frt.data_ena') == "Yes": data_ena = True else: data_ena = False FreqRideThrough = p1547.FrequencyRideThrough( ts=ts, support_interfaces={"hil": phil}) # result params # result_params = lib_1547.get_rslt_param_plot() # ts.log(result_params # grid simulator is initialized with test parameters and enabled ts.log_debug(15 * "*" + "Gridsim initialization" + 15 * "*") grid = gridsim.gridsim_init(ts, support_interfaces={ "hil": phil }) # Turn on AC so the EUT can be initialized if grid is not None: grid.voltage(v_nom) # Set the grid simulator rocof to 3Hz/s grid.rocof(FreqRideThrough.get_rocof_dic()) # pv simulator is initialized with test parameters and enabled ts.log_debug(15 * "*" + "PVsim initialization" + 15 * "*") pv = pvsim.pvsim_init(ts) if pv is not None: pv.power_set(p_rated) pv.power_on() # Turn on DC so the EUT can be initialized # initialize data acquisition ts.log_debug(15 * "*" + "DAS initialization" + 15 * "*") daq = das.das_init(ts, support_interfaces={"hil": phil, "pvsim": pv}) daq.waveform_config({ "mat_file_name": "WAV.mat", "wfm_channels": FreqRideThrough.get_wfm_file_header() }) if daq is not None: daq.sc['F_MEAS'] = 100 # open result summary file result_summary_filename = 'result_summary.csv' result_summary = open(ts.result_file_path(result_summary_filename), 'a+') ts.result_file(result_summary_filename) result_summary.write('Test Name, Waveform File, RMS File\n') """ Set the frequency droop function and droop values to make the active power change with respect to frequency as small as possible. """ # Wait to establish communications with the EUT after AC and DC power are provided eut = der.der_init(ts, support_interfaces={"hil": phil}) if eut is not None: eut.config() # Default curve is characteristic curve 1 fw_curve = 1 ActiveFunction = p1547.ActiveFunction(ts=ts, script_name='Freq-Watt', functions=[FW], criteria_mode=[True, True, True]) fw_param = ActiveFunction.get_params(function=FW, curve=fw_curve) ts.log_debug('fw_param:%s' % fw_param) if eut is not None: params = { 'Ena': True, 'curve': fw_curve, 'dbf': fw_param['dbf'], 'kof': fw_param['kof'], 'RspTms': fw_param['tr'] } settings = eut.freq_watt(params) ts.log_debug('Initial EUT FW settings are %s' % settings) """ Set or verify that all frequency trip settings are set to not influence the outcome of the test. """ # ts.log_debug('HFRT and trip parameters from EUT : {}'.format(eut.frt_stay_connected_high())) # ts.log_debug('LFRT and trip parameters from EUT : {}'.format(eut.frt_stay_connected_low())) """ Operate the ac test source at nominal frequency ± 0.1 Hz. """ # Configured in PHIL on startup # Initial loop for all mode that will be executed modes = FreqRideThrough.get_modes() # Options: LFRT, HFRT ts.log(f"FRT modes tested : '{modes}'") for current_mode in modes: for repetition in range(1, repetitions + 1): dataset_filename = f'{current_mode}_{round(pwr*100)}PCT_{repetition}' ts.log_debug(15 * "*" + f"Starting {dataset_filename}" + 15 * "*") if data_ena: daq.data_capture(True) """ Setting up available power to appropriate power level """ if pv is not None: ts.log_debug(f'Setting power level to {pwr}') pv.iv_curve_config(pmp=p_rated, vmp=v_nom_in) pv.irradiance_set(1000.) pv.power_set(p_rated * pwr) """ Initiating voltage sequence for FRT """ frt_test_sequences = FreqRideThrough.set_test_conditions( current_mode) ts.log_debug(frt_test_sequences) frt_stop_time = FreqRideThrough.get_frt_stop_time( frt_test_sequences) if phil is not None: # This adds 5 seconds of nominal behavior for EUT normal shutdown. This 5 sec is not recorded. frt_stop_time = frt_stop_time + 5 ts.log('Stop time set to %s' % phil.set_stop_time(frt_stop_time)) # The driver should take care of this by selecting "Yes" to "Load the model to target?" phil.load_model_on_hil() # You need to first load the model, then configure the parameters # Now that we have all the test_sequences its time to sent them to the model. FreqRideThrough.set_frt_model_parameters( frt_test_sequences) # The driver parameter "Execute the model on target?" should be set to "No" phil.start_simulation() ts.sleep(0.5) sim_time = phil.get_time() while (frt_stop_time - sim_time ) > 1.0: # final sleep will get to stop_time. sim_time = phil.get_time() ts.log( 'Sim Time: %0.3f. Waiting another %0.3f sec before saving data.' % (sim_time, frt_stop_time - sim_time)) ts.sleep(5) rms_dataset_filename = "No File" wave_start_filename = "No File" if data_ena: rms_dataset_filename = dataset_filename + "_RMS.csv" daq.data_capture(False) # complete data capture ts.log( 'Waiting for Opal to save the waveform data: {}'. format(dataset_filename)) ts.sleep(10) if wav_ena: # Convert and save the .mat file ts.log('Processing waveform dataset(s)') wave_start_filename = dataset_filename + "_WAV.csv" ds = daq.waveform_capture_dataset( ) # returns list of databases of waveforms (overloaded) ts.log(f'Number of waveforms to save {len(ds)}') if len(ds) > 0: ds[0].to_csv( ts.result_file_path(wave_start_filename)) ts.result_file(wave_start_filename) if data_ena: ds = daq.data_capture_dataset() ts.log('Saving file: %s' % rms_dataset_filename) ds.to_csv(ts.result_file_path(rms_dataset_filename)) ds.remove_none_row( ts.result_file_path(rms_dataset_filename), "TIME") result_params = { 'plot.title': rms_dataset_filename.split('.csv')[0], 'plot.x.title': 'Time (sec)', 'plot.x.points': 'TIME', 'plot.y.points': 'AC_VRMS_1, AC_VRMS_2, AC_VRMS_3', 'plot.y.title': 'Voltage (V)', 'plot.y2.points': 'AC_IRMS_1, AC_IRMS_2, AC_IRMS_3', 'plot.y2.title': 'Current (A)', } ts.result_file(rms_dataset_filename, params=result_params) result_summary.write( '%s, %s, %s,\n' % (dataset_filename, wave_start_filename, rms_dataset_filename)) phil.stop_simulation() result = script.RESULT_COMPLETE except script.ScriptFail as e: reason = str(e) if reason: ts.log_error(reason) except Exception as e: ts.log_error((e, traceback.format_exc())) ts.log_error('Test script exception: %s' % traceback.format_exc()) finally: if grid is not None: grid.close() if pv is not None: if p_rated is not None: pv.power_set(p_rated) pv.close() if daq is not None: daq.close() if eut is not None: # eut.fixed_pf(params={'Ena': False, 'PF': 1.0}) eut.close() if rs is not None: rs.close() if phil is not None: if phil.model_state() == 'Model Running': phil.stop_simulation() phil.close() if result_summary is not None: result_summary.close() # create result workbook excelfile = ts.config_name() + '.xlsx' rslt.result_workbook(excelfile, ts.results_dir(), ts.result_dir()) ts.result_file(excelfile) return result
def test_run(): eut = None chil = None daq = None pv = None result_summary = None result = script.RESULT_FAIL # result params result_params = { 'plot.title': ts.name, 'plot.x.title': 'Time (sec)', 'plot.x.points': 'TIME', 'plot.y.points': 'W_TARG, W_TOTAL, W_INV', 'plot.y.title': 'EUT Power (W)', } try: # Initialize DER configuration eut = der.der_init(ts) eut.config() # Initialize CHIL environment, if necessary chil = hil.hil_init(ts) if chil is not None: chil.config() # PV simulator is initialized with test parameters and enabled pv = pvsim.pvsim_init(ts) pv.irradiance_set(1000) pv.power_on() # Initialize data acquisition with soft channels (sc) that include data that doesn't come from the DAQ sc_points = ['W_TARG', 'W_TOTAL', 'W_INV'] daq = das.das_init(ts, sc_points=sc_points) ts.log('DAS device: %s' % daq.info()) # Open result summary file - this will include a selection of DAQ data to evaluate performance of the EUT result_summary_filename = 'result_summary.csv' result_summary = open(ts.result_file_path(result_summary_filename), 'a+') # Open .csv file ts.result_file( result_summary_filename) # create result file in the GUI # Write result summary header result_summary.write( 'Test Name, Power Setting (%), Inverter-Reported Power (W), DAS Power (W), ' 'Inverter-Reported Power (%), DAS Power (%)\n') # Get EUT nameplate power eut_nameplate_power = eut.nameplate().get('WRtg') inv_power = eut.measurements().get('W') timeout = 20. if inv_power <= eut_nameplate_power / 10.: eut.connect(params={'Conn': True}) pv.irradiance_set( 995) # Perturb the pv slightly to start the inverter while inv_power <= eut_nameplate_power / 10. and timeout >= 0: ts.log( 'Inverter power is at %0.1f. Waiting %s more seconds or until EUT starts...' % (inv_power, timeout)) ts.sleep(1) timeout -= 1 inv_power = eut.measurements().get('W') if timeout == 0: result = script.RESULT_FAIL raise der.DERError('Inverter did not start.') for time_loop in range(2): daq.data_capture(True) # Begin data capture for this power loop for power_limit_pct in [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]: daq.sc['W_TARG'] = eut_nameplate_power * ( float(power_limit_pct) / 100.) eut.limit_max_power(params={ 'Ena': True, 'WMaxPct': power_limit_pct }) ts.log('EUT power set to %0.2f%%' % power_limit_pct) ts.sleep(2) daq.sc['W_INV'] = eut.measurements().get( 'W') # Get the inverter-measured power and save it. daq.data_sample( ) # force a data capture point after the sleep and add this to the dataset daq_data = daq.data_capture_read( ) # read the last data point dictionary from the daq object try: # if 3 phase device add up the power from each phase daq.sc['W_TOTAL'] = daq_data['AC_P_1'] + daq_data[ 'AC_P_2'] + daq_data['AC_P_2'] except Exception, e: # if single phase device daq.sc['W_TOTAL'] = daq_data['AC_P_1'] # Record 1 set of power values for each power level setting result_summary.write( '%s, %s, %s, %s, %s, %s\n' % (time_loop + 1, power_limit_pct, daq.sc['W_INV'], daq.sc['W_TOTAL'], daq.sc['W_INV'] / eut_nameplate_power, daq.sc['W_TOTAL'] / eut_nameplate_power)) daq.data_capture(False) # Stop data capture ds = daq.data_capture_dataset( ) # generate dataset from the daq data that was recorded testname = 'CurtailmentRun_%s' % (str(time_loop + 1) ) # Pick name for the DAS data filename = testname + '.csv' # Pick name for the DAS .csv data file ds.to_csv( ts.result_file_path(filename)) # Convert data to .cvs file result_params[ 'plot.title'] = testname # update title for the excel plot for this dataset ts.result_file( filename, params=result_params ) # Add results info to .xml log, which will be used to plot ts.log('Saving data capture: %s' % filename) result = script.RESULT_COMPLETE
def watt_var_mode(wv_curves, wv_response_time, pwr_lvls): result = script.RESULT_FAIL daq = None v_nom = None grid = None pv = None eut = None chil = None result_summary = None dataset_filename = None try: cat = ts.param_value('eut.cat') cat2 = ts.param_value('eut.cat2') sink_power = ts.param_value('eut.sink_power') p_rated = ts.param_value('eut.p_rated') p_rated_prime = ts.param_value('eut.p_rated_prime') var_rated = ts.param_value('eut.var_rated') s_rated = ts.param_value('eut.s_rated') # DC voltages v_in_nom = ts.param_value('eut.v_in_nom') #v_min_in = ts.param_value('eut.v_in_min') #v_max_in = ts.param_value('eut.v_in_max') # AC voltages v_nom = ts.param_value('eut.v_nom') v_low = ts.param_value('eut.v_low') v_high = ts.param_value('eut.v_high') p_min = ts.param_value('eut.p_min') p_min_prime = ts.param_value('eut.p_min_prime') phases = ts.param_value('eut.phases') # EUI Absorb capabilities absorb = {} absorb['ena'] = ts.param_value('eut_cpf.sink_power') """ A separate module has been create for the 1547.1 Standard """ #lib_1547 = p1547.module_1547(ts=ts, aif='WV', criteria) ActiveFunction = p1547.ActiveFunction(ts=ts, functions=[WV], script_name='Watt-Var', criteria_mode=[True, True, True]) ts.log_debug("1547.1 Library configured for %s" % ActiveFunction.get_script_name()) # result params result_params = ActiveFunction.get_rslt_param_plot() ''' a) Connect the EUT according to the instructions and specifications provided by the manufacturer. ''' # initialize HIL environment, if necessary chil = hil.hil_init(ts) if chil is not None: chil.config() # pv simulator is initialized with test parameters and enabled pv = pvsim.pvsim_init(ts) if pv is not None: pv.power_set(p_rated) pv.power_on() # Turn on DC so the EUT can be initialized # DAS soft channels das_points = ActiveFunction.get_sc_points() # initialize data acquisition system daq = das.das_init(ts, sc_points=das_points['sc']) ts.log_debug(0.05 * ts.param_value('eut.s_rated')) daq.sc['P_TARGET'] = v_nom daq.sc['Q_TARGET'] = 100 daq.sc['Q_TARGET_MIN'] = 100 daq.sc['Q_TARGET_MAX'] = 100 daq.sc['event'] = 'None' ts.log('DAS device: %s' % daq.info()) ''' b) Set all voltage trip parameters to the widest range of adjustability. Disable all reactive/active power control functions. ''' eut = der.der_init(ts) if eut is not None: eut.config() ts.log_debug(eut.measurements()) #Disable all functions on EUT eut.deactivate_all_fct() ts.log_debug('Voltage trip parameters set to the widest range: v_min: {0} V, ' 'v_max: {1} V'.format(v_low, v_high)) try: eut.vrt_stay_connected_high(params={'Ena': True, 'ActCrv': 0, 'Tms1': 3000, 'V1': v_high, 'Tms2': 0.16, 'V2': v_high}) except Exception as e: ts.log_error('Could not set VRT Stay Connected High curve. %s' % e) try: eut.vrt_stay_connected_low(params={'Ena': True, 'ActCrv': 0, 'Tms1': 3000, 'V1': v_low, 'Tms2': 0.16, 'V2': v_low}) except Exception as e: ts.log_error('Could not set VRT Stay Connected Low curve. %s' % e) else: ts.log_debug('Set L/HVRT and trip parameters set to the widest range of adjustability possible.') # Special considerations for CHIL ASGC/Typhoon startup if chil is not None: inv_power = eut.measurements().get('W') timeout = 120. if inv_power <= p_rated * 0.85: pv.irradiance_set(995) # Perturb the pv slightly to start the inverter ts.sleep(3) eut.connect(params={'Conn': True}) while inv_power <= p_rated * 0.85 and timeout >= 0: ts.log('Inverter power is at %0.1f. Waiting up to %s more seconds or until EUT starts...' % (inv_power, timeout)) ts.sleep(1) timeout -= 1 inv_power = eut.measurements().get('W') if timeout == 0: result = script.RESULT_FAIL raise der.DERError('Inverter did not start.') ts.log('Waiting for EUT to ramp up') ts.sleep(8) ts.log_debug('DAS data_read(): %s' % daq.data_read()) ''' c) Set all AC test source parameters to the nominal operating voltage and frequency. ''' grid = gridsim.gridsim_init(ts, support_interfaces={'hil': chil}) # Turn on AC so the EUT can be initialized if grid is not None: grid.voltage(v_nom) # open result summary file result_summary_filename = 'result_summary.csv' result_summary = open(ts.result_file_path(result_summary_filename), 'a+') ts.result_file(result_summary_filename) result_summary.write(ActiveFunction.get_rslt_sum_col_name()) ''' d) Adjust the EUT's available active power to Prated. For an EUT with an input voltage range, set the input voltage to Vin_nom. The EUT may limit active power throughout the test to meet reactive power requirements. For an EUT with an input voltage range. ''' if pv is not None: pv.iv_curve_config(pmp=p_rated, vmp=v_in_nom) pv.irradiance_set(1000.) ''' dd) Repeat steps e) through dd) for characteristics 2 and 3. ''' #TODO 1.Add absorb option possibility for EUT #TODO 2.Add communication with EUT for wv_curve in wv_curves: ts.log('Starting test with characteristic curve %s' % (wv_curve)) ActiveFunction.reset_curve(wv_curve) ActiveFunction.reset_time_settings(tr=wv_response_time[wv_curve], number_tr=2) p_pairs = ActiveFunction.get_params(function=WV, curve=wv_curve) ''' d2) Set EUT volt-var parameters to the values specified by Characteristic 1. All other function should be turned off. Turn off the autonomously adjusting reference voltage. ''' if eut is not None: # Activate watt-var function with following parameters # SunSpec convention is to use percentages for P and Q points. wv_curve_params = {'w': [p_pairs['P0']*(100/p_rated), p_pairs['P1']*(100/p_rated), p_pairs['P2']*(100/p_rated), p_pairs['P3']*(100/p_rated)], 'var': [p_pairs['Q0']*(100/var_rated), p_pairs['Q1']*(100/var_rated), p_pairs['Q2']*(100/var_rated), p_pairs['Q3']*(100/var_rated)]} ts.log_debug('Sending WV points: %s' % wv_curve_params) eut.watt_var(params={'Ena': True, 'curve': wv_curve_params}) ''' e) Verify volt-var mode is reported as active and that the correct characteristic is reported. ''' ts.log_debug('Initial EUT VV settings are %s' % eut.watt_var()) ''' cc) Repeat test steps d) through cc) at EUT power set at 20% and 66% of rated power. ''' for power in pwr_lvls: ActiveFunction.reset_pwr(pwr=power) if pv is not None: pv_power_setting = (p_rated * power) pv.iv_curve_config(pmp=pv_power_setting, vmp=v_in_nom) pv.irradiance_set(1000.) # Special considerations for CHIL ASGC/Typhoon startup # if chil is not None: inv_power = eut.measurements().get('W') timeout = 120. if inv_power <= pv_power_setting * 0.85: pv.irradiance_set(995) # Perturb the pv slightly to start the inverter ts.sleep(3) eut.connect(params={'Conn': True}) while inv_power <= pv_power_setting * 0.85 and timeout >= 0: ts.log('Inverter power is at %0.1f. Waiting up to %s more seconds or until EUT starts...' % (inv_power, timeout)) ts.sleep(1) timeout -= 1 inv_power = eut.measurements().get('W') if timeout == 0: result = script.RESULT_FAIL raise der.DERError('Inverter did not start.') ts.log('Waiting for EUT to ramp up') ts.sleep(8) #Create Watt-Var Dictionary p_steps_dict = ActiveFunction.create_wv_dict_steps() filename = 'WV_%s_PWR_%d' % (wv_curve, power * 100) ActiveFunction.reset_filename(filename=filename) ts.log('------------{}------------'.format(dataset_filename)) # Start the data acquisition systems daq.data_capture(True) for step_label, p_step in p_steps_dict.items(): ts.log('Power step: setting Grid power to %s (W)(%s)' % (p_step, step_label)) ActiveFunction.start(daq=daq, step_label=step_label) step_dict = {'V': v_nom, 'P': p_step} if pv is not None: pv.power_set(step_dict['P']) ActiveFunction.record_timeresponse(daq=daq) ActiveFunction.evaluate_criterias(daq=daq, step_dict=step_dict) result_summary.write(ActiveFunction.write_rslt_sum()) ts.log('Sampling complete') dataset_filename = filename + ".csv" daq.data_capture(False) ds = daq.data_capture_dataset() ts.log('Saving file: %s' % dataset_filename) ds.to_csv(ts.result_file_path(dataset_filename)) result_params['plot.title'] = dataset_filename.split('.csv')[0] ts.result_file(dataset_filename, params=result_params) result = script.RESULT_COMPLETE except script.ScriptFail as e: reason = str(e) if reason: ts.log_error(reason) except Exception as e: if dataset_filename is not None: dataset_filename = dataset_filename + ".csv" daq.data_capture(False) ds = daq.data_capture_dataset() ts.log('Saving file: %s' % dataset_filename) ds.to_csv(ts.result_file_path(dataset_filename)) result_params['plot.title'] = dataset_filename.split('.csv')[0] ts.result_file(dataset_filename, params=result_params) ts.log_error('Test script exception: %s' % traceback.format_exc()) finally: if daq is not None: daq.close() if pv is not None: pv.power_set(p_rated) pv.close() if grid is not None: if v_nom is not None: grid.voltage(v_nom) grid.close() if chil is not None: chil.close() if eut is not None: eut.deactivate_all_fct() eut.close() if result_summary is not None: result_summary.close() return result
def watt_var_mode(wv_curves, wv_response_time): result = script.RESULT_FAIL daq = None v_nom = None grid = None pv = None eut = None chil = None result_summary = None dataset_filename = None try: cat = ts.param_value('eut.cat') cat2 = ts.param_value('eut.cat2') sink_power = ts.param_value('eut.sink_power') p_rated = ts.param_value('eut.p_rated') p_rated_prime = ts.param_value('eut.p_rated_prime') var_rated = ts.param_value('eut.var_rated') s_rated = ts.param_value('eut.s_rated') # DC voltages v_in_nom = ts.param_value('eut.v_in_nom') #v_min_in = ts.param_value('eut.v_in_min') #v_max_in = ts.param_value('eut.v_in_max') # AC voltages v_nom = ts.param_value('eut.v_nom') v_low = ts.param_value('eut.v_low') v_high = ts.param_value('eut.v_high') p_min = ts.param_value('eut.p_min') p_min_prime = ts.param_value('eut.p_min_prime') phases = ts.param_value('eut.phases') # EUI Absorb capabilities absorb = {} absorb['ena'] = ts.param_value('eut_cpf.sink_power') """ A separate module has been create for the 1547.1 Standard """ lib_1547 = p1547.module_1547(ts=ts, aif='WV', absorb=absorb) ts.log_debug("1547.1 Library configured for %s" % lib_1547.get_test_name()) # result params result_params = lib_1547.get_rslt_param_plot() ''' a) Connect the EUT according to the instructions and specifications provided by the manufacturer. ''' # initialize HIL environment, if necessary chil = hil.hil_init(ts) if chil is not None: chil.config() # DAS soft channels das_points = lib_1547.get_sc_points() # initialize data acquisition system daq = das.das_init(ts, sc_points=das_points['sc']) daq.sc['P_TARGET'] = p_min daq.sc['Q_TARGET'] = 100 daq.sc['Q_TARGET_MIN'] = 100 daq.sc['Q_TARGET_MAX'] = 100 daq.sc['event'] = 'None' ts.log('DAS device: %s' % daq.info()) # pv simulator is initialized with test parameters and enabled pv = pvsim.pvsim_init(ts) if pv is not None: pv.power_set(p_rated) pv.power_on() # Turn on DC so the EUT can be initialized if callable(getattr(daq, "set_dc_measurement", None) ): # for DAQs that don't natively have dc measurements daq.set_dc_measurement( pv) # send pv obj to daq to get dc measurements ts.sleep(0.5) eut = der.der_init(ts) if eut is not None: eut.config() ts.log_debug(eut.measurements()) # Special considerations for CHIL ASGC/Typhoon startup if chil is not None: if chil.hil_info()['mode'] == 'Typhoon': inv_power = eut.measurements().get('W') timeout = 120. if inv_power <= p_rated * 0.85: pv.irradiance_set( 995) # Perturb the pv slightly to start the inverter ts.sleep(3) eut.connect(params={'Conn': True}) while inv_power <= p_rated * 0.85 and timeout >= 0: ts.log( 'Inverter power is at %0.1f. Waiting up to %s more seconds or until EUT starts...' % (inv_power, timeout)) ts.sleep(1) timeout -= 1 inv_power = eut.measurements().get('W') if timeout == 0: result = script.RESULT_FAIL raise der.DERError('Inverter did not start.') ts.log('Waiting for EUT to ramp up') ts.sleep(8) ts.log_debug('DAS data_read(): %s' % daq.data_read()) ''' b) Set all AC test source parameters to the nominal operating voltage and frequency. ''' grid = gridsim.gridsim_init( ts) # Turn on AC so the EUT can be initialized if grid is not None: # for HIL-based gridsim objects, link the chil parameters to voltage/frequency simulink parameters if callable(getattr(grid, "gridsim_info", None)): if grid.gridsim_info()['mode'] == 'Opal': grid.config(hil_object=chil) grid.voltage(v_nom) # open result summary file result_summary_filename = 'result_summary.csv' result_summary = open(ts.result_file_path(result_summary_filename), 'a+') ts.result_file(result_summary_filename) result_summary.write(lib_1547.get_rslt_sum_col_name()) ''' c) Set all EUT parameters to the rated active power conditions for the EUT. ''' if pv is not None: pv.iv_curve_config(pmp=p_rated, vmp=v_in_nom) pv.irradiance_set(1000.) ts.log('Waiting for EUT to power up. Sleeping 30 sec.') ts.sleep(30) ''' d) Set all voltage trip parameters to default settings. ''' try: eut.vrt_stay_connected_high( params={ 'Ena': True, 'ActCrv': 0, 'Tms1': 3000, 'V1': v_high, 'Tms2': 0.16, 'V2': v_high }) except Exception, e: ts.log_error('Could not set VRT Stay Connected High curve. %s' % e) try: eut.vrt_stay_connected_low( params={ 'Ena': True, 'ActCrv': 0, 'Tms1': 3000, 'V1': v_low, 'Tms2': 0.16, 'V2': v_low }) except Exception, e: ts.log_error('Could not set VRT Stay Connected Low curve. %s' % e)
def volt_watt_mode(vw_curves, vw_response_time, pwr_lvls): result = script.RESULT_FAIL daq = None data = None p_rated = None v_nom = None grid = None pv = None eut = None chil = None result_summary = None dataset_filename = None try: p_rated = ts.param_value('eut.p_rated') s_rated = ts.param_value('eut.s_rated') # DC voltages v_in_nom = ts.param_value('eut.v_in_nom') v_min_in = ts.param_value('eut.v_in_min') v_max_in = ts.param_value('eut.v_in_max') # AC voltages v_nom = ts.param_value('eut.v_nom') v_min = ts.param_value('eut.v_low') v_max = ts.param_value('eut.v_high') p_min = ts.param_value('eut.p_min') phases = ts.param_value('eut.phases') # EUI Absorb capabilities absorb = {} absorb['ena'] = ts.param_value('eut_vw.sink_power') absorb['p_rated_prime'] = ts.param_value('eut_vw.p_rated_prime') absorb['p_min_prime'] = ts.param_value('eut_vw.p_min_prime') """ Version validation """ p1547.VersionValidation(script_version=ts.info.version) """ A separate module has been create for the 1547.1 Standard """ ActiveFunction = p1547.ActiveFunction(ts=ts, functions=[VW], script_name='Volt-Watt', criteria_mode=[True, True, True]) ts.log_debug("1547.1 Library configured for %s" % ActiveFunction.get_script_name()) # result params result_params = ActiveFunction.get_rslt_param_plot() ''' a) Connect the EUT according to the instructions and specifications provided by the manufacturer. ''' ts.log_debug(15 * "*" + "HIL initialization" + 15 * "*") # initialize HIL environment, if necessary chil = hil.hil_init(ts) if chil is not None: chil.config() ts.log_debug(15 * "*" + "PVSIM initialization" + 15 * "*") # pv simulator is initialized with test parameters and enabled pv = pvsim.pvsim_init(ts) if pv is not None: pv.power_set(p_rated) pv.power_on() # Turn on DC so the EUT can be initialized ts.log_debug(15 * "*" + "DAS initialization" + 15 * "*") # DAS soft channels #das_points = {'sc': ('P_TARGET', 'P_TARGET_MIN', 'P_TARGET_MAX', 'P_MEAS', 'V_TARGET','V_MEAS','event')} das_points = ActiveFunction.get_sc_points() # initialize data acquisition system das_points = ActiveFunction.get_sc_points() daq = das.das_init(ts, sc_points=das_points['sc'], support_interfaces={'hil': chil}) if daq is not None: daq.sc['P_TARGET'] = p_rated daq.sc['P_TARGET_MIN'] = 100 daq.sc['P_TARGET_MAX'] = 100 daq.sc['V_TARGET'] = v_nom daq.sc['event'] = 'None' ts.log('DAS device: %s' % daq.info()) ''' b) Set all voltage trip parameters to the widest range of adjustability. Disable all reactive/active power control functions. ''' ts.log_debug(15 * "*" + "EUT initialization" + 15 * "*") eut = der.der_init(ts, support_interfaces={'hil': chil}) if eut is not None: eut.config() #Disable all functions on EUT eut.deactivate_all_fct() ts.log_debug(eut.measurements()) ts.log_debug( 'L/HVRT and trip parameters set to the widest range : v_min: {0} V, v_max: {1} V' .format(v_min, v_max)) try: eut.vrt_stay_connected_high( params={ 'Ena': True, 'ActCrv': 0, 'Tms1': 3000, 'V1': v_max, 'Tms2': 0.16, 'V2': v_max }) except Exception as e: ts.log_error( 'Could not set VRT Stay Connected High curve. %s' % e) try: eut.vrt_stay_connected_low( params={ 'Ena': True, 'ActCrv': 0, 'Tms1': 3000, 'V1': v_min, 'Tms2': 0.16, 'V2': v_min }) except Exception as e: ts.log_error('Could not set VRT Stay Connected Low curve. %s' % e) else: ts.log_debug( 'Set L/HVRT and trip parameters set to the widest range of adjustability possible.' ) ''' c) Set all AC test source parameters to the nominal operating voltage and frequency. ''' ts.log_debug(15 * "*" + "GRIDSIM initialization" + 15 * "*") # grid simulator is initialized with test parameters and enabled grid = gridsim.gridsim_init(ts, support_interfaces={ 'hil': chil }) # Turn on AC so the EUT can be initialized if grid is not None: grid.voltage(v_nom) # open result summary file result_summary_filename = 'result_summary.csv' result_summary = open(ts.result_file_path(result_summary_filename), 'a+') ts.result_file(result_summary_filename) result_summary.write(ActiveFunction.get_rslt_sum_col_name()) ''' v) Test may be repeated for EUT's that can also absorb power using the P' values in the characteristic definition. ''' # TODO: add P' tests (Like CPF -> for absorb_power in absorb_powers:) ''' u) Repeat steps d) through u) for characteristics 2 and 3. ''' for vw_curve in vw_curves: ts.log('Starting test with characteristic curve %s' % (vw_curve)) ActiveFunction.reset_curve(vw_curve) ActiveFunction.reset_time_settings(tr=vw_response_time[vw_curve], number_tr=2) v_pairs = ActiveFunction.get_params(curve=vw_curve, function=VW) ''' t) Repeat steps d) through t) at EUT power set at 20% and 66% of rated power. ''' for power in pwr_lvls: ActiveFunction.reset_pwr(pwr=power) ''' d) Adjust the EUT's available active power to Prated. For an EUT with an input voltage range, set the input voltage to Vin_nom. The EUT may limit active power throughout the test to meet reactive power requirements. For an EUT with an input voltage range. ''' if pv is not None: pv_power_setting = (p_rated * power) pv.iv_curve_config(pmp=pv_power_setting, vmp=v_in_nom) pv.irradiance_set(1000.) # Special considerations for CHIL ASGC/Typhoon startup # if chil is not None: if eut is not None: if eut.measurements() is not None: inv_power = eut.measurements().get('W') timeout = 120. if inv_power <= pv_power_setting * 0.85: pv.irradiance_set( 995 ) # Perturb the pv slightly to start the inverter ts.sleep(3) eut.connect(params={'Conn': True}) while inv_power <= pv_power_setting * 0.85 and timeout >= 0: ts.log( 'Inverter power is at %0.1f. Waiting up to %s more seconds or until EUT starts...' % (inv_power, timeout)) ts.sleep(1) timeout -= 1 inv_power = eut.measurements().get('W') if timeout == 0: result = script.RESULT_FAIL raise der.DERError( 'Inverter did not start.') ts.log('Waiting for EUT to ramp up') ts.sleep(8) ''' e) Set EUT volt-watt parameters to the values specified by Characteristic 1. All other functions should be turned off. ''' if eut is not None: vw_curve_params = { 'v': [v_pairs['V1'] / v_nom, v_pairs['V2'] / v_nom], 'w': [v_pairs['P1'] / p_rated, v_pairs['P2'] / p_rated], 'DeptRef': 'W_MAX_PCT', "RmpTms": vw_response_time[vw_curve] } vw_params = { 'Ena': True, 'ActCrv': 1, 'curve': vw_curve_params } ts.log_debug('Writing the following params to EUT: %s' % vw_params) eut.volt_watt(params=vw_params) ''' f) Verify volt-watt mode is reported as active and that the correct characteristic is reported. ''' ts.log_debug('Initial EUT VW settings are %s' % eut.volt_watt()) if chil is not None: ts.log('Start simulation of CHIL') chil.start_simulation() ''' Refer to P1547 Library and IEEE1547.1 standard for steps ''' v_steps_dict = ActiveFunction.create_vw_dict_steps() # Configure the data acquisition system ts.log('Starting data capture for power = %s' % power) dataset_filename = ('VW_{0}_PWR_{1}'.format(vw_curve, power)) ActiveFunction.reset_filename(filename=dataset_filename) ts.log('------------{}------------'.format(dataset_filename)) daq.data_capture(True) for step_label, v_step in v_steps_dict.items(): ts.log( 'Voltage step: setting Grid simulator voltage to %s (%s)' % (v_step, step_label)) ActiveFunction.start(daq=daq, step_label=step_label) step_dict = {'V': v_step} if grid is not None: grid.voltage(step_dict['V']) ActiveFunction.record_timeresponse(daq=daq) ActiveFunction.evaluate_criterias(daq=daq, step_dict=step_dict) result_summary.write(ActiveFunction.write_rslt_sum()) # create result workbook ts.log('Sampling complete') dataset_filename = dataset_filename + ".csv" daq.data_capture(False) ds = daq.data_capture_dataset() ts.log('Saving file: %s' % dataset_filename) ds.to_csv(ts.result_file_path(dataset_filename)) result_params['plot.title'] = dataset_filename.split('.csv')[0] ts.result_file(dataset_filename, params=result_params) result = script.RESULT_COMPLETE except script.ScriptFail as e: reason = str(e) if reason: ts.log_error(reason) except Exception as e: if dataset_filename is not None: dataset_filename = dataset_filename + ".csv" daq.data_capture(False) ds = daq.data_capture_dataset() ts.log('Saving file: %s' % dataset_filename) ds.to_csv(ts.result_file_path(dataset_filename)) result_params['plot.title'] = dataset_filename.split('.csv')[0] ts.result_file(dataset_filename, params=result_params) ts.log_error('Test script exception: %s' % traceback.format_exc()) raise finally: if daq is not None: daq.close() if pv is not None: if p_rated is not None: pv.power_set(p_rated) pv.close() if grid is not None: if v_nom is not None: grid.voltage(v_nom) grid.close() if chil is not None: chil.close() if eut is not None: eut.close() if result_summary is not None: result_summary.close() return result
def volt_watt_mode_imbalanced_grid(imbalance_resp, vw_curves, vw_response_time): result = script.RESULT_FAIL daq = None p_rated = None grid = None pv = None eut = None chil = None result_summary = None try: cat = ts.param_value('eut.cat') cat2 = ts.param_value('eut.cat2') p_rated = ts.param_value('eut.p_rated') s_rated = ts.param_value('eut.s_rated') # DC voltages v_in_nom = ts.param_value('eut.v_in_nom') # AC voltages v_nom = ts.param_value('eut.v_nom') v_min = ts.param_value('eut.v_low') v_max = ts.param_value('eut.v_high') p_min = ts.param_value('eut.p_min') phases = ts.param_value('eut.phases') imbalance_fix = ts.param_value('vw.imbalance_fix') # EUI Absorb capabilities absorb = {} absorb['ena'] = ts.param_value('eut_vw.sink_power') absorb['p_rated_prime'] = ts.param_value('eut_vw.p_rated_prime') absorb['p_min_prime'] = ts.param_value('eut_vw.p_min_prime') """ A separate module has been create for the 1547.1 Standard """ ActiveFunction = p1547.ActiveFunction(ts=ts, functions=[VW], script_name='Volt-Watt', criteria_mode=[True, True, True]) ts.log_debug('1547.1 Library configured for %s' % ActiveFunction.get_script_name()) ActiveFunction.set_imbalance_config(imbalance_angle_fix=imbalance_fix) ts.log_debug('1547.1 Library configured for %s' % ActiveFunction.get_script_name()) ''' a) Connect the EUT according to the instructions and specifications provided by the manufacturer. ''' ts.log_debug(15 * "*" + "HIL initialization" + 15 * "*") # initialize HIL environment, if necessary chil = hil.hil_init(ts) if chil is not None: chil.config() ts.log_debug(15 * "*" + "GRIDSIM initialization" + 15 * "*") # grid simulator is initialized with test parameters and enabled grid = gridsim.gridsim_init(ts, support_interfaces={ 'hil': chil }) # Turn on AC so the EUT can be initialized if grid is not None: grid.voltage(v_nom) ts.log_debug(15 * "*" + "PVSIM initialization" + 15 * "*") # pv simulator is initialized with test parameters and enabled pv = pvsim.pvsim_init(ts) if pv is not None: pv.power_set(p_rated) pv.power_on() # Turn on DC so the EUT can be initialized ts.log_debug(15 * "*" + "DAS initialization" + 15 * "*") # DAS soft channels #das_points = {'sc': ('P_TARGET', 'P_TARGET_MIN', 'P_TARGET_MAX', 'P_MEAS', 'V_TARGET', 'V_MEAS', 'event')} das_points = ActiveFunction.get_sc_points() # initialize data acquisition system daq = das.das_init(ts, sc_points=das_points['sc'], support_interfaces={'hil': chil}) if daq is not None: daq.sc['P_TARGET'] = p_rated daq.sc['P_TARGET_MIN'] = 100 daq.sc['P_TARGET_MAX'] = 100 daq.sc['V_TARGET'] = v_nom daq.sc['event'] = 'None' ts.log('DAS device: %s' % daq.info()) ''' b) Set all voltage trip parameters to the widest range of adjustability. Disable all reactive/active power control functions. ''' ts.log_debug(15 * "*" + "EUT initialization" + 15 * "*") eut = der.der_init(ts, support_interfaces={'hil': chil}) if eut is not None: eut.config() #Disable all functions on EUT #eut.deactivate_all_fct() ts.log_debug(eut.measurements()) ts.log_debug( 'L/HVRT and trip parameters set to the widest range : v_min: {0} V, v_max: {1} V' .format(v_min, v_max)) try: eut.vrt_stay_connected_high( params={ 'Ena': True, 'ActCrv': 0, 'Tms1': 3000, 'V1': v_max, 'Tms2': 0.16, 'V2': v_max }) except Exception as e: ts.log_error( 'Could not set VRT Stay Connected High curve. %s' % e) try: eut.vrt_stay_connected_low( params={ 'Ena': True, 'ActCrv': 0, 'Tms1': 3000, 'V1': v_min, 'Tms2': 0.16, 'V2': v_min }) except Exception as e: ts.log_error('Could not set VRT Stay Connected Low curve. %s' % e) else: ts.log_debug( 'Set L/HVRT and trip parameters set to the widest range of adjustability possible.' ) # Special considerations for CHIL ASGC/Typhoon startup if chil is not None: if eut is not None: inv_power = eut.measurements().get('W') timeout = 120. if inv_power <= p_rated * 0.85: pv.irradiance_set( 995) # Perturb the pv slightly to start the inverter ts.sleep(3) eut.connect(params={'Conn': True}) while inv_power <= p_rated * 0.85 and timeout >= 0: ts.log( 'Inverter power is at %0.1f. Waiting up to %s more seconds or until EUT starts...' % (inv_power, timeout)) ts.sleep(1) timeout -= 1 inv_power = eut.measurements().get('W') if timeout == 0: result = script.RESULT_FAIL raise der.DERError('Inverter did not start.') ts.log('Waiting for EUT to ramp up') ts.sleep(8) ''' c) Set all AC test source parameters to the nominal operating voltage and frequency. ''' if grid is not None: grid.voltage(v_nom) # open result summary file result_summary_filename = 'result_summary.csv' result_summary = open(ts.result_file_path(result_summary_filename), 'a+') ts.result_file(result_summary_filename) result_summary.write(ActiveFunction.get_rslt_sum_col_name()) ''' d) Adjust the EUT's available active power to Prated. For an EUT with an input voltage range, set the input voltage to Vin_nom. ''' if pv is not None: pv.iv_curve_config(pmp=p_rated, vmp=v_in_nom) pv.irradiance_set(1000.) for imbalance_response in imbalance_resp: for vw_curve in vw_curves: ''' e) Set EUT volt-watt parameters to the values specified by Characteristic 1. All other function be turned off. ''' if eut is not None: #eut.deactivate_all_fct() pass ts.log('Starting test with characteristic curve %s' % (vw_curve)) ActiveFunction.reset_curve(vw_curve) ActiveFunction.reset_time_settings( tr=vw_response_time[vw_curve], number_tr=2) v_pairs = ActiveFunction.get_params(curve=vw_curve, function=VW) # it is assumed the EUT is on eut = der.der_init(ts) if eut is not None: vw_curve_params = { 'v': [ round(v_pairs['V1'] * (v_nom), 2), round(v_pairs['V2'] * (v_nom), 2) ], 'w': [ round(v_pairs['P1'] * (p_rated), 2), round(v_pairs['P2'] * (p_rated), 2) ], 'DeptRef': 'W_MAX_PCT' } vw_params = { 'Ena': True, 'ActCrv': 1, 'curve': vw_curve_params } ''' f) Verify volt-watt mode is reported as active and that the correct characteristic is reported ''' eut.volt_watt(params=vw_params) ts.log_debug('Initial EUT VW settings are %s' % eut.volt_watt()) ts.log_debug('curve points: %s' % v_pairs) ''' g) Once steady state is reached, begin the adjustment of phase voltages. ''' """ Test start """ ActiveFunction.set_step_label('G') daq.sc['event'] = ActiveFunction.get_step_label() daq.data_sample() ts.log('Wait for steady state to be reached') ts.sleep(2 * vw_response_time[vw_curve]) ts.log('Starting imbalance test with VW mode at %s (%s)' % (imbalance_response, imbalance_fix)) dataset_filename = 'VW_IMB_%s_%s' % (imbalance_response, imbalance_fix) ts.log('------------{}------------'.format(dataset_filename)) # Start the data acquisition systems daq.data_capture(True) ''' Step h) For multiphase units, step the AC test source voltage to Case A from Table 24 ''' if grid is not None: step_label = ActiveFunction.get_step_label() ts.log( 'Voltage step: setting Grid simulator to case A (IEEE 1547.1-Table 24)(%s)' % step_label) ActiveFunction.start(daq=daq, step_label=step_label) v_target = ActiveFunction.set_grid_asymmetric( grid=grid, case='case_a') step_dict = {'V': v_target} ActiveFunction.record_timeresponse(daq=daq) ActiveFunction.evaluate_criterias(daq=daq, step_dict=step_dict) result_summary.write(ActiveFunction.write_rslt_sum()) ''' Step i) For multiphase units, step the AC test source voltage to VN. ''' if grid is not None: step_label = ActiveFunction.get_step_label() ts.log( 'Voltage step: setting Grid simulator voltage to %s (%s)' % (v_nom, step_label)) ActiveFunction.start(daq=daq, step_label=step_label) v_target = v_nom grid.voltage(v_target) step_dict = {'V': v_target} ActiveFunction.record_timeresponse(daq=daq) ActiveFunction.evaluate_criterias(daq=daq, step_dict=step_dict) result_summary.write(ActiveFunction.write_rslt_sum()) ''' Step j) For multiphase units, step the AC test source voltage to Case B from Table 24 ''' if grid is not None: step_label = ActiveFunction.get_step_label() ts.log( 'Voltage step: setting Grid simulator to case B (IEEE 1547.1-Table 24)(%s)' % step_label) ActiveFunction.start(daq=daq, step_label=step_label) v_target = ActiveFunction.set_grid_asymmetric( grid=grid, case='case_b') step_dict = {'V': v_target} ActiveFunction.record_timeresponse(daq=daq) ActiveFunction.evaluate_criterias(daq=daq, step_dict=step_dict) result_summary.write(ActiveFunction.write_rslt_sum()) ''' Step k) For multiphase units, step the AC test source voltage to VN. ''' if grid is not None: step_label = ActiveFunction.get_step_label() ts.log( 'Voltage step: setting Grid simulator voltage to %s (%s)' % (v_nom, step_label)) ActiveFunction.start(daq=daq, step_label=step_label) v_target = v_nom grid.voltage(v_target) step_dict = {'V': v_target} ActiveFunction.record_timeresponse(daq=daq) ActiveFunction.evaluate_criterias(daq=daq, step_dict=step_dict) result_summary.write(ActiveFunction.write_rslt_sum()) # Get the rslt parameters for plot result_params = ActiveFunction.get_rslt_param_plot() ts.log('Sampling complete') dataset_filename = dataset_filename + ".csv" daq.data_capture(False) ds = daq.data_capture_dataset() ts.log('Saving file: %s' % dataset_filename) ds.to_csv(ts.result_file_path(dataset_filename)) result_params['plot.title'] = dataset_filename.split('.csv')[0] ts.result_file(dataset_filename, params=result_params) result = script.RESULT_COMPLETE except script.ScriptFail as e: reason = str(e) if reason: ts.log_error(reason) finally: if daq is not None: daq.close() if pv is not None: if p_rated is not None: pv.power_set(p_rated) pv.close() if grid is not None: if v_nom is not None: grid.voltage(v_nom) grid.close() if chil is not None: chil.close() if eut is not None: eut.volt_var(params={'Ena': False}) eut.volt_watt(params={'Ena': False}) eut.close() if result_summary is not None: result_summary.close() return result
def test_run(): result = script.RESULT_FAIL daq = None data = None trigger = None grid = None pv = None eut = None chil = None rs = None try: # initialize hardware-in-the-loop environment (if applicable) ts.log('Configuring HIL system...') chil = hil.hil_init(ts) if chil is not None: chil.config() # initialize grid simulator grid = gridsim.gridsim_init(ts) # initialize pv simulator pv = pvsim.pvsim_init(ts) p_rated = ts.param_value('fw.p_rated') pv.power_set(p_rated) pv.power_on() # power on at p_rated # initialize data acquisition system daq = das.das_init(ts, sc_points=das_points['sc']) daq.sc['P_target_pct'] = 100 daq.sc['P_min_pct'] = 100 daq.sc['P_max_pct'] = 100 daq.sc['eval_flag'] = 0 # Configure the EUT communications eut = der.der_init(ts) eut.config() ts.log_debug(eut.measurements()) ts.log_debug( 'Set L/HFRT and trip parameters to the widest range of adjustability possible.' ) fw_mode = ts.param_value('fw.fw_mode') f_nom = ts.param_value('fw.f_nom') f_min = ts.param_value('fw.f_min') f_max = ts.param_value('fw.f_max') MSAHz = ts.param_value('fw.MSAHz') MSAP = ts.param_value('fw.MSAP') t_settling = ts.param_value('fw.ts') fstart_min = ts.param_value('fw.fstart_min') fstart_max = ts.param_value('fw.fstart_max') k_pf_min = ts.param_value('fw.k_pf_min') k_pf_max = ts.param_value('fw.k_pf_max') k_pf = ts.param_value('fw.kpf') # %Prated/Hz n_points = ts.param_value('test.n_points') irr = ts.param_value('test.irr') n_iterations = ts.param_value('test.n_iter') curves = ts.param_value('test.curves') if curves == 'Both': fw_curves = [1, 2] elif curves == 'Charactistic Curve 1': fw_curves = [1] else: # Charactistic Curve 2 fw_curves = [2] if irr == 'All': pv_powers = [1., 0.66, 0.33] elif irr == '100%': pv_powers = [1.] elif irr == '66%': pv_powers = [0.66] else: #Charactistic Curve 2 pv_powers = [0.33] # open result summary file result_summary_filename = 'result_summary.csv' result_summary = open(ts.result_file_path(result_summary_filename), 'a+') ts.result_file(result_summary_filename) result_summary.write( 'Result, Test Name, Power Level, Iteration, direction, ' 'Freq, Power, P_min, P_max, Dataset File\n') for fw_curve in fw_curves: if fw_curve == 1: # characteristic curve 1 hz_stop = fstart_max + 100. / k_pf_max hz_start = fstart_min else: # characteristic curve 2 hz_stop = fstart_min + 100. / k_pf_min hz_start = fstart_max if fw_mode == 'Parameters': eut.freq_watt_param( params={ 'HysEna': False, 'HzStr': hz_start, 'HzStop': hz_stop, 'WGra': k_pf_min }) else: # pointwise eut.freq_watt(params={'ActCrv': 1}) parameters = { 'hz': [fstart_min, fstart_min, hz_stop, hz_stop], 'w': [100, 100, 0, 0] } ts.log_debug(parameters) eut.freq_watt_curve(id=1, params=parameters) eut.freq_watt(params={'Ena': True}) ts.log_debug(eut.freq_watt()) # start and stop frequencies for the grid simulator steps f_start = fstart_min f_end = f_max - MSAHz for power in pv_powers: pv.power_set(p_rated * power) for n_iter in range(n_iterations): # SA14.3.2(d) and (e) daq.data_capture(True) f_steps = list(np.linspace(f_start, f_end, n_points)) + \ list(np.linspace(f_end, f_start, n_points)) + [49.0] filename = 'FW_curve_%s_power=%0.2f_iter=%s.csv' % ( fw_curve, power, n_iter + 1) step_count = 0 for f_step in f_steps: step_count += 1 grid.freq(f_step) daq.sc['freq_set'] = f_step ts.log( ' Recording power at frequency %0.3f Hz for 2*t_settling = %0.1f sec.' % (f_step, 2 * t_settling)) p_targ = p_target(f_step, f_nom, hz_start, hz_stop) daq.sc['P_target_pct'] = p_targ daq.sc['P_min_pct'] = p_targ - (MSAP / p_rated) * 100. daq.sc['P_max_pct'] = p_targ + (MSAP / p_rated) * 100. ts.sleep(t_settling) daq.sc[ 'eval_flag'] = 1 # flag the time in which the power will be analyzed, see Figure SA14.3 daq.data_capture() ts.sleep( t_settling * 1.5 ) # This time period will be analyzed for pass/fail criteria data = daq.data_capture_read() ts.log_debug( 'Powers targ, min, max: %s, %s, %s' % (p_targ, daq.sc['P_min_pct'], daq.sc['P_max_pct'])) ts.log_debug('Powers are: %s, %s, %s' % (data.get('AC_P_1'), data.get('AC_P_2'), data.get('AC_P_3'))) AC_W = data.get('AC_P_1') + data.get( 'AC_P_2') + data.get('AC_P_3') AC_W_pct = (AC_W / p_rated) * 100. if daq.sc['P_min_pct'] <= AC_W_pct <= daq.sc[ 'P_max_pct']: passfail = 'Pass' else: passfail = 'Fail' if step_count <= n_points: direction = 'up' else: direction = 'down' result_summary.write( '%s, %s, %s, %s, %s, %s, %s, %s, %s, %s \n' % (passfail, ts.config_name(), power * 100., n_iter + 1, direction, f_step, AC_W_pct, daq.sc['P_min_pct'], daq.sc['P_max_pct'], filename)) daq.sc['eval_flag'] = 0 daq.data_capture() daq.data_capture(False) ds = daq.data_capture_dataset() ts.log('Saving file: %s' % filename) ds.to_csv(ts.result_file_path(filename)) ts.result_file(filename) result = script.RESULT_COMPLETE except script.ScriptFail, e: reason = str(e) if reason: ts.log_error(reason)
def test_run(): result = script.RESULT_FAIL # Variables use in script daq = None data = None grid = None pv = None eut = None chil = None result_summary = None dataset_filename = None fw_curves = [] fw_response_time = [0, 0, 0] f_steps_dic = {} try: """ Test Configuration """ # Get all test script parameter mode = ts.param_value("fw.mode") eut_absorb = ts.param_value("eut_fw.absorb") """ A separate module has been create for the 1547.1 Standard """ lib_1547 = p1547.module_1547(ts=ts, aif='FW') ts.log_debug("1547.1 Library configured for %s" % lib_1547.get_test_name()) if eut_absorb == "Yes": absorb_powers = [False, True] else: absorb_powers = [False] p_rated = ts.param_value('eut.p_rated') s_rated = ts.param_value('eut.s_rated') # DC voltages v_nom_in = ts.param_value('eut.v_in_nom') irr = ts.param_value('fw.power_lvl') if mode == 'Above': if irr == 'All': pwr_lvls = [1., 0.66, 0.2] elif irr == '100%': pwr_lvls = [1.] elif irr == '66%': pwr_lvls = [0.66] elif irr == '20%': pwr_lvls = [0.2] else: pwr_lvls = [1.] # AC voltages f_nom = ts.param_value('eut.f_nom') f_min = ts.param_value('eut.f_min') f_max = ts.param_value('eut.f_max') p_min = ts.param_value('eut.p_min') phases = ts.param_value('eut.phases') # EUI FW parameters absorb_enable = ts.param_value('eut_fw.sink_power') p_rated_prime = ts.param_value('eut_fw.p_rated_prime') p_min_prime = ts.param_value('eut_fw.p_min_prime') p_small = ts.param_value('eut_fw.p_small') if ts.param_value('fw.test_1') == 'Enabled': fw_curves.append(1) fw_response_time[1] = float(ts.param_value('fw.test_1_tr')) if ts.param_value('fw.test_2') == 'Enabled': fw_curves.append(2) fw_response_time[2] = float(ts.param_value('fw.test_2_tr')) ''' a) Connect the EUT according to the instructions and specifications provided by the manufacturer. ''' # initialize HIL environment, if necessary chil = hil.hil_init(ts) if chil is not None: chil.config() # DAS soft channels # TODO : add to library 1547 #das_points = {'sc': ('P_TARGET', 'P_TARGET_MIN', 'P_TARGET_MAX', 'P_MEAS', 'F_TARGET', 'F_MEAS', 'event')} das_points = lib_1547.get_sc_points() # initialize data acquisition system daq = das.das_init(ts, sc_points=das_points['sc']) if daq is not None: daq.sc['P_TARGET'] = 100 daq.sc['P_TARGET_MIN'] = 100 daq.sc['P_TARGET_MAX'] = 100 daq.sc['P_MEAS'] = 100 daq.sc['F_TARGET'] = f_nom daq.sc['P_TARGET'] = p_rated daq.sc['event'] = 'None' ts.log('DAS device: %s' % daq.info()) # Configure the EUT communications eut = der.der_init(ts) ''' b) Set all frequency trip parameters to the widest range of adjustability. Disable all reactive/active power control functions. ''' if eut is not None: eut.config() #eut.deactivate_all_fct(act_fct='FW') ts.log_debug(eut.measurements()) ts.log_debug( 'L/HFRT and trip parameters set to the widest range : f_min:{0} Hz, f_max:{1} Hz' .format(f_min, f_max)) eut_response = eut.frt_stay_connected_high( params={ 'Ena': True, 'ActCrv': 0, 'Tms1': 3000, 'Hz1': f_max, 'Tms2': 160, 'Hz2': f_max }) ts.log_debug( 'HFRT and trip parameters from EUT : {}'.format(eut_response)) eut_response = eut.frt_stay_connected_low( params={ 'Ena': True, 'ActCrv': 0, 'Tms1': 3000, 'Hz1': f_min, 'Tms2': 160, 'Hz2': f_min }) ts.log_debug( 'LFRT and trip parameters from EUT : {}'.format(eut_response)) else: ts.log_debug( 'Set L/HFRT and trip parameters to the widest range of adjustability possible.' ) ''' c) Set all AC test source parameters to the nominal operating voltage and frequency ''' grid = gridsim.gridsim_init(ts) if grid is not None: grid.freq(f_nom) if mode == 'Below': # 1547.1 : Frequency is ramped at the ROCOF for the category of the EUT. # In this case the ROCOF is based on table 21 of 1547.2018 # (Category III is use because of table B.1 of 1547.2018) # The ROCOF unit : Hz/s ts.log('Set Grid simulator ROCOF to 3 Hz/s') grid.rocof(3.0) # result params result_params = lib_1547.get_rslt_param_plot() # open result summary file result_summary_filename = 'result_summary.csv' result_summary = open(ts.result_file_path(result_summary_filename), 'a+') ts.result_file(result_summary_filename) result_summary.write(lib_1547.get_rslt_sum_col_name()) ''' above_d) Adjust the EUT's available active power to Prated . below_d) "" "" "". Set the EUT's output power to 50% of P rated . ''' pv = pvsim.pvsim_init(ts) if pv is not None: pv.iv_curve_config(pmp=p_rated, vmp=v_nom_in) pv.irradiance_set(1000.) if mode == 'Below': if eut is not None: ts.log_debug( "In Below mode, EUT's output power is set to 50%% of %s (Prated)" % p_rated) eut.limit_max_power(params={ 'MaxLimWEna': True, 'MaxLimW_PCT': 50 }) """ Test start """ ''' above_r) For EUT's that can absorb power, rerun Characteristic 1 allowing the unit to absorb power by programing a negative Pmin . below_p) "" "" "". Set the unit to absorb power at -50% of P rated . ''' for absorb_power in absorb_powers: if absorb_power: if eut is not None: if mode == 'Below': ts.log_debug( "Config EUT's absorb power at -50%% of P rated") eut.limit_max_power( params={ 'MaxLimWEna': True, 'MaxLimW_PCT': 50 #'MaxLimW': round(p_rated / 2.0, 2) * -1 }) else: ts.log_debug( "Config EUT's absorb power to %s (P\'min)" % p_min_prime) eut.limit_max_power(params={ 'MaxLimWEna': True, 'MaxLimW': p_min_prime }) ''' above_q) Repeat steps b) through p) for Characteristic 2. below_o) "" "" "" ''' for fw_curve in fw_curves: ts.log('Starting test with characteristic curve %s' % (fw_curve)) fw_param = lib_1547.get_params(curve=fw_curve) a_f = lib_1547.MRA_F * 1.5 lib_1547.set_step_label(starting_label='G') f_steps_dic[mode] = collections.OrderedDict() if mode == 'Above': # 1547.1 (5.15.2.2): f_steps_dic[mode][lib_1547.get_step_label()] = ( f_nom + fw_param['dbf']) + a_f f_steps_dic[mode][lib_1547.get_step_label()] = ( f_nom + fw_param['dbf']) - a_f f_steps_dic[mode][lib_1547.get_step_label()] = ( f_nom + fw_param['dbf']) + a_f f_steps_dic[mode][lib_1547.get_step_label( )] = fw_param['f_small'] + f_nom + fw_param['dbf'] # STD_CHANGE : step k) should consider the accuracy f_steps_dic[mode][lib_1547.get_step_label()] = f_max - a_f f_steps_dic[mode][lib_1547.get_step_label( )] = f_max - fw_param['f_small'] f_steps_dic[mode][lib_1547.get_step_label()] = ( f_nom + fw_param['dbf']) + a_f f_steps_dic[mode][lib_1547.get_step_label()] = ( f_nom + fw_param['dbf']) - a_f f_steps_dic[mode][lib_1547.get_step_label()] = f_nom for step, frequency in f_steps_dic[mode].iteritems(): f_steps_dic[mode].update( {step: np.around(frequency, 3)}) if frequency > f_max: ts.log( "{0} frequency step (value : {1}) changed to fH (f_max)" .format(step, frequency)) f_steps_dic[mode].update({step: f_max}) elif mode == 'Below': # 1547.1 (5.15.3.2): f_steps_dic[mode][lib_1547.get_step_label()] = ( f_nom + fw_param['dbf']) - a_f f_steps_dic[mode][lib_1547.get_step_label()] = ( f_nom - fw_param['dbf']) - a_f f_steps_dic[mode][lib_1547.get_step_label( )] = f_nom - fw_param['f_small'] - fw_param['dbf'] # STD_CHANGE : step j) should consider the accuracy f_steps_dic[mode][lib_1547.get_step_label()] = f_min + a_f f_steps_dic[mode][lib_1547.get_step_label( )] = f_min + fw_param['f_small'] f_steps_dic[mode][lib_1547.get_step_label()] = ( f_nom - fw_param['dbf']) - a_f f_steps_dic[mode][lib_1547.get_step_label()] = ( f_nom - fw_param['dbf']) + a_f f_steps_dic[mode][lib_1547.get_step_label()] = f_nom for step, frequency in f_steps_dic[mode].iteritems(): f_steps_dic[mode].update( {step: np.around(frequency, 3)}) if frequency < f_min: ts.log( "{0} frequency step (value : {1}) changed to fL (f_min)" .format(step, frequency)) f_steps_dic[mode].update({step: f_min}) ''' p) Repeat test steps b) through o) with the EUT power set at 20% and 66% of rated power. ''' for power in pwr_lvls: if pv is not None: pv_power_setting = (p_rated * power) pv.iv_curve_config(pmp=pv_power_setting, vmp=v_nom_in) pv.irradiance_set(1000.) ''' e) Set EUT freq-watt parameters to the values specified by Characteristic 1. All other functions should be turned off. ''' # STD_CHANGE : dbf and kf don't exist but dbof=dbuf and kof=kuf was the point of having two? if eut is not None: params = { 'Ena': True, 'curve': fw_curve, 'dbf': fw_param['dbf'], 'kof': fw_param['kof'], 'RspTms': fw_param['tr'] } ts.log_debug(params) settings = eut.freq_watt(params) ''' f) Verify freq-watt mode is reported as active and that the correct characteristic is reported. ''' ts.log_debug('Initial EUT FW settings are %s' % settings) ts.log_debug('Test parameters : %s' % fw_param) ts.log('Starting data capture for power = %s' % power) dataset_filename = 'FW_{0}_PWR_{1}_{2}'.format( fw_curve, power, mode) if absorb_power: dataset_filename = 'FW_{0}_PWR_{1}_{2}_ABSORB'.format( fw_curve, power, mode) ts.log( '------------{}------------'.format(dataset_filename)) ''' g) Once steady state is reached, read and record the EUT's active power, reactive power, voltage,frequency, and current measurements. ''' # STD_CHANGE there should be a wait for steady state to be reached in both mode step = 'Step F' daq.sc['event'] = step daq.data_sample() ts.log('Wait for steady state to be reached') ts.sleep(4 * fw_param['tr']) daq.data_capture(True) for step_label, f_step in f_steps_dic[mode].iteritems(): p_initial = lib_1547.get_initial_value(daq=daq, step=step_label) ts.log( 'Frequency step: setting Grid simulator frequency to %s (%s)' % (f_step, step_label)) step_dict = {'F': f_step} if grid is not None: grid.freq(f_step) lib_1547.process_data( daq=daq, tr=fw_param['tr'], step=step_label, initial_value=p_initial, x_target=step_dict, #y_target=None, #automatically calculated in p1547 curve=fw_curve, pwr_lvl=power, result_summary=result_summary, filename=dataset_filename) dataset_filename = dataset_filename + ".csv" daq.data_capture(False) ds = daq.data_capture_dataset() ts.log('Saving file: %s' % dataset_filename) ds.to_csv(ts.result_file_path(dataset_filename)) result_params['plot.title'] = os.path.splitext( dataset_filename)[0] ts.result_file(dataset_filename, params=result_params) result = script.RESULT_COMPLETE return result except script.ScriptFail, e: reason = str(e) if reason: ts.log_error(reason)
def test_run(): result = script.RESULT_FAIL grid = None pv = p_rated = None daq = None eut = None rs = None chil = None result_summary = None step = None q_initial = None dataset_filename = None try: cat = ts.param_value('eut.cat') cat2 = ts.param_value('eut.cat2') sink_power = ts.param_value('eut.sink_power') p_rated = ts.param_value('eut.p_rated') p_rated_prime = ts.param_value('eut.p_rated_prime') s_rated = ts.param_value('eut.s_rated') var_rated = ts.param_value('eut.var_rated') # DC voltages v_nom_in_enabled = ts.param_value('cpf.v_in_nom') v_min_in_enabled = ts.param_value('cpf.v_in_min') v_max_in_enabled = ts.param_value('cpf.v_in_max') v_nom_in = ts.param_value('eut.v_in_nom') v_min_in = ts.param_value('eut_cpf.v_in_min') v_max_in = ts.param_value('eut_cpf.v_in_max') # AC voltages v_nom = ts.param_value('eut.v_nom') v_min = ts.param_value('eut.v_low') v_max = ts.param_value('eut.v_high') f_nom = ts.param_value('eut.f_nom') f_min = ts.param_value('eut.f_min') f_max = ts.param_value('eut.f_max') p_min = ts.param_value('eut.p_min') p_min_prime = ts.param_value('eut.p_min_prime') phases = ts.param_value('eut.phases') # RT test parameters lf_ena = ts.param_value('frt.lf_ena') hf_ena = ts.param_value('frt.hf_ena') freq_response_time = ts.param_value('frt.response_time') n_iter = ts.param_value('frt.n_iter') pwr_lvl = ts.param_value('frt.pwr_value') # Pass/fail accuracies pf_msa = ts.param_value('eut.pf_msa') # EUI Absorb capabilities absorb = {} absorb['ena'] = ts.param_value('eut_cpf.sink_power') absorb['p_rated_prime'] = ts.param_value('eut_cpf.p_rated_prime') absorb['p_min_prime'] = ts.param_value('eut_cpf.p_min_prime') # Functions to be enabled for test mode = [] steps_dict = {} if lf_ena == 'Enabled': mode.append(LF) lf_value = ts.param_value('frt.lf_value') if not(isinstance(lf_value, float) or isinstance(lf_value, int)): #TODO if not numeric value inputed pass elif not (f_min <= lf_value <= 57.0): #TODO raise error if not between f_min and 57.0 raise ValueError steps_dict[LF] = {'location': f'C:/', 'value': lf_value} if hf_ena == 'Enabled': mode.append(HF) hf_value = ts.param_value('frt.hf_value') if not(isinstance(hf_value, float) or isinstance(hf_value, int)): #TODO if not numeric value inputed pass elif not 61.7 <= hf_value <= f_max: #TODO raise error if not between 63.0 and f_max raise ValueError steps_dict[HF] = {'location': f'C:/', 'value': hf_value} if not (isinstance(freq_response_time, float) or isinstance(freq_response_time, int)): # TODO if not numeric value inputed pass elif 299 > freq_response_time: # TODO timedelay must be over 299 sec raise ValueError time_step = {'location': f'C:....', 'value': freq_response_time } """ A separate module has been create for the 1547.1 Standard """ lib_1547 = p1547.module_1547(ts=ts, aif='FW', absorb=absorb) ts.log_debug("1547.1 Library configured for %s" % lib_1547.get_test_name()) # result params result_params = lib_1547.get_rslt_param_plot() ts.log(result_params) # initialize HIL environment, if necessary phil = hil.hil_init(ts) if phil is not None: phil.config() # grid simulator is initialized with test parameters and enabled grid = gridsim.gridsim_init(ts) # Turn on AC so the EUT can be initialized if grid is not None: grid.voltage(v_nom) # pv simulator is initialized with test parameters and enabled pv = pvsim.pvsim_init(ts) if pv is not None: pv.power_set(p_rated) pv.power_on() # Turn on DC so the EUT can be initialized # DAS soft channels das_points = lib_1547.get_sc_points() # initialize data acquisition daq = das.das_init(ts, sc_points=das_points['sc']) """ if daq is not None: daq.sc['V_MEAS'] = 100 daq.sc['P_MEAS'] = 100 daq.sc['Q_MEAS'] = 100 daq.sc['Q_TARGET_MIN'] = 100 daq.sc['Q_TARGET_MAX'] = 100 daq.sc['PF_TARGET'] = 1 daq.sc['event'] = 'None' ts.log('DAS device: %s' % daq.info()) """ """ a) Connect the EUT according to the instructions and specifications provided by the manufacturer. """ # it is assumed the EUT is on eut = der.der_init(ts) if eut is not None: eut.config() eut.deactivate_all_fct() # open result summary file result_summary_filename = 'result_summary.csv' result_summary = open(ts.result_file_path(result_summary_filename), 'a+') ts.result_file(result_summary_filename) result_summary.write(lib_1547.get_rslt_sum_col_name()) """ c) Set the frequency droop function and droop values to make the active power change with respect to frequency as small as possible. """ if eut is not None: default_curve = 1 fw_settings = lib_1547.get_params(aif=FW) fw_curve_params = { 'Ena': True, 'curve': default_curve, 'dbf': fw_settings[default_curve]['dbf'], 'kof': fw_settings[default_curve]['kof'], 'RspTms': fw_settings[default_curve]['tr'] } eut.freq_watt(fw_curve_params) ts.log_debug('Sending FW points: %s' % fw_curve_params) """ d) Set or verify that all frequency trip settings are set to not influence the outcome of the test. """ ts.log_debug('If not done already, set L/HVRT and trip parameters to the widest range of adjustability.') ts.log_debug(f'{mode}') #TODO Isolated Source mode?? for current_mode in mode: ts.log_debug(f'Initializing {current_mode}') # TODO FIND A WAY TO SET ROCOF """ e) Operate the ac test source at nominal frequency ± 0.1 Hz. """ if grid is not None: grid.voltage(v_nom) ts.log(f'Setting Grid simulator voltage to {v_nom}') grid.freq(f_nom) ts.log(f'Setting Grid simulator frequency to {f_nom}') for i in range(1, n_iter+1): ts.log_debug('Starting mode = %s and %s' % (current_mode, current_mode == VV)) daq.data_capture(True) dataset_filename = f'FRT_{current_mode}_{i}' ts.log('------------{}------------'.format(dataset_filename)) #step_label = lib_1547.set_step_label(starting_label='F') #step = lib_1547.get_step_label() """ f) Operate EUT at any convenient power level between 90% and 100% of EUT rating and at any convenient power factor. Record the output current of the EUT at the nominal frequency condition. """ if pv is not None: pv.iv_curve_config(pmp=p_rated, vmp=v_nom_in) pv.irradiance_set(1000.) pv.power_set(round(pwr_lvl*p_rated)) """ ***For High Frequency RT test mode*** g) Adjust the source frequency from PN to PU where fU is greater than or equal to 61.8 Hz. The source shall be held at this frequency for period th, which shall be not less than 299 s. """ # Set values for steps freq_step_location = steps_dict[current_mode]['location'] freq_step_value = steps_dict[current_mode]['value'] ts.log(f'Frequency step: setting Grid simulator frequency to {freq_step_value}Hz') ts.log(f'At frequency step location: {freq_step_location}') # Set timestep time_step_location = time_step['location'] time_step_value = time_step['value'] ts.log(f'Time setting: setting Grid simulator time setting to {time_step_value}sec') ts.log(f'At time step location: {time_step_location}') if phil is not None: #Send commands to HIL phil.set_params((freq_step_location, freq_step_value)) phil.set_params((time_step_location, time_step_value)) #TODO HIL SECTION TO BE COMPLETED ts.log('Stop time set to %s' % phil.set_stop_time(stop_time)) if compilation == 'Yes': ts.log(" Model ID: {}".format(phil.compile_model().get("modelId"))) if stop_sim == 'Yes': ts.log(" {}".format(phil.stop_simulation())) if load == 'Yes': ts.log(" {}".format(phil.load_model_on_hil())) if execute == 'Yes': ts.log(" {}".format(phil.start_simulation())) sim_time = phil.get_time() while (stop_time - sim_time) > 1.0: # final sleep will get to stop_time. sim_time = phil.get_time() ts.log('Sim Time: %s. Waiting another %s sec before saving data.' % ( sim_time, stop_time - sim_time)) ts.sleep(1) """ h) Decrease the frequency of the ac test source to the nominal frequency ± 0.1 Hz. """ if grid is not None: grid.freq(f_nom) ts.log(f'Frequency step: setting Grid simulator frequency to {f_nom}') """ i) Repeat steps f) and g) twice for a total of three tests. """ """ j) During all frequency transitions in steps f) through h), the ROCOF shall be greater than or equal to the ROCOF limit in Table 21 of IEEE Std 1547-2018 and shall be within the demonstrated ROCOF capability of the EUT. """ ts.log('Sampling complete') dataset_filename = dataset_filename + ".csv" daq.data_capture(False) ds = daq.data_capture_dataset() ts.log('Saving file: %s' % dataset_filename) ds.to_csv(ts.result_file_path(dataset_filename)) result_params['plot.title'] = dataset_filename.split('.csv')[0] ts.result_file(dataset_filename, params=result_params) result = script.RESULT_COMPLETE except script.ScriptFail as e: reason = str(e) if reason: ts.log_error(reason) except Exception as e: ts.log_error((e, traceback.format_exc())) if dataset_filename is not None: dataset_filename = dataset_filename + ".csv" daq.data_capture(False) ds = daq.data_capture_dataset() ts.log('Saving file: %s' % dataset_filename) ds.to_csv(ts.result_file_path(dataset_filename)) result_params['plot.title'] = dataset_filename.split('.csv')[0] ts.result_file(dataset_filename, params=result_params) ts.log_error('Test script exception: %s' % traceback.format_exc()) finally: if grid is not None: grid.close() if pv is not None: if p_rated is not None: pv.power_set(p_rated) pv.close() if daq is not None: daq.close() if eut is not None: eut.freq_watt(params={'Ena': False}) eut.close() if rs is not None: rs.close() if chil is not None: chil.close() if result_summary is not None: result_summary.close() # create result workbook excelfile = ts.config_name() + '.xlsx' rslt.result_workbook(excelfile, ts.results_dir(), ts.result_dir()) ts.result_file(excelfile) return result
def test_run(): result = script.RESULT_FAIL daq = None pv = None grid = None try: # read test parameters tests_param = ts.param_value('eut.tests') s_rated = ts.param_value('eut.s_rated') p_rated = ts.param_value('eut.p_rated') # v_dc_min = ts.param_value('eut.v_dc_min') ## # v_dc_max = ts.param_value('eut.v_dc_max') ## v_nom = ts.param_value('eut.v_nom') v_min = ts.param_value('eut.v_min') v_max = ts.param_value('eut.v_max') v_msa = ts.param_value('eut.v_msa') var_msa = ts.param_value('eut.var_msa') var_ramp_max = ts.param_value('eut.var_ramp_max') q_max_cap = ts.param_value('eut.q_max_cap') q_max_ind = ts.param_value('eut.q_max_ind') k_var_max = ts.param_value('eut.k_var_max') deadband_min = ts.param_value('eut.vv_deadband_min') deadband_max = ts.param_value('eut.vv_deadband_max') t_settling = ts.param_value('eut.vv_t_settling') # power_priority = ts.param_value('eut.power_priority') p_min_pct = ts.param_value('srd.vv_p_min_pct') p_max_pct = ts.param_value('srd.vv_p_max_pct') k_var_min_srd = ts.param_value('srd.vv_k_var_min') try: k_var_min = float(k_var_min_srd) except ValueError: k_var_min = None segment_point_count = ts.param_value('srd.vv_segment_point_count') # set power priorities to be tested power_priorities = [] if ts.param_value('vv.pp_active') == 'Enabled': power_priorities.append('Active') if ts.param_value('vv.pp_reactive') == 'Enabled': power_priorities.append('Reactive') # default power range p_min = p_rated * .2 p_max = p_rated # use values from SRD, if supplied if p_min_pct is not None: p_min = p_rated * (p_min_pct / 100.) if p_max is not None: p_max = p_rated * (p_max_pct / 100.) p_avg = (p_min + p_max) / 2 q_min_cap = q_max_cap / 4 q_min_ind = q_max_ind / 4 v_dev = min(v_nom - v_min, v_max - v_nom) # calculate k_var_min if not suppied in the SRD if k_var_min is None: k_var_min = (q_max_cap / 4) / (v_dev - deadband_max / 2) k_var_avg = (k_var_min + k_var_max) / 2 deadband_avg = (deadband_min + deadband_max) / 2 # list of active tests active_tests = [] if ts.param_value('vv.test_1') == 'Enabled': active_tests.append(1) if ts.param_value('vv.test_2') == 'Enabled': active_tests.append(2) if ts.param_value('vv.test_3') == 'Enabled': active_tests.append(3) # create test curves based on input parameters tests = [0] * 4 ''' The script only sets points 1-4 in the EUT, however they use v[0] and v[5] for testing purposes to define n points on the line segment to verify the reactive power ''' # Test 1 - Characteristic 1 "Most Aggressive" Curve q = [0] * 5 q[1] = q_max_cap # Q1 q[2] = 0 q[3] = 0 q[4] = q_max_ind v = [0] * 6 v[2] = v_nom - deadband_min / 2 v[1] = v[2] - abs(q[1]) / k_var_max v[0] = v_min v[3] = v_nom + deadband_min / 2 v[4] = v[3] + abs(q[4]) / k_var_max v[5] = v_max tests[1] = [list(v), list(q)] # Test 2 - Characteristic 2 "Average" Curve q = [0] * 5 q[1] = q_max_cap * .5 q[2] = 0 q[3] = 0 q[4] = q_max_ind * .5 v = [0] * 6 v[2] = v_nom - deadband_avg / 2 v[1] = v[2] - abs(q[1]) / k_var_avg v[0] = v_min v[3] = v_nom + deadband_avg / 2 v[4] = v[3] + abs(q[4]) / k_var_avg v[5] = v_max tests[2] = [list(v), list(q)] # Test 3 - Characteristic 3 "Least Aggressive" Curve q = [0] * 5 q[1] = q_min_cap q[2] = 0 q[3] = 0 q[4] = q_min_ind v = [0] * 6 v[0] = v_min v[2] = v_nom - deadband_min / 2 v[3] = v_nom + deadband_min / 2 if k_var_min == 0: v[1] = 0.99 * v[2] v[4] = 1.01 * v[3] else: v[1] = v[2] - abs(q[1]) / k_var_min v[4] = v[3] + abs(q[4]) / k_var_min v[5] = v_max tests[3] = [list(v), list(q)] ts.log('tests = %s' % (tests)) # list of tuples each containing (power level as % of max, # of test at power level) power_levels = [] count = ts.param_value('vv.n_r_100') if count > 0: power_levels.append((1, count)) count = ts.param_value('vv.n_r_66') if count > 0: power_levels.append((.66, count)) count = ts.param_value('vv.n_r_min') if count > 0: power_levels.append(((p_min / p_max), count)) ''' 1) Connect the EUT and measurement equipment according to the requirements in Sections 4 and 5 of IEEE Std 1547.1-2005 and specifications provided by the manufacturer. ''' ''' 2) Set all AC source parameters to the nominal operating conditions for the EUT. Frequency is set at nominal and held at nominal throughout this test. Set the EUT power to Pmax. ''' # grid simulator is initialized with test parameters and enabled grid = gridsim.gridsim_init(ts) # pv simulator is initialized with test parameters and enabled pv = pvsim.pvsim_init(ts) pv.power_set(p_max) pv.power_on() # initialize data acquisition daq = das.das_init(ts) ''' 3) Turn on the EUT. Set all L/HVRT parameters to the widest range of adjustability possible with the VV Q(V) enabled. The EUT's range of disconnect settings may depend on which function(s) are enabled. ''' # it is assumed the EUT is on eut = der.der_init(ts) eut.config() for priority in power_priorities: ''' 4) If the EUT has the ability to set 'Active Power Priority' or 'Reactive Power Priority', select Priority being evaluated. ''' ''' 5) Set the EUT to provide reactive power according to the Q(V) characteristic defined in Test 1 in Table SA13.1. ''' for test in active_tests: ts.log('Starting test - %s' % (test_labels[test])) # create voltage settings along all segments of the curve v = tests[test][0] q = tests[test][1] voltage_points = voltage_sample_points(v, segment_point_count) ts.log('Voltage test points = %s' % (voltage_points)) # set dependent reference type if priority == 'Active': dept_ref = 'VAR_AVAL_PCT' elif priority == 'Reactive': dept_ref = 'VAR_MAX_PCT' else: raise script.ScriptFail( 'Unknown power priority setting: %s') # set volt/var curve eut.volt_var_curve( 1, params={ # convert curve points to percentages and set DER parameters 'v': [ v[1] / v_nom * 100.0, v[2] / v_nom * 100.0, v[3] / v_nom * 100.0, v[4] / v_nom * 100.0 ], 'var': [ q[1] / q_max_cap * 100.0, q[2] / q_max_cap * 100.0, q[3] / q_max_cap * 100.0, q[4] / q_max_cap * 100.0 ], 'Dept_Ref': dept_ref }) # enable volt/var curve eut.volt_var(params={'Ena': True, 'ActCrv': 1}) for level in power_levels: power = level[0] # set input power level ts.log( ' Setting the input power of the PV simulator to %0.2f' % (p_max * power)) pv.power_set(p_max * power) count = level[1] for i in xrange(1, count + 1): ''' 6) Set the EPS voltage to a value greater than V4 for a duration of not less than the settling time. ''' ''' 7) Begin recording the time domain response of the EUT AC voltage and current, and DC voltage and current. Step down the simulated EPS voltage (the rise/fall time of simulated EPS voltage shall be < 1 cyc or < 1% of settling time) until at least three points are recorded in each line segment of the characteristic curve or the EUT trips from the LVRT must trip requirements. Continue recording the time domain response for at least twice the settling time after each voltage step. ''' ''' 8) Set all AC source parameters to the nominal operating conditions for the EUT. Frequency is set at nominal and held at nominal throughout this test. Set the EUT power to Pmax then repeat Repeat Step (7), except raising, instead of dropping, the simulated EPS voltage (the rise/fall time of simulated EPS voltage shall be < 1 cyc or < 1% of settling time) until at least three points are recorded in each line segment of the characteristic curve or the EUT trips from HVRT must trip requirements. ''' # test voltage high to low # start capture test_str = 'VV_high_%s_%s_%s' % (str(test), str(power), str(i)) ts.log( 'Starting data capture for test %s, testing voltage high to low, with %s, ' 'Power = %s%%, and sweep = %s' % (test_str, test_labels[test], power * 100., i)) daq.data_capture(True) for v in reversed(voltage_points): ts.log( ' Setting the grid voltage to %0.2f and waiting %0.1f seconds.' % (v, t_settling)) grid.voltage(v) ts.sleep(t_settling) # stop capture and save daq.data_capture(False) ds = daq.data_capture_dataset() filename = '%s.csv' % (test_str) ds.to_csv(ts.result_file_path(filename)) ts.result_file(filename) ts.log('Saving data capture') # test voltage low to high # start capture test_str = 'VV_low_%s_%s_%s' % (str(test), str(power), str(i)) ts.log( 'Starting data capture for test %s, testing voltage low to high, with %s, ' 'Power = %s%%, and sweep = %s' % (test_str, test_labels[test], power * 100., i)) daq.data_capture(True) for v in voltage_points: ts.log( ' Setting the grid voltage to %0.2f and waiting %0.1f seconds.' % (v, t_settling)) grid.voltage(v) ts.sleep(t_settling) # stop capture and save daq.data_capture(False) ds = daq.data_capture_dataset() filename = '%s.csv' % (test_str) ds.to_csv(ts.result_file_path(filename)) ts.result_file(filename) ts.log('Saving data capture') ''' 9) Repeat test Steps (6) - (8) at power levels of 20 and 66%; as described by the following: a) For the 20% test, the EUT output power set to 20% of its Prated nominal rating b) For the 66% test the test input source is to be adjusted to limit the EUT output power to a value between 50% and 95% of rated output power. c) The 66% power level, as defined in (b), shall be repeated for a total of five sweeps of the Q(V) curve to validate consistency. ''' ''' 10) Repeat steps (6) - (9) for the remaining tests in Table SA13.1. Other than stated in (9) (c), the required number of sweeps for each of these repetitions is three. In the case of EUT without adjustable (V, Q) points, this step may be eliminated. ''' ''' 11) If the EUT has the ability to set 'Active Power Priority' and 'Reactive Power Priority', select the other Priority, return the simulated EPS voltage to nominal, and repeat steps (5) - (10). ''' result = script.RESULT_COMPLETE except script.ScriptFail, e: reason = str(e) if reason: ts.log_error(reason)
def test_run(): result = script.RESULT_FAIL grid = None pv = p_rated = None daq = None eut = None rs = None phil = None result_summary = None step = None q_initial = None dataset_filename = None try: sink_power = ts.param_value('eut.sink_power') p_rated = ts.param_value('eut.p_rated') p_rated_prime = ts.param_value('eut.p_rated_prime') s_rated = ts.param_value('eut.s_rated') var_rated = ts.param_value('eut.var_rated') # DC voltages v_nom_in_enabled = ts.param_value('cpf.v_in_nom') v_min_in_enabled = ts.param_value('cpf.v_in_min') v_max_in_enabled = ts.param_value('cpf.v_in_max') v_nom_in = ts.param_value('eut.v_in_nom') v_min_in = ts.param_value('eut_cpf.v_in_min') v_max_in = ts.param_value('eut_cpf.v_in_max') # AC voltages v_nom = ts.param_value('eut.v_nom') v_min = ts.param_value('eut.v_low') v_max = ts.param_value('eut.v_high') f_nom = ts.param_value('eut.f_nom') p_min = ts.param_value('eut.p_min') p_min_prime = ts.param_value('eut.p_min_prime') phases = ts.param_value('eut.phases') low_pwr_ena = ts.param_value('vrt.low_pwr_ena') high_pwr_ena = ts.param_value('vrt.high_pwr_ena') low_pwr_value = ts.param_value('vrt.low_pwr_value') high_pwr_value = ts.param_value('vrt.high_pwr_value') # Pass/fail accuracies pf_msa = ts.param_value('eut.pf_msa') # EUI Absorb capabilities absorb = {} absorb['ena'] = ts.param_value('eut_cpf.sink_power') absorb['p_rated_prime'] = ts.param_value('eut_cpf.p_rated_prime') absorb['p_min_prime'] = ts.param_value('eut_cpf.p_min_prime') startup_time = ts.param_value('eut.startup_time') # Following parameters are collected in p1547.VoltageRideThrough.set_vrt_params in init: # vrt.phase_comb, vrt.lv_ena, vrt.hv_ena, vrt.consecutive_ena, vrt.cat, vrt.range_steps # Functions to be enabled for test mode = [] pwr_lvl = [] steps_dict = {} timestep_dict = {} sequence_dict = {} parameters = [] # initialize HIL environment, if necessary ts.log_debug(15 * "*" + "HIL initialization" + 15 * "*") phil = hil.hil_init(ts) if phil is not None: # return self.ts.param_value(self.group_name + '.' + GROUP_NAME + '.' + name) open_proj = phil._param_value('hil_config_open') compilation = phil._param_value('hil_config_compile') stop_sim = phil._param_value('hil_config_stop_sim') load = phil._param_value('hil_config_load') execute = phil._param_value('hil_config_execute') model_name = phil._param_value('hil_config_model_name') phil.config() ''' RTLab OpWriteFile Math using worst case scenario of 160 seconds, 14 signals and Ts = 40e-6 Duration of acquisition in number of points: Npoints = (Tend-Tstart)/(Ts*dec) = (160)/(0.000040*250) = 16e3 Acquisition frame duration: Tframe = Nbss * Ts * dec = 1000*0.000040*250 = 10 sec Number of buffers to be acquired: Nbuffers = Npoints / Nbss = (Tend - Tstart) / Tframe = 16 Minimum file size: MinSize= Nbuffers x SizeBuf = [(Tend - Tstart) / Ts ] * (Nsig+1) * 8 * Nbss = (160/40e-6)*(14+1)*8*1000 = 4.8e11 SizeBuf = 1/Nbuffers * {[(Tend - Tstart) / Ts ]*(Nsig+1)*8*Nbss} = [(160/0.000040)*(14+1)*8*1e3]/16 = 30e9 Size of one buffer in bytes (SizeBuf) = (Nsig+1) * 8 * Nbss (Minimum) = (14+1)*8*1000 = 120e3 ''' if low_pwr_ena == 'Enabled': pwr_lvl.append(low_pwr_value) else: ts.log_debug('No low power chosen') if high_pwr_ena == 'Enabled': pwr_lvl.append(high_pwr_value) else: ts.log_debug('No high power chosen') if high_pwr_ena == 'Disabled' and low_pwr_ena == 'Disabled': ts.log_error('No power tests included in VRT test!') """ Configure settings in 1547.1 Standard module for the Voltage Ride Through Tests """ VoltRideThrough = p1547.VoltageRideThrough(ts=ts, support_interfaces={"hil": phil}) # result params # result_params = lib_1547.get_rslt_param_plot() # ts.log(result_params # grid simulator is initialized with test parameters and enabled ts.log_debug(15 * "*" + "Gridsim initialization" + 15 * "*") grid = gridsim.gridsim_init(ts, support_interfaces={"hil": phil}) # Turn on AC so the EUT can be initialized if grid is not None: grid.voltage(v_nom) # Setting the nominal voltage for the EUT in /SM_Source/SVP Commands/voltage_ph_x... # pv simulator is initialized with test parameters and enabled ts.log_debug(15 * "*" + "PVsim initialization" + 15 * "*") pv = pvsim.pvsim_init(ts) if pv is not None: pv.power_set(p_rated) pv.power_on() # Turn on DC so the EUT can be initialized # initialize data acquisition ts.log_debug(15 * "*" + "DAS initialization" + 15 * "*") daq = das.das_init(ts, support_interfaces={"hil": phil, "pvsim": pv}) if daq is not None: daq.sc['V_MEAS'] = 100 """ daq.sc['P_MEAS'] = 100 daq.sc['Q_MEAS'] = 100 daq.sc['Q_TARGET_MIN'] = 100 daq.sc['Q_TARGET_MAX'] = 100 daq.sc['PF_TARGET'] = 1 daq.sc['event'] = 'None' ts.log('DAS device: %s' % daq.info()) """ """ This test doesn't have specific procedure steps. """ # open result summary file result_summary_filename = 'result_summary.csv' result_summary = open(ts.result_file_path(result_summary_filename), 'a+') ts.result_file(result_summary_filename) result_summary.write('Test Name, Waveform File, RMS File\n') """ During the LVRT test, the settings for magnitude and duration of undervoltage tripping functions shall be disabled or set so as not to influence the outcome of the test. If the EUT provides a voltage-active power control mode, that mode shall be disabled. Connect the EUT according to the instructions and specifications provided by the manufacturer. """ # Wait to establish communications with the EUT after AC and DC power are provided eut = der.der_init(ts) # start = time.time() # comm_wait_time = max(0.0, startup_time - 60.) # while time.time()-start < comm_wait_time - 1: # ts.sleep(1) # ts.log('Waiting another %0.2f seconds until communicating with EUT' % # (comm_wait_time - (time.time()-start))) if eut is not None: eut.config() # if eut is not None: # eut.deactivate_all_fct() """ The voltage-reactive power control mode of the EUT shall be set to the default settings specified in Table 8 of IEEE Std 1547-2018 for the applicable performance category, and enabled. """ # Default curve is characteristic curve 1 vv_curve = 1 VoltVar = p1547.VoltVar(ts=ts, imbalance=True) v_pairs = VoltVar.get_params(curve=vv_curve) ts.log_debug('v_pairs:%s' % v_pairs) if eut is not None: # Set to: V = {92, 98, 102, 108}, Var = {44, 0 , 0 , -44} vv_curve_params = {'v': [round(v_pairs['V1'] * (100 / v_nom)), round(v_pairs['V2'] * (100 / v_nom)), round(v_pairs['V3'] * (100 / v_nom)), round(v_pairs['V4'] * (100 / v_nom))], 'q': [round(v_pairs['Q1'] * (100 / var_rated)), round(v_pairs['Q2'] * (100 / var_rated)), round(v_pairs['Q3'] * (100 / var_rated)), round(v_pairs['Q4'] * (100 / var_rated))], 'DeptRef': 'Q_MAX_PCT'} ts.log_debug('Setting VV points: %s' % vv_curve_params) # eut.volt_var(params={'Ena': True, 'curve': vv_curve_params}) """ The frequency-active power control mode of the EUT shall be set to the default settings. """ # if eut is not None: # params = {'Ena': True, # 'curve': 1, # 'dbf': fw_param['dbf'], # 'kof': fw_param['kof'], # 'RspTms': fw_param['tr']} # ts.log_debug(params) # eut.freq_watt(params) """ Set or verify that all frequency trip settings are set to not influence the outcome of the test. """ # ts.log_debug('HFRT and trip parameters from EUT : {}'.format(eut.frt_stay_connected_high())) # ts.log_debug('LFRT and trip parameters from EUT : {}'.format(eut.frt_stay_connected_low())) """ Operate the ac test source at nominal frequency ± 0.1 Hz. """ # Configured in PHIL on startup # Initial loop for all mode that will be executed modes = VoltRideThrough.get_modes() # Options: LV_CAT_2, HV_CAT_2, LV_CAT_3, HV_CAT_3 ts.log(f"VRT modes tested : '{modes}'") for current_mode in modes: # Configuring waveform timing blocks with offset in seconds # daq.waveform_config(vrt_lib_1547.get_waveform_config(current_mode,offset=5)) ts.log_debug(f'Clearing old parameters if any') # parameters.clear() ts.log_debug(f'Initializing {current_mode}') """ The ride-through tests shall be performed at two output power levels, high and low, and at any convenient power factor greater than 0.90. The output power levels shall be measured prior to the disturbance, i.e., in test condition A. High-power tests shall be performed at any active power level greater than 90% of the EUT nameplate active power rating at nominal voltage. ... Low-power tests shall be performed at any convenient power level between 25% to 50% of EUT nameplate apparent power rating at nominal voltage. """ for pwr in pwr_lvl: # Loop for all power levels dataset_filename = f'VRT_{current_mode}_{round(pwr*100)}PCT' ts.log(f'------------{dataset_filename}------------') daq.data_capture(True) """ Setting up available power to appropriate power level """ if pv is not None: ts.log_debug(f'Setting power level to {pwr}') pv.iv_curve_config(pmp=p_rated, vmp=v_nom_in) pv.irradiance_set(1000.) pv.power_set(p_rated * pwr) """ Initiating voltage sequence for VRT """ vrt_parameters, vrt_start_time, vrt_stop_time = VoltRideThrough.get_model_parameters(current_mode) ts.log(f"The start time will be at {vrt_start_time}") ts.log(f"The stop time will be at {vrt_stop_time}") ts.log(f"Total VRT time is {vrt_stop_time-vrt_start_time}") VoltRideThrough.waveform_config(param={"pre_trigger": vrt_start_time - 5, "post_trigger": vrt_stop_time + 5}) if phil is not None: # Set model parameters phil.set_parameters(vrt_parameters) vrt_stop_time = vrt_stop_time + 5 ts.sleep(0.5) ts.log('Stop time set to %s' % phil.set_stop_time(vrt_stop_time)) phil.load_model_on_hil() phil.start_simulation() sim_time = phil.get_time() while (vrt_stop_time - sim_time) > 1.0: # final sleep will get to stop_time. sim_time = phil.get_time() ts.log('Sim Time: %0.3f. Waiting another %0.3f sec before saving data.' % ( sim_time, vrt_stop_time - sim_time)) ts.sleep(5) ts.log('Sampling RMS complete') rms_dataset_filename = dataset_filename + "_RMS.csv" wave_start_filename = dataset_filename + "_WAV.csv" daq.data_capture(False) # complete data capture ts.log('Waiting for Opal to save the waveform data: {}'.format(dataset_filename)) ts.sleep(10) # Convert and save the .mat file that contains the phase jump start ts.log('Processing waveform dataset(s)') ds = daq.waveform_capture_dataset() # returns list of databases of waveforms (overloaded) ts.log(f'Number of waveforms to save {len(ds)}') if len(ds) > 0: ds[0].to_csv(ts.result_file_path(wave_start_filename)) ts.result_file(wave_start_filename) ts.log('Sampling RMS complete') ds = daq.data_capture_dataset() ts.log('Saving file: %s' % rms_dataset_filename) ds.to_csv(ts.result_file_path(rms_dataset_filename)) ds.remove_none_row(ts.result_file_path(rms_dataset_filename), "TIME") result_params = { 'plot.title': rms_dataset_filename.split('.csv')[0], 'plot.x.title': 'Time (sec)', 'plot.x.points': 'TIME', 'plot.y.points': 'AC_VRMS_1, AC_VRMS_2, AC_VRMS_3', 'plot.y.title': 'Voltage (V)', 'plot.y2.points': 'AC_IRMS_1, AC_IRMS_2, AC_IRMS_3', 'plot.y2.title': 'Current (A)', } # Remove the None in the dataset file ts.result_file(rms_dataset_filename, params=result_params) result_summary.write('%s, %s, %s,\n' % (dataset_filename, wave_start_filename, rms_dataset_filename)) phil.stop_simulation() result = script.RESULT_COMPLETE except script.ScriptFail as e: reason = str(e) if reason: ts.log_error(reason) except Exception as e: ts.log_error((e, traceback.format_exc())) ts.log_error('Test script exception: %s' % traceback.format_exc()) finally: if grid is not None: grid.close() if pv is not None: if p_rated is not None: pv.power_set(p_rated) pv.close() if daq is not None: daq.close() if eut is not None: # eut.fixed_pf(params={'Ena': False, 'PF': 1.0}) eut.close() if rs is not None: rs.close() if phil is not None: if phil.model_state() == 'Model Running': phil.stop_simulation() phil.close() if result_summary is not None: result_summary.close() # create result workbook excelfile = ts.config_name() + '.xlsx' rslt.result_workbook(excelfile, ts.results_dir(), ts.result_dir()) ts.result_file(excelfile) return result
def vw_mode(vw_curves, mode=None): result = script.RESULT_FAIL daq = None v_nom = None grid = None pv = None eut = None chil = None result_summary = None dataset_filename = None try: # Rated powers p_rated = ts.param_value('eut.p_rated') var_rated = ts.param_value('eut.var_rated') s_rated = ts.param_value('eut.s_rated') # DC power if ts.param_value( 'pvsim.terrasas.pmp' ) is not None: # TODO - REPLACE WITH CORRECT REFERENCE p_pvsim = ts.param_value('pvsim.terrasas.pmp') else: p_pvsim = p_rated # DC voltages v_in_nom = ts.param_value('eut.v_in_nom') # AC voltages v_nom = ts.param_value('eut.v_nom') v_low = ts.param_value('eut.v_low') v_high = ts.param_value('eut.v_high') phases = ts.param_value('eut.phases') vw_response_time = 0 vw_timing = [ ts.param_value('vw.commencement_time'), ts.param_value('vw.completion_time'), ts.param_value('vw.step_time_period') ] """ A separate module has been create for the DR_AS_NZS_4777.2 Standard """ pAus4777.VersionValidation(script_version=ts.info.version) if mode == 'Volt-Var': #VoltVar = pAus4777.VoltVar(ts=ts) Active_function = pAus4777.ActiveFunction(ts=ts, functions=[VW, VV]) else: Active_function = pAus4777.ActiveFunction(ts=ts, functions=[VW]) #ts.log_debug(f"AUS4777,2 Library configured for {Active_function.script_complete_name}") #ts.log_debug(f"AUS4777,2 Library configured for {Active_function.VoltWatt.get_params()}") # result params x_axis_specs = {'min': v_low * 0.9} #result_params = VoltWatt.get_rslt_param_plot(x_axis_specs=x_axis_specs) result_params = Active_function.get_rslt_param_plot( x_axis_specs=x_axis_specs) ts.log_debug(result_params) ''' Connect the EUT according to the instructions and specifications provided by the manufacturer and initialisation of the chil, pvsim, das, eut/der and the gridsim ''' # initialize HIL environment, if necessary chil = hil.hil_init(ts) if chil is not None: chil.config() # initialize the pvsim pv = pvsim.pvsim_init(ts) # DAS soft channels das_points = Active_function.get_sc_points() # initialize data acquisition system daq = das.das_init(ts, sc_points=das_points['sc']) daq.sc['V_TARGET'] = v_nom daq.sc['TR_SS_TARGET'] = 10 daq.sc['Q_TARGET'] = 100 daq.sc['Q_TARGET_MIN'] = 100 daq.sc['Q_TARGET_MAX'] = 100 daq.sc['P_TARGET'] = 100 daq.sc['P_TARGET_MIN'] = 100 daq.sc['P_TARGET_MAX'] = 100 daq.sc['event'] = 'None' ts.log(f'DAS device: {daq.info()}') # Setting the pvsim to the rated power of the eut if pv is not None: pv.iv_curve_config(pmp=p_pvsim, vmp=v_in_nom) #pv.iv_curve_config(pmp=p_rated, vmp=v_in_nom) #pv.irradiance_set(0.) #ts.log("PV simulator irradiance set to 0, sleeping for 15 seconds to allow EUT to shut down") #ts.sleep(15) pv.irradiance_set(1000.) pv.power_on() # Turn on DC so the EUT can be initialized pvsim_sleeptime = 60 ts.log( f"PV simulator enabled, sleeping for {pvsim_sleeptime} seconds to allow EUT to stabilise" ) ts.sleep(pvsim_sleeptime) # initialize the eut eut = der.der_init(ts) if eut is not None: eut.config() # ts.log_debug(eut.measurements()) #Deactivating all functions on EUT #eut.deactivate_all_fct() # initialize the GridSim grid = gridsim.gridsim_init(ts, support_interfaces={ 'hil': chil }) # Turn on AC so the EUT can be initialized gridsim_sleeptime = 120 ts.log( f"Grid simulator enabled, sleeping for {gridsim_sleeptime} seconds to allow EUT to connect" ) ts.sleep(gridsim_sleeptime) # open result summary file result_summary_filename = 'result_summary.csv' result_summary = open(ts.result_file_path(result_summary_filename), 'a+') ts.result_file(result_summary_filename) ts.log(f'col_name={Active_function.get_rslt_sum_col_name()}') result_summary.write(Active_function.get_rslt_sum_col_name()) ''' Repeat the test for each regions curves (Australia A, Australia B, Australia C, New Zealand and Allowed range) ''' ts.log(f'curves={vw_curves}') for vw_curve in vw_curves: #ts.log(f'curves={vw_curve}') ts.log(f'Starting test with characteristic curve {vw_curve}') Active_function.reset_curve(vw_curve) Active_function.reset_time_settings(tr=vw_timing, number_tr=3) if mode == 'Volt-Var': vv_pairs = Active_function.get_params(function=VV, region=vw_curve) ts.log_debug(f'volt-var_pairs:{vv_pairs}') vw_pairs = Active_function.get_params(function=VW, region=vw_curve) ts.log_debug(f'volt-watt_pairs:{vw_pairs}') ''' (a) Enable the volt-watt and volt-var response modes. ''' if eut is not None: # Activate volt-var function with following parameters # SunSpec convention is to use percentages for V and Q points. if mode == 'Volt-Var': vv_curve_params = { 'v': [(vv_pairs['Vv1'] / v_nom) * 100, (vv_pairs['Vv2'] / v_nom) * 100, (vv_pairs['Vv3'] / v_nom) * 100, (vv_pairs['Vv4'] / v_nom) * 100], 'var': [(vv_pairs['Q1'] / s_rated) * 100, (vv_pairs['Q2'] / s_rated) * 100, (vv_pairs['Q3'] / s_rated) * 100, (vv_pairs['Q4'] / s_rated) * 100], 'vref': round(v_nom, 2), 'RmpPtTms': vw_response_time } ts.log_debug(f'Sending Volt-Var points: {vv_curve_params}') eut.volt_var(params={ 'Ena': True, 'ACTCRV': vw_curve, 'curve': vv_curve_params }) ts.log_debug( f'Initial EUT Volt-Var settings are {eut.volt_var()}') # Activate volt-watt function with following parameters # SunSpec convention is to use percentages for V and P points. vw_curve_params = { 'v': [(vw_pairs['Vw1'] / v_nom) * 100, (vw_pairs['Vw2'] / v_nom) * 100], 'w': [(vw_pairs['P1'] / s_rated) * 100, (vw_pairs['P2'] / s_rated) * 100] } ts.log_debug(f'Sending Volt-Watt points: {vw_curve_params}') eut.volt_watt(params={ 'Ena': True, 'ACTCRV': vw_curve, 'curve': vw_curve_params }) ts.log_debug( f'Initial EUT Volt-Watt settings are {eut.volt_watt()}') """ (b) Set the grid source equal to the grid test voltage. Vary the energy source until the a.c. output of the device under test equals 100 ± 5 % of its rated active power output. """ # Setting grid to vnom before test if grid is not None: grid.voltage(v_nom) # Setting the pvsim to the rated power of the eut if pv is not None: pv.iv_curve_config(pmp=p_pvsim, vmp=v_in_nom) #pv.iv_curve_config(pmp=p_rated, vmp=v_in_nom) pv.irradiance_set(1000.) """ Going trough step C to step N """ #Construct the v_steps_dict from step c to step n if mode == 'Volt-Var': v_steps_dict = Active_function.create_vw_dict_steps( mode=mode, secondary_pairs=vv_pairs) else: v_steps_dict = Active_function.create_vw_dict_steps(mode=mode) ts.log_debug(v_steps_dict) dataset_filename = f'VW_{vw_curve}' if mode == 'Volt-Var': dataset_filename += '_combined_VV' Active_function.reset_filename(filename=dataset_filename) # Start the data acquisition systems daq.data_capture(True) for step_label, v_step in v_steps_dict.items(): ts.log( f'Voltage step: setting Grid simulator voltage to {v_step} ({step_label})' ) if 'C' in step_label or 'H' in step_label: if grid is not None: grid.voltage(v_step) else: Active_function.start(daq=daq, step_label=step_label) if grid is not None: grid.voltage(v_step) Active_function.record_timeresponse(daq=daq, step_value=v_step) Active_function.evaluate_criterias() result_summary.write(Active_function.write_rslt_sum()) """ (o) Summarize results in a table from initial value to final voltage value showing voltage, apparent power, active power, reactive power and time to reach required reactive power level for each voltage step. Plot results on a graph of voltage versus apparent power, active power and reactive power. """ ts.log('Sampling complete') dataset_filename = dataset_filename + ".csv" daq.data_capture(False) ds = daq.data_capture_dataset() ts.log(f'Saving file: {dataset_filename}') ds.to_csv(ts.result_file_path(dataset_filename)) result_params['plot.title'] = dataset_filename.split('.csv')[0] ts.result_file(dataset_filename, params=result_params) result = script.RESULT_COMPLETE except script.ScriptFail as e: reason = str(e) if reason: ts.log_error(reason) except Exception as e: if dataset_filename is not None: dataset_filename = dataset_filename + ".csv" daq.data_capture(False) ds = daq.data_capture_dataset() ts.log(f'Saving file: {dataset_filename}') ds.to_csv(ts.result_file_path(dataset_filename)) result_params['plot.title'] = dataset_filename.split('.csv')[0] ts.result_file(dataset_filename, params=result_params) ts.log_error(f'Test script exception: {traceback.format_exc()}') finally: if daq is not None: daq.close() if pv is not None: pv.close() if grid is not None: if v_nom is not None: grid.voltage(v_nom) grid.close() if chil is not None: chil.close() if eut is not None: eut.close() if result_summary is not None: result_summary.close() return result
def test_run(): result = script.RESULT_FAIL daq = None grid = None pv = None eut = None chil = None result_summary = None dataset_filename = None v_nom = None try: settings_test = ts.param_value('iop.settings_test') monitoring_test = ts.param_value('iop.monitoring_test') v_nom = float(ts.param_value('eut.v_nom')) MSA_V = 0.01 * v_nom va_max = float(ts.param_value('eut.s_rated')) va_crg_max = va_max MSA_Q = 0.05 * float(ts.param_value('eut.s_rated')) MSA_P = 0.05 * float(ts.param_value('eut.s_rated')) MSA_F = 0.01 f_nom = float(ts.param_value('eut.f_nom')) phases = ts.param_value('eut.phases') p_rated = float(ts.param_value('eut.p_rated')) w_max = p_rated p_min = p_rated w_crg_max = p_rated var_rated = float(ts.param_value('eut.var_rated')) var_max = float(ts.param_value('eut.var_rated')) # initialize DER configuration eut = der.der_init(ts) eut.config() das_points = {'sc': ('event')} daq = das.das_init(ts, sc_points=das_points['sc']) # initialize HIL environment, if necessary chil = hil.hil_init(ts) if chil is not None: chil.config() # pv simulator is initialized with test parameters and enabled pv = pvsim.pvsim_init(ts) if pv is not None: pv.power_set(p_rated) pv.power_on() # Turn on DC so the EUT can be initialized # grid simulator is initialized with test parameters and enabled grid = gridsim.gridsim_init( ts) # Turn on AC so the EUT can be initialized if grid is not None: grid.voltage(v_nom) lib_1547 = p1547.module_1547(ts=ts, aif='Interoperability Tests') ts.log_debug("1547.1 Library configured for %s" % lib_1547.get_test_name()) ''' 6.4 Nameplate data test a) Read from the DER each nameplate data item listed in Table 28 in IEEE Std 1547-2018. b) Compare each value received to the expected values from the manufacturer-provided expected values. Table 28 - Nameplate information ________________________________________________________________________________________________________________ Parameter Description ________________________________________________________________________________________________________________ 1. Active power rating at unity power factor Active power rating in watts at unity power factor (nameplate active power rating) 2. Active power rating at specified over-excited Active power rating in watts at specified over-excited power factor power factor 3. Specified over-excited power factor Over-excited power factor as described in 5.2 4. Active power rating at specified under-excited Active power rating in watts at specified under-excited power factor power factor 5. Specified under-excited power factor Under-excited power factor as described in 5.2 6. Apparent power maximum rating Maximum apparent power rating in voltamperes 7. Normal operating performance category Indication of reactive power and voltage/power control capability. (Category A/B as described in 1.4) 8. Abnormal operating performance category Indication of voltage and frequency ride-through capability Category I, II, or III, as described in 1.4 9. Reactive power injected maximum rating Maximum injected reactive power rating in vars 10. Reactive power absorbed maximum rating Maximum absorbed reactive power rating in vars 11. Active power charge maximum rating Maximum active power charge rating in watts 12. Apparent power charge maximum rating Maximum apparent power charge rating in voltamperes. May differ from the apparent power maximum rating 13. AC voltage nominal rating Nominal AC voltage rating in RMS volts 14. AC voltage maximum rating Maximum AC voltage rating in RMS volts 15. AC voltage minimum rating Minimum AC voltage rating in RMS volts 16. Supported control mode functions Indication of support for each control mode function 17. Reactive susceptance that remains connected to Reactive susceptance that remains connected to the Area the Area EPS in the cease to energize and trip EPS in the cease to energize and trip state state 18. Manufacturer Manufacturer 19. Model Model 20. Serial number Serial number 21. Version Version ''' ts.log('---') der_info = eut.info() nameplate = eut.nameplate() ts.log('DER Nameplate Information:') if nameplate is not None: ts.log( ' Active power rating at unity power factor (nameplate active power rating) [WRtg]: %s' % nameplate.get('WRtg')) ts.log( ' Active power rating at specified over-excited power factor: %s' % 'Unknown') ts.log(' Specified over-excited power factor [PFRtgQ1]: %s' % nameplate.get('PFRtgQ1')) ts.log(' Specified under-excited power factor [PFRtgQ2]: %s' % nameplate.get('PFRtgQ2')) ts.log(' Apparent power maximum rating: %s' % nameplate.get('VARtg')) ts.log(' Normal operating performance category: %s' % 'Unknown') ts.log(' Abnormal operating performance category: %s' % 'Unknown') ts.log(' Reactive power injected maximum rating [VArRtgQ1]: %s' % nameplate.get('VArRtgQ1')) ts.log(' Reactive power absorbed maximum rating [VArRtgQ4]: %s' % nameplate.get('VArRtgQ4')) ts.log(' Apparent power charge maximum rating: %s' % nameplate.get('MaxChrRte')) ts.log(' AC voltage nominal rating: %s' % 'Unknown') ts.log(' AC voltage maximum rating: %s' % 'Unknown') ts.log(' AC voltage minimum rating: %s' % 'Unknown') if der_info is not None: ts.log(' Supported control mode functions: %s' % der_info.get('Options')) else: ts.log_warning('DER info not supported') if nameplate is not None: ts.log( ' Reactive susceptance that remains connected to the Area EPS in the cease to ' 'energize and trip state: %s' % 'Unknown') else: ts.log_warning('DER nameplate not supported') if der_info is not None: ts.log(' Manufacturer: %s' % (der_info.get('Manufacturer'))) ts.log(' Model: %s' % (der_info.get('Model'))) ts.log(' Serial Number: %s' % (der_info.get('SerialNumber'))) ts.log(' Version: %s' % (der_info.get('Version'))) else: ts.log_warning('DER info not supported') if settings_test: ''' 6.5 Basic settings information test a) Read from the DER each parameter identified in Table 42. b) For each, verify that the value reported matches the behavior of the DER measured though independent test equipment separate from the DER interface. c) Adjust values as identified in Table 42. d) Repeat steps a) and b) for the new values. e) Adjust parameters back to the initial values and verify that the value reported matches the initial values. Table 42 - Basic settings test levels ____________________________________________________________________________________________________________ Parameter Adjustment required Additional test instructions ____________________________________________________________________________________________________________ Active Power Maximum Set to 80% of Initial Value Apparent Power Maximum Set to 80% of Initial Value Reactive Power Injected Maximum Set to 80% of Initial Value Reactive Power Absorbed Maximum Set to 80% of Initial Value Active Power Charge Maximum Set to 80% of Initial Value This test applies only to DER that include energy storage. Apparent Power Charge Maximum Set to 80% of Initial Value This test applies only to DER that include energy storage. AC Current Maximum Set to 80% of Initial Value Control Mode Functions Not applicable Stated Energy Storage Capacity Set to 80% of Initial Value This test applies only to DER that include energy storage. Mode Enable Interval Set to 5 s, set to 300 s ''' # Special considerations for CHIL ASGC/Typhoon startup if chil is not None: inv_power = eut.measurements().get('W') timeout = 120. if inv_power <= p_rated * 0.85: pv.irradiance_set( 995) # Perturb the pv slightly to start the inverter ts.sleep(3) eut.connect(params={'Conn': True}) while inv_power <= p_rated * 0.85 and timeout >= 0: ts.log( 'Inverter power is at %0.1f. Waiting up to %s more seconds or until EUT starts...' % (inv_power, timeout)) ts.sleep(1) timeout -= 1 inv_power = eut.measurements().get('W') if timeout == 0: result = script.RESULT_FAIL raise der.DERError('Inverter did not start.') ts.log('Waiting for EUT to ramp up') ts.sleep(8) # ts.log_debug('DAS data_read(): %s' % daq.data_read()) settings = eut.settings() if settings is not None: # Active Power Maximum ts.log(' Active Power Maximum [WMax]: %s' % (settings.get('WMax'))) ts.log(' Setting WMax to %f.' % (0.8 * w_max)) eut.settings(params={'WMax': 0.8 * w_max}) ts.sleep(2) power = lib_1547.get_measurement_total( data=daq.data_capture_read(), type_meas='P', log=True) ts.log(' Power was recorded to be: %f.' % power) ts.log(' Returning WMax to %f.' % w_max) eut.settings(params={'WMax': w_max}) # Apparent Power Maximum ts.log(' Apparent Power Maximum [VAMax]: %s' % (settings.get('VAMax'))) ts.log(' Setting VAMax to %f.' % (0.8 * va_max)) eut.settings(params={'VAMax': 0.8 * va_max}) ts.sleep(2) va = lib_1547.get_measurement_total( data=daq.data_capture_read(), type_meas='VA', log=True) ts.log(' Apparent Power was recorded to be: %f.' % va) ts.log(' Returning VAMax to %f.' % va_max) eut.settings(params={'VAMax': va_max}) # Reactive Power Injected Maximum # TODO check on sign convention here ts.log(' Reactive Power Injected Maximum [VArMaxQ1]: %s' % (settings.get('VArMaxQ1'))) ts.log(' Setting VArMaxQ1 to %f.' % (0.8 * var_max)) eut.settings(params={'VArMaxQ1': 0.8 * var_max}) eut.reactive_power(params={ 'Ena': True, 'VArPct_Mod': 'VArMax', 'VArMaxPct': 100 }) ts.sleep(2) q = lib_1547.get_measurement_total( data=daq.data_capture_read(), type_meas='Q', log=True) ts.log(' Reactive Power Injected was recorded to be: %f.' % q) ts.log(' Returning VArMaxQ1 to %f.' % var_max) eut.settings(params={'VArMaxQ1': var_max}) eut.reactive_power(params={'Ena': False}) # Reactive Power Absorbed Maximum ts.log(' Reactive Power Absorbed Maximum [VArMaxQ4]: %s' % (settings.get('VArMaxQ4'))) ts.log(' Setting VArMaxQ4 to %f.' % (0.8 * var_max)) eut.settings(params={'VArMaxQ4': 0.8 * var_max}) eut.reactive_power(params={ 'Ena': True, 'VArPct_Mod': 'VArMax', 'VArMaxPct': -100 }) ts.sleep(2) q = lib_1547.get_measurement_total( data=daq.data_capture_read(), type_meas='Q', log=True) ts.log(' Reactive Power Absorbed was recorded to be: %f.' % q) ts.log(' Returning VArMaxQ4 to %f.' % var_max) eut.settings(params={'VArMaxQ4': var_max}) eut.reactive_power(params={'Ena': False}) # AC Current Maximum ts.log(' Apparent Power Maximum [VAMax]: %s' % (settings.get('VAMax'))) ts.log(' Setting VAMax to %f.' % (0.8 * va_max)) eut.settings(params={'VAMax': 0.8 * va_max}) ts.sleep(2) va = lib_1547.get_measurement_total( data=daq.data_capture_read(), type_meas='VA', log=True) ts.log(' Apparent Power was recorded to be: %f.' % va) ts.log(' Returning VAMax to %f.' % va_max) eut.settings(params={'VAMax': va_max}) # Mode Enable Interval # TODO add this assessment {'ModeInterval': [5, 300]} else: ts.log_warning('DER settings not supported') storage = eut.storage() if storage is not None: # Active Power Charge Maximum ts.log(' Active Power Charge Maximum [WChaMax]: %s' % (storage.get('WChaMax'))) ts.log(' Setting WChaMax to %f.' % (0.8 * w_crg_max)) eut.storage(params={'WChaMax': 0.8 * w_crg_max}) ts.log(' Setting InWRte to %f %% of max charging rate.' % 100.) # Percent of max charging rate. eut.storage(params={'InWRte': 100.}) ts.sleep(2) power = lib_1547.get_measurement_total( data=daq.data_capture_read(), type_meas='P', log=True) ts.log(' Apparent Power was recorded to be: %f.' % power) ts.log(' Returning WChaMax to %f.' % w_crg_max) eut.storage(params={'WChaMax': w_crg_max}) eut.storage(params={'InWRte': 0.}) # Apparent Power Charge Maximum ts.log(' Apparent Power Charge Maximum [VAChaMax]: %s' % (storage.get('VAChaMax'))) ts.log(' Setting WChaMax to %f.' % (0.8 * va_crg_max)) eut.storage(params={'VAChaMax': 0.8 * va_crg_max}) ts.log(' Setting InWRte to %f %% of max charging rate.' % 100.) # Percent of max charging rate. eut.storage(params={'InWRte': 100.}) ts.sleep(2) power = lib_1547.get_measurement_total( data=daq.data_capture_read(), type_meas='VA', log=True) ts.log(' Apparent Power was recorded to be: %f.' % power) ts.log(' Returning VAChaMax to %f.' % va_crg_max) eut.storage(params={'VAChaMax': va_crg_max}) eut.storage(params={'InWRte': 0.}) # Stated Energy Storage Capacity # StorAval = State of charge (ChaState) minus storage reserve (MinRsvPct) times capacity rating (AhrRtg) # TODO add this assessment else: ts.log_warning('DER storage not supported') if monitoring_test: ''' 6.6 Monitoring information test a) Set the operating conditions of the DER to the values specified in the "Operating Point A" column in Table 43. b) Wait not less than 30 s, then read from the DER each monitoring information, and verify that the reported values match the operating conditions as identified. c) Change the operating conditions of the DER as specified in the "Operating Point B" column 16 in Table 43. d) Repeat step b). ''' # Monitoring information parameter: Active Power # Operating Point A: 0 to 10% of DER "active power rating at unity power factor" # Operating Point B: 90 to 100% of DER "active power rating at unity power factor" # Pass/Fail Criteria: Reported values match test operating conditions within the accuracy requirements # specified in Table 3 in IEEE Std 1547-2018. (+/- 5% Srated) m = eut.measurements() if m is not None: ts.log(' Active Power reported from the EUT is: %s W' % (m.get('W'))) for setpoint in [5, 100]: eut.limit_max_power(params={ 'Ena': True, 'WMaxPct': setpoint }) # curtial to 5% of power inaccurate_measurement = True while inaccurate_measurement: power_pct = 100 * (eut.measurements().get('W') / w_max) ts.log(' EUT power is currently %f%% Prated' % power_pct) if setpoint - 5. <= power_pct <= setpoint + 5.: # +/- 5% Srated ts.log( 'EUT has recorded power +/- 5%% Srated, as required by IEEE 1547-2018.' ) ts.log('Returning EUT to rated power.') inaccurate_measurement = False ts.sleep(1) eut.limit_max_power(params={'Ena': False}) else: ts.log_warning('DER measurements not supported') ts.log('---') # Monitoring information parameter: Reactive Power # Operating Point A: 90 to 100% of DER "reactive power injected maximum rating" # Operating Point B: 90 to 100% of DER "reactive power absorbed maximum rating" # Pass/Fail Criteria: Reported values match test operating conditions within the accuracy requirements # specified in Table 3 in IEEE Std 1547-2018. (+/- 5% Srated) m = eut.measurements() if m is not None: ts.log(' Reactive Power reported from the EUT is: %f VAr' % (m.get('VAr'))) for setpoint in [5, 100]: eut.reactive_power(params={ 'Ena': True, 'VArPct_Mod': 'VArMax', 'VArMaxPct': 100 }) inaccurate_measurement = True while inaccurate_measurement: q_pct = 100 * (eut.measurements().get('VAr') / var_max) ts.log( ' EUT reactive power is currently %f%% Qrated' % q_pct) if setpoint - 5. <= q_pct <= setpoint + 5.: # +/- 5% Srated ts.log( 'EUT has recorded reactive power +/- 5%% Srated, as required by IEEE 1547-2018.' ) ts.log('Returning EUT to rated power.') inaccurate_measurement = False ts.sleep(1) eut.reactive_power(params={'Ena': False}) else: ts.log_warning('DER measurements not supported') ts.log('---') # Monitoring information parameter: Voltage # Operating Point A: At or below 0.90x(AC voltage nominal rating) # Operating Point B: At or above 1.08x(AC voltage nominal rating) # Pass/Fail Criteria: Reported values match test operating conditions within the accuracy requirements # specified in Table 3 in IEEE Std 1547-2018. (+/-1% Vnom) for setpoint in [90, 108]: grid.voltage(setpoint) inaccurate_measurement = True while inaccurate_measurement: voltages = [] if self.phases == 'Single phase': voltages.append(eut.measurements()['PhVphA']) elif self.phases == 'Split phase': voltages.append(eut.measurements()['PhVphA']) voltages.append(eut.measurements()['PhVphB']) elif self.phases == 'Three phase': voltages.append(eut.measurements()['PhVphB'] / v_nom) voltages.append(eut.measurements()['PhVphB'] / v_nom) voltages.append(eut.measurements()['PhVphC'] / v_nom) # TODO: also check phase to phase voltages voltages.append(eut.measurements()['PPVphAB'] / (v_nom * math.sqrt(3))) voltages.append(eut.measurements()['PPVphBC'] / (v_nom * math.sqrt(3))) voltages.append(eut.measurements()['PPVphCA'] / (v_nom * math.sqrt(3))) ts.log(' EUT voltages are currently %s pu' % voltages) pass_criteria = [] for voltage in voltages: if setpoint / 100. - 0.01 <= voltage <= setpoint / 100. + 0.01: # +/- 1% Vnom pass_criteria.append(True) else: pass_criteria.append(True) if all(pass_criteria): ts.log( 'EUT has recorded voltage +/- 1%% Vnom, as required by IEEE 1547-2018.' ) ts.log('Returning EUT to rated power.') inaccurate_measurement = False ts.sleep(1) grid.voltage(v_nom) # Monitoring information parameter: Frequency # Operating Point A: At or below 57.2Hz # Operating Point B: At or above 61.6Hz # Pass/Fail Criteria: Reported values match test operating conditions within the accuracy requirements # specified in Table 3 in IEEE Std 1547-2018. (10 mHz) # Monitoring information parameter: Operational State # Operating Point A: On # Operating Point B: Off # Pass/Fail Criteria: Reported Operational State matches the device present condition for on and off states. # Monitoring information parameter: Connection Status # Operating Point A: Connected: Enable Permit and AC conditions have been met to enter service as specified # in Table 39 of IEEE Std 1547-2018 # Operating Point B: Disconnected: Permit service is disabled # Pass/Fail Criteria: Reported Connection Status matches the device present connection condition. status = eut.controls_status() if status is not None: ts.log(' Is Fixed_W enabled?: %s' % (status.get('Fixed_W'))) ts.log(' Is Fixed_Var enabled?: %s' % (status.get('Fixed_Var'))) ts.log(' Is Fixed_PF enabled?: %s' % (status.get('Fixed_PF'))) ts.log(' Is Volt_Var enabled?: %s' % (status.get('Volt_Var'))) ts.log(' Is Freq_Watt_Param enabled?: %s' % (status.get('Freq_Watt_Param'))) ts.log(' Is Freq_Watt_Curve enabled?: %s' % (status.get('Freq_Watt_Curve'))) ts.log(' Is Dyn_Reactive_Power enabled?: %s' % (status.get('Dyn_Reactive_Power'))) ts.log(' Is LVRT enabled?: %s' % (status.get('LVRT'))) ts.log(' Is HVRT enabled?: %s' % (status.get('HVRT'))) ts.log(' Is Watt_PF enabled?: %s' % (status.get('Watt_PF'))) ts.log(' Is Volt_Watt enabled?: %s' % (status.get('Volt_Watt'))) ts.log(' Is Scheduled enabled?: %s' % (status.get('Scheduled'))) ts.log(' Is LFRT enabled?: %s' % (status.get('LFRT'))) ts.log(' Is HFRT enabled?: %s' % (status.get('HFRT'))) ts.log('---') status = eut.conn_status() ts.log(' Is PV_Connected?: %s' % (status.get('PV_Connected'))) ts.log(' Is PV_Available?: %s' % (status.get('PV_Available'))) ts.log(' Is PV_Operating?: %s' % (status.get('PV_Operating'))) ts.log(' Is PV_Test?: %s' % (status.get('PV_Test'))) ts.log(' Is Storage_Connected?: %s' % (status.get('Storage_Connected'))) ts.log(' Is Storage_Available?: %s' % (status.get('Storage_Available'))) ts.log(' Is Storage_Operating?: %s' % (status.get('Storage_Operating'))) ts.log(' Is Storage_Test?: %s' % (status.get('Storage_Test'))) ts.log(' Is EPC_Connected?: %s' % (status.get('EPC_Connected'))) ts.log('---') # Monitoring information parameter: Alarm Status # Operating Point A: Has alarms set # Operating Point B: No alarms set # Pass/Fail Criteria: Reported Alarm Status matches the device present alarm condition for alarm and no # alarm conditions. The DER manufacturer shall specify at least one way an alarm condition which is # supported in the protocol being tested can be set and cleared. return script.RESULT_COMPLETE except script.ScriptFail, e: reason = str(e) if reason: ts.log_error(reason)
def test_run(): result = script.RESULT_FAIL daq = None grid = None pv = None eut = None chil = None result_summary = None fw_curves = collections.OrderedDict() try: """ Configuration """ # EUT FW parameters p_rated = ts.param_value('eut_fw.p_rated') f_nom = ts.param_value('eut_fw.f_nom') f_min = ts.param_value('eut_fw.f_max') f_max = ts.param_value('eut_fw.f_min') p_small = ts.param_value('eut_fw.p_small') p_large = ts.param_value('eut_fw.p_large') phases = ts.param_value('eut_fw.phases') eut_absorb = ts.param_value('eut_fw.absorb') eff = { 1.0: ts.param_value('eut_fw.efficiency_100') / 100, 0.66: ts.param_value('eut_fw.efficiency_66') / 100, 0.2: ts.param_value('eut_fw.efficiency_20') / 100, } # Test Parameters mode = ts.param_value('fw.mode') curves = ts.param_value('fw.curves') irr = ts.param_value('fw.irr') """ Equipment Configuration """ # initialize hardware-in-the-loop environment (if applicable) ts.log('Configuring HIL system...') chil = hil.hil_init(ts) if chil is not None: chil.config() # DAS soft channels das_points = { 'sc': ('P_TARGET', 'P_TARGET_MIN', 'P_TARGET_MAX', 'P_ACT', 'F_TARGET', 'F_ACT', 'event') } # initialize data acquisition system daq = das.das_init(ts, sc_points=das_points['sc']) daq.sc['P_TARGET'] = 100 daq.sc['P_TARGET_MIN'] = 100 daq.sc['P_TARGET_MAX'] = 100 daq.sc['F_TARGET'] = f_nom daq.sc['event'] = 'None' """ EUT Configuration """ # Configure the EUT communications eut = der.der_init(ts) # 1547.1: b) Set all frequency trip parameters to the widest range of adjustability. if eut is not None: eut.config() ts.log_debug(eut.measurements()) ts.log_debug( 'L/HFRT and trip parameters set to the widest range : f_min:{0} Hz, f_max:{1} Hz' .format(f_min, f_max)) eut_response = eut.frt_stay_connected_high( params={ 'Ena': True, 'ActCrv': 0, 'Tms1': 3000, 'Hz1': f_max, 'Tms2': 160, 'Hz2': f_max }) ts.log_debug( 'HFRT and trip parameters from EUT : {}'.format(eut_response)) eut_response = eut.frt_stay_connected_low( params={ 'Ena': True, 'ActCrv': 0, 'Tms1': 3000, 'Hz1': f_min, 'Tms2': 160, 'Hz2': f_min }) ts.log_debug( 'LFRT and trip parameters from EUT : {}'.format(eut_response)) else: ts.log_debug( 'Set L/HFRT and trip parameters to the widest range of adjustability possible.' ) # 1547.1: c) Set all AC test source source parameters to the nominal operating voltage and frequency grid = gridsim.gridsim_init(ts) # 1547.1: d) Adjust the EUT's active power to P_rated pv = pvsim.pvsim_init(ts) pv.power_set(p_rated) pv.power_on() """ Test Configuration """ if mode == 'Both': modes = ['Above', 'Below'] elif mode == 'Above': modes = ['Above'] elif mode == 'Below': modes = ['Below'] # 1547.2018 : Using Category III as specified in Table B.1 if curves == 'Characteristic Curve 1' or curves == 'Both': fw_curves[1] = { 'dbf': 0.036, 'kf': 0.05, 'tr': ts.param_value('fw.tr_1'), 'f_small': p_small * f_nom * 0.05 } if curves == 'Characteristic Curve 2' or curves == 'Both': fw_curves[2] = { 'dbf': 0.017, 'kf': 0.02, 'tr': ts.param_value('fw.tr_2'), 'f_small': p_small * f_nom * 0.02 } if irr == 'All': pv_powers = [1., 0.66, 0.2] elif irr == '100%': pv_powers = [1.] elif irr == '66%': pv_powers = [0.66] elif irr == '20%': pv_powers = [0.2] ts.log_debug("Power level tested : {}".format(pv_powers)) if eut_absorb == "Yes": absorb_powers = [False, True] else: absorb_powers = [False] # open result summary file result_summary_filename = 'result_summary.csv' result_summary = open(ts.result_file_path(result_summary_filename), 'a+') ts.result_file(result_summary_filename) result_summary.write('Result,' 'Test Name,' 'Power Level,' 'Mode,' 'Freq_target,' 'Freq_actual,' 'Power_target,' 'Power_actual,' 'P_min,' 'P_max,' 'Dataset File\n') """ Test start """ for mode in modes: if mode == 'Below': # 1547.1 : [5.15.3.2 Procedure] Remove the 66% and 20% power level since not required in 'Below' mode # Set the unit to absorb power at –50% of P rated pv_powers = [1.] # 1547.1 : [5.15.3.2 Procedure] Frequency is ramped at the ROCOF for the category of the EUT. # In this case the ROCOF is based on table 21 of 1547.2018 # (Category III is use because of table B.1 of 1547.2018) # The ROCOF unit : Hz/s ts.log('Set Grid simulator power to 3 Hz/s') grid.rocof(3.0) for absorb_power in absorb_powers: for fw_curve, fw_params in fw_curves.iteritems(): for power in pv_powers: pv_power_setting = (p_rated * power) / eff[power] ts.log( 'Set PV simulator power to {} with efficiency at {} %' .format(p_rated * power, eff[power] * 100.)) pv.power_set(pv_power_setting) result = normal_curve_test( mode=mode, fw_curve=fw_curve, fw_params=fw_params, power=power, daq=daq, eut=eut, grid=grid, result_summary=result_summary, absorb_power=absorb_power) result = script.RESULT_COMPLETE except script.ScriptFail, e: reason = str(e) if reason: ts.log_error(reason)
def test_run(): eut = None chil = None grid = None pv = None try: # Initialize DER configuration eut = der.der_init(ts) eut.config() # Initialize CHIL environment, if necessary chil = hil.hil_init(ts) if chil is not None: chil.config() # PV simulator is initialized with test parameters and enabled pv = pvsim.pvsim_init(ts) pv.irradiance_set(1000) pv.power_on() # grid simulator is initialized with test parameters and enabled grid = gridsim.gridsim_init(ts) # Get EUT nameplate power eut_nameplate_power = eut.nameplate().get('WRtg') inv_power = eut.measurements().get('W') timeout = 20. if inv_power <= eut_nameplate_power / 10.: eut.connect(params={'Conn': True}) pv.irradiance_set(995) # Perturb the pv slightly to start the inverter while inv_power <= eut_nameplate_power / 10. and timeout >= 0: ts.log('Inverter power is at %0.1f. Waiting %s more seconds or until EUT starts...' % (inv_power, timeout)) ts.sleep(1) timeout -= 1 inv_power = eut.measurements().get('W') if timeout == 0: result = script.RESULT_FAIL raise der.DERError('Inverter did not start.') fw_mode = 'Pointwise' if fw_mode == 'Parameters': eut.freq_watt_param(params={'HysEna': False, 'HzStr': 50.2, 'HzStop': 51.5, 'WGra': 140.}) else: # Pointwise eut.freq_watt(params={'ActCrv': 1}) f_points = [50, 50.2, 51.5, 53] p_points = [100, 100, 0, 0] parameters = {'hz': f_points, 'w': p_points} # ts.log_debug(parameters) eut.freq_watt_curve(id=1, params=parameters) eut.freq_watt(params={'Ena': True}) ts.log_debug(eut.freq_watt()) # Create list of frequencies to iterate over freq_values = list(np.linspace(49.5, 53, num=50)) sleep_time = 1.0 for freq in freq_values: grid.freq(freq) # set grid frequency ts.log(' f = %0.3f Hz. Sleeping for %0.2f seconds...' % (freq, sleep_time)) ts.sleep(sleep_time) # Disable the FW function eut.freq_watt(params={'Ena': False}) ts.log('FW Disabled') result = script.RESULT_COMPLETE except Exception, e: ts.log_error('Script failure: %s' % e)
def volt_watt_mode_imbalanced_grid(imbalance_resp, vw_curves, vw_response_time): result = script.RESULT_FAIL daq = None p_rated = None grid = None pv = None eut = None chil = None result_summary = None try: cat = ts.param_value('eut.cat') cat2 = ts.param_value('eut.cat2') p_rated = ts.param_value('eut.p_rated') s_rated = ts.param_value('eut.s_rated') # DC voltages v_in_nom = ts.param_value('eut.v_in_nom') # AC voltages v_nom = ts.param_value('eut.v_nom') v_min = ts.param_value('eut.v_low') v_max = ts.param_value('eut.v_high') p_min = ts.param_value('eut.p_min') phases = ts.param_value('eut.phases') imbalance_fix = ts.param_value('vw.imbalance_fix') # EUI Absorb capabilities absorb = {} absorb['ena'] = ts.param_value('eut_vw.sink_power') absorb['p_rated_prime'] = ts.param_value('eut_vw.p_rated_prime') absorb['p_min_prime'] = ts.param_value('eut_vw.p_min_prime') ''' A separate module has been create for the 1547.1 Standard ''' lib_1547 = p1547.module_1547(ts=ts, aif='VW', imbalance_angle_fix=imbalance_fix) ts.log_debug('1547.1 Library configured for %s' % lib_1547.get_test_name()) # Get the rslt parameters for plot result_params = lib_1547.get_rslt_param_plot() ''' a) Connect the EUT according to the instructions and specifications provided by the manufacturer. ''' # initialize HIL environment, if necessary chil = hil.hil_init(ts) if chil is not None: chil.config() # grid simulator is initialized with test parameters and enabled grid = gridsim.gridsim_init( ts) # Turn on AC so the EUT can be initialized if grid is not None: grid.voltage(v_nom) # pv simulator is initialized with test parameters and enabled pv = pvsim.pvsim_init(ts) if pv is not None: pv.power_set(p_rated) pv.power_on() # Turn on DC so the EUT can be initialized # DAS soft channels # TODO : add to library 1547 das_points = { 'sc': ('P_TARGET', 'P_TARGET_MIN', 'P_TARGET_MAX', 'P_MEAS', 'V_TARGET', 'V_MEAS', 'event') } # initialize data acquisition system daq = das.das_init(ts, sc_points=das_points['sc']) if daq is not None: daq.sc['P_TARGET'] = 100 daq.sc['P_TARGET_MIN'] = 100 daq.sc['P_TARGET_MAX'] = 100 daq.sc['V_TARGET'] = v_nom daq.sc['event'] = 'None' ts.log('DAS device: %s' % daq.info()) ''' b) Set all voltage trip parameters to the widest range of adjustability. Disable all reactive/active power control functions. ''' eut = der.der_init(ts) if eut is not None: eut.config() ts.log_debug( 'L/HVRT and trip parameters set to the widest range : v_min: {0} V, v_max: {1} V' .format(v_min, v_max)) try: eut.vrt_stay_connected_high( params={ 'Ena': True, 'ActCrv': 0, 'Tms1': 3000, 'V1': v_max, 'Tms2': 0.16, 'V2': v_max }) except Exception, e: ts.log_error( 'Could not set VRT Stay Connected High curve. %s' % e) try: eut.vrt_stay_connected_low( params={ 'Ena': True, 'ActCrv': 0, 'Tms1': 3000, 'V1': v_min, 'Tms2': 0.16, 'V2': v_min }) except Exception, e: ts.log_error('Could not set VRT Stay Connected Low curve. %s' % e)
def test_run(): result = script.RESULT_FAIL grid = None pv = p_rated = None daq = None eut = None rs = None phil = None result_summary = None step = None q_initial = None dataset_filename = None try: sink_power = ts.param_value('eut.sink_power') p_rated = ts.param_value('eut.p_rated') p_rated_prime = ts.param_value('eut.p_rated_prime') s_rated = ts.param_value('eut.s_rated') var_rated = ts.param_value('eut.var_rated') # DC voltages v_nom_in_enabled = ts.param_value('cpf.v_in_nom') v_min_in_enabled = ts.param_value('cpf.v_in_min') v_max_in_enabled = ts.param_value('cpf.v_in_max') v_nom_in = ts.param_value('eut.v_in_nom') v_min_in = ts.param_value('eut_cpf.v_in_min') v_max_in = ts.param_value('eut_cpf.v_in_max') # AC voltages v_nom = ts.param_value('eut.v_nom') v_min = ts.param_value('eut.v_low') v_max = ts.param_value('eut.v_high') f_nom = ts.param_value('eut.f_nom') p_min = ts.param_value('eut.p_min') p_min_prime = ts.param_value('eut.p_min_prime') phases = ts.param_value('eut.phases') low_pwr_ena = ts.param_value('vrt.low_pwr_ena') high_pwr_ena = ts.param_value('vrt.high_pwr_ena') low_pwr_value = ts.param_value('vrt.low_pwr_value') high_pwr_value = ts.param_value('vrt.high_pwr_value') # Pass/fail accuracies pf_msa = ts.param_value('eut.pf_msa') # EUI Absorb capabilities absorb = {} absorb['ena'] = ts.param_value('eut_cpf.sink_power') absorb['p_rated_prime'] = ts.param_value('eut_cpf.p_rated_prime') absorb['p_min_prime'] = ts.param_value('eut_cpf.p_min_prime') # Following parameters are collected in p1547.VoltageRideThrough.set_vrt_params in init: # vrt.lv_ena, vrt.hv_ena, vrt.consecutive_ena, vrt.cat, vrt.range_steps consecutive_ena = ts.param_value('vrt.consecutive_ena') if consecutive_ena == "Enabled": consecutive_label = "CE" else: consecutive_label = "CD" phase_comb_list = [] if ts.param_value('vrt.one_phase_mode') == "Enabled": phase_comb_list.append([ts.param_value('vrt.one_phase_value')]) if ts.param_value('vrt.two_phase_mode') == "Enabled": phase_comb_list.append([ts.param_value('vrt.two_phase_value_1'),ts.param_value('vrt.two_phase_value_2')]) if ts.param_value('vrt.three_phase_mode') == "Enabled": phase_comb_list.append(['A', 'B', 'C']) # Functions to be enabled for test mode = [] pwr_lvl = [] steps_dict = {} timestep_dict = {} sequence_dict = {} parameters = [] # initialize HIL environment, if necessary ts.log_debug(15 * "*" + "HIL initialization" + 15 * "*") phil = hil.hil_init(ts) if phil is not None: # return self.ts.param_value(self.group_name + '.' + GROUP_NAME + '.' + name) open_proj = phil._param_value('hil_config_open') compilation = phil._param_value('hil_config_compile') stop_sim = phil._param_value('hil_config_stop_sim') load = phil._param_value('hil_config_load') execute = phil._param_value('hil_config_execute') model_name = phil._param_value('hil_config_model_name') phil.config() ''' RTLab OpWriteFile Math using worst case scenario of 160 seconds, 14 signals and Ts = 40e-6 Duration of acquisition in number of points: Npoints = (Tend-Tstart)/(Ts*dec) = (350)/(0.000040*25) = 1350e3 Acquisition frame duration: Tframe = Nbss * Ts * dec = 1000*0.000040*250 = 10 sec Number of buffers to be acquired: Nbuffers = Npoints / Nbss = (Tend - Tstart) / Tframe = 16 Minimum file size: MinSize= Nbuffers x SizeBuf = [(Tend - Tstart) / Ts ] * (Nsig+1) * 8 * Nbss = (160/40e-6)*(14+1)*8*1000 = 4.8e11 SizeBuf = 1/Nbuffers * {[(Tend - Tstart) / Ts ]*(Nsig+1)*8*Nbss} = [(160/0.000040)*(14+1)*8*1e3]/16 = 30e9 Size of one buffer in bytes (SizeBuf) = (Nsig+1) * 8 * Nbss (Minimum) = (14+1)*8*1000 = 120e3 ''' if low_pwr_ena == 'Enabled': pwr_lvl.append(low_pwr_value) else: ts.log_debug('No low power chosen') if high_pwr_ena == 'Enabled': pwr_lvl.append(high_pwr_value) else: ts.log_debug('No high power chosen') if high_pwr_ena == 'Disabled' and low_pwr_ena == 'Disabled': ts.log_error('No power tests included in VRT test!') if ts.param_value('vrt.wav_ena') == "Yes" : wav_ena = True else : wav_ena = False if ts.param_value('vrt.data_ena') == "Yes" : data_ena = True else : data_ena = False """ Configure settings in 1547.1 Standard module for the Voltage Ride Through Tests """ VoltRideThrough = p1547.VoltageRideThrough(ts=ts, support_interfaces={"hil": phil}) # result params # result_params = lib_1547.get_rslt_param_plot() # ts.log(result_params # grid simulator is initialized with test parameters and enabled ts.log_debug(15 * "*" + "Gridsim initialization" + 15 * "*") grid = gridsim.gridsim_init(ts, support_interfaces={"hil": phil}) # Turn on AC so the EUT can be initialized if grid is not None: grid.voltage(v_nom) # pv simulator is initialized with test parameters and enabled ts.log_debug(15 * "*" + "PVsim initialization" + 15 * "*") pv = pvsim.pvsim_init(ts) if pv is not None: pv.power_set(p_rated) pv.power_on() # Turn on DC so the EUT can be initialized # initialize data acquisition ts.log_debug(15 * "*" + "DAS initialization" + 15 * "*") daq = das.das_init(ts, support_interfaces={"hil": phil, "pvsim": pv}) daq.waveform_config({"mat_file_name":"Data.mat", "wfm_channels": VoltRideThrough.get_wfm_file_header()}) if daq is not None: daq.sc['V_MEAS'] = 100 """ daq.sc['P_MEAS'] = 100 daq.sc['Q_MEAS'] = 100 daq.sc['Q_TARGET_MIN'] = 100 daq.sc['Q_TARGET_MAX'] = 100 daq.sc['PF_TARGET'] = 1 daq.sc['event'] = 'None' ts.log('DAS device: %s' % daq.info()) """ """ This test doesn't have specific procedure steps. """ # open result summary file result_summary_filename = 'result_summary.csv' result_summary = open(ts.result_file_path(result_summary_filename), 'a+') ts.result_file(result_summary_filename) result_summary.write('Test Name, Waveform File, RMS File\n') """ During the LVRT test, the settings for magnitude and duration of undervoltage tripping functions shall be disabled or set so as not to influence the outcome of the test. If the EUT provides a voltage-active power control mode, that mode shall be disabled. Connect the EUT according to the instructions and specifications provided by the manufacturer. """ # Wait to establish communications with the EUT after AC and DC power are provided eut = der.der_init(ts, support_interfaces={'hil': phil}) # start = time.time() # comm_wait_time = max(0.0, startup_time - 60.) # while time.time()-start < comm_wait_time - 1: # ts.sleep(1) # ts.log('Waiting another %0.2f seconds until communicating with EUT' % # (comm_wait_time - (time.time()-start))) if eut is not None: eut.config() # if eut is not None: # eut.deactivate_all_fct() # Initial loop for all mode that will be executed modes = VoltRideThrough.get_modes() # Options: LV_CAT_2, HV_CAT_2, LV_CAT_3, HV_CAT_3 ts.log(f"VRT modes tested : '{modes}'") ts.log(f"VRT power level tested : '{pwr_lvl}'") ts.log(f"VRT phase combination tested : '{phase_comb_list}'") for current_mode in modes: # Configuring waveform timing blocks with offset in seconds # daq.waveform_config(vrt_lib_1547.get_waveform_config(current_mode,offset=5)) """ The ride-through tests shall be performed at two output power levels, high and low, and at any convenient power factor greater than 0.90. The output power levels shall be measured prior to the disturbance, i.e., in test condition A. High-power tests shall be performed at any active power level greater than 90% of the EUT nameplate active power rating at nominal voltage. ... Low-power tests shall be performed at any convenient power level between 25% to 50% of EUT nameplate apparent power rating at nominal voltage. """ for pwr in pwr_lvl: for phase in phase_comb_list : phase_combination_label = "PH" + ''.join(phase) dataset_filename = f'{current_mode}_{round(pwr*100)}PCT_{phase_combination_label}_{consecutive_label}' ts.log_debug(15 * "*" + f"Starting {dataset_filename}" + 15 * "*") if data_ena: daq.data_capture(True) """ Setting up available power to appropriate power level """ if pv is not None: ts.log_debug(f'Setting power level to {pwr}') pv.iv_curve_config(pmp=p_rated, vmp=v_nom_in) pv.irradiance_set(1000.) pv.power_set(p_rated * pwr) """ Initiating voltage sequence for VRT """ vrt_test_sequences = VoltRideThrough.set_test_conditions(current_mode) VoltRideThrough.set_phase_combination(phase) vrt_stop_time = VoltRideThrough.get_vrt_stop_time(vrt_test_sequences) if phil is not None: # Set model parameters #phil.set_parameters(vrt_parameters) # This adds 5 seconds of nominal behavior for EUT normal shutdown. This 5 sec is not recorded. vrt_stop_time = vrt_stop_time + 5 ts.log('Stop time set to %s' % phil.set_stop_time(vrt_stop_time)) # The driver should take care of this by selecting "Yes" to "Load the model to target?" ts.sleep(2.0) phil.load_model_on_hil() # You need to first load the model, then configure the parameters # Now that we have all the test_sequences its time to sent them to the model. VoltRideThrough.set_vrt_model_parameters(vrt_test_sequences) """ The voltage-reactive power control mode of the EUT shall be set to the default settings specified in Table 8 of IEEE Std 1547-2018 for the applicable performance category, and enabled. """ # Default curve is characteristic curve 1 vv_curve = 1 ActiveFunction = p1547.ActiveFunction(ts=ts, script_name='Volt-Var', functions=[VV], criteria_mode=[True, True, True]) # Don't need to be set to imbalance mode #ActiveFunction.set_imbalance_config(imbalance_angle_fix="std") #ActiveFunction.reset_curve(vv_curve) #ActiveFunction.reset_time_settings(tr=10, number_tr=2) v_pairs = ActiveFunction.get_params(function=VV, curve=vv_curve) ts.log_debug('v_pairs:%s' % v_pairs) if eut is not None: # Set to: V = {92, 98, 102, 108}, Var = {44, 0 , 0 , -44} vv_curve_params = {'v': [round(v_pairs['V1']/v_nom,2), round(v_pairs['V2']/v_nom,2), round(v_pairs['V3']/v_nom,2), round(v_pairs['V4']/v_nom,2)], 'var': [round(v_pairs['Q1']/p_rated,2), round(v_pairs['Q2']/p_rated,2), round(v_pairs['Q3']/p_rated,2), round(v_pairs['Q4']/p_rated,2)], 'vref': 1.0, 'RmpPtTms': 1.0} ts.log_debug('Setting VV points: %s' % vv_curve_params) eut.volt_var(params={'Ena': True, 'ACTCRV': vv_curve, 'curve': vv_curve_params}) ts.log_debug('Initial EUT VV settings are %s' % eut.volt_var()) # The driver parameter "Execute the model on target?" should be set to "No" phil.start_simulation() ts.sleep(0.5) sim_time = phil.get_time() while (vrt_stop_time - sim_time) > 1.0: # final sleep will get to stop_time. sim_time = phil.get_time() ts.log('Sim Time: %0.3f. Waiting another %0.3f sec before saving data.' % ( sim_time, vrt_stop_time - sim_time)) ts.sleep(5) rms_dataset_filename = "No File" wave_start_filename = "No File" if data_ena: rms_dataset_filename = dataset_filename + "_RMS.csv" daq.data_capture(False) # complete data capture ts.log('Waiting for Opal to save the waveform data: {}'.format(dataset_filename)) ts.sleep(10) if wav_ena: # Convert and save the .mat file ts.log('Processing waveform dataset(s)') wave_start_filename = dataset_filename + "_WAV.csv" ds = daq.waveform_capture_dataset() # returns list of databases of waveforms (overloaded) ts.log(f'Number of waveforms to save {len(ds)}') if len(ds) > 0: ds[0].to_csv(ts.result_file_path(wave_start_filename)) ts.result_file(wave_start_filename) if data_ena: ds = daq.data_capture_dataset() ts.log('Saving file: %s' % rms_dataset_filename) ds.to_csv(ts.result_file_path(rms_dataset_filename)) ds.remove_none_row(ts.result_file_path(rms_dataset_filename), "TIME") result_params = { 'plot.title': rms_dataset_filename.split('.csv')[0], 'plot.x.title': 'Time (sec)', 'plot.x.points': 'TIME', 'plot.y.points': 'AC_VRMS_1, AC_VRMS_2, AC_VRMS_3', 'plot.y.title': 'Voltage (V)', 'plot.y2.points': 'AC_IRMS_1, AC_IRMS_2, AC_IRMS_3', 'plot.y2.title': 'Current (A)', } ts.result_file(rms_dataset_filename, params=result_params) result_summary.write('%s, %s, %s,\n' % (dataset_filename, wave_start_filename, rms_dataset_filename)) phil.stop_simulation() result = script.RESULT_COMPLETE except script.ScriptFail as e: reason = str(e) if reason: ts.log_error(reason) except Exception as e: ts.log_error((e, traceback.format_exc())) ts.log_error('Test script exception: %s' % traceback.format_exc()) finally: if grid is not None: grid.close() if pv is not None: if p_rated is not None: pv.power_set(p_rated) pv.close() if daq is not None: daq.close() if eut is not None: # eut.fixed_pf(params={'Ena': False, 'PF': 1.0}) eut.close() if rs is not None: rs.close() if phil is not None: if phil.model_state() == 'Model Running': phil.stop_simulation() phil.close() if result_summary is not None: result_summary.close() # create result workbook excelfile = ts.config_name() + '.xlsx' rslt.result_workbook(excelfile, ts.results_dir(), ts.result_dir()) ts.result_file(excelfile) return result
def volt_watt_mode(vw_curves, t_settling, pwr_lvls): result = script.RESULT_FAIL daq = None data = None grid = None pv = None eut = None chil = None result_summary = None try: # result params result_params = { 'plot.title': 'title_name', 'plot.x.title': 'Time (sec)', 'plot.x.points': 'TIME', 'plot.y.points': 'V_TARGET,V_MEAS', 'plot.y.title': 'Voltage (V)', 'plot.V_TARGET.point': 'True', 'plot.y2.points': 'P_TARGET,P_MEAS', 'plot.P_TARGET.point': 'True', 'plot.P_TARGET.min_error': 'P_TARGET_MIN', 'plot.P_TARGET.max_error': 'P_TARGET_MAX', } cat = ts.param_value('eut.cat') cat2 = ts.param_value('eut.cat2') sink_power = ts.param_value('eut.sink_power') p_rated = ts.param_value('eut.p_rated') p_rated_prime = ts.param_value('eut.p_rated_prime') s_rated = ts.param_value('eut.s_rated') eff = { 1.00: ts.param_value('eut.efficiency_100') / 100, 0.66: ts.param_value('eut.efficiency_66') / 100, 0.20: ts.param_value('eut.efficiency_20') / 100 } absorb_enable = ts.param_value('eut.abs_enabled') # DC voltages v_nom_in = ts.param_value('eut.v_in_nom') v_min_in = ts.param_value('eut.v_in_min') v_max_in = ts.param_value('eut.v_in_max') # AC voltages v_nom = ts.param_value('eut.v_nom') v_min = ts.param_value('eut.v_low') v_max = ts.param_value('eut.v_high') p_min = ts.param_value('eut.p_min') p_min_prime = ts.param_value('eut.p_min_prime') phases = ts.param_value('eut.phases') pf_settling_time = ts.param_value('eut.pf_settling_time') imbalance_resp = ts.param_value('eut.imbalance_resp') # Pass/fail accuracies pf_msa = ts.param_value('eut.pf_msa') # According to Table 3-Minimum requirements for manufacturers stated measured and calculated accuracy MSA_Q = 0.05 * s_rated MSA_P = 0.05 * s_rated MSA_V = 0.01 * v_nom a_v = 1.5 * MSA_V ''' a) Connect the EUT according to the instructions and specifications provided by the manufacturer. ''' # initialize HIL environment, if necessary chil = hil.hil_init(ts) if chil is not None: chil.config() # grid simulator is initialized with test parameters and enabled grid = gridsim.gridsim_init(ts) # Turn on AC so the EUT can be initialized if grid is not None: grid.voltage(v_nom) # pv simulator is initialized with test parameters and enabled pv = pvsim.pvsim_init(ts) pv.power_set(p_rated) pv.power_on() # Turn on DC so the EUT can be initialized # DAS soft channels das_points = {'sc': ('P_TARGET', 'P_TARGET_MIN', 'P_TARGET_MAX', 'P_MEAS', 'V_TARGET','V_MEAS','event')} # initialize data acquisition system daq = das.das_init(ts, sc_points=das_points['sc']) daq.sc['P_TARGET'] = 100 daq.sc['P_TARGET_MIN'] = 100 daq.sc['P_TARGET_MAX'] = 100 daq.sc['V_TARGET'] = v_nom daq.sc['event'] = 'None' ts.log('DAS device: %s' % daq.info()) ''' b) Set all voltage trip parameters to the widest range of adjustability. Disable all reactive/active power control functions. ''' # it is assumed the EUT is on eut = der.der_init(ts) if eut is not None: vw_curve_params = {'v': [v_start, v_stop], 'w': [100., 0], 'DeptRef': 'W_MAX_PCT'} vw_params = {'Ena': True, 'ActCrv': 1, 'curve': vw_curve_params} eut.volt_watt(params=vw_params) ts.log_debug('Initial EUT VW settings are %s' % eut.volt_watt()) ''' c) Set all AC test source parameters to the nominal operating voltage and frequency. ''' if grid is not None: grid.voltage(v_nom) # open result summary file result_summary_filename = 'result_summary.csv' result_summary = open(ts.result_file_path(result_summary_filename), 'a+') ts.result_file(result_summary_filename) result_summary.write('Result,Test Name,Power Level,Iteration,direction,V_target,V_actual,Power_target,Power_actual,P_min,P_max,Dataset File\n') ''' d) Adjust the EUT's available active power to Prated. For an EUT with an input voltage range, set the input voltage to Vin_nom. The EUT may limit active power throughout the test to meet reactive power requirements. For an EUT with an input voltage range, repeat steps d) through o) for Vin_min and Vin_max. ''' #if pv is not None: # TODO implement IV_curve_config #pv.iv_curve_config(pmp=p_rated, vpm=v_nom) #pv.iv_curve_config(pmp=p_rated, vpm=v_in) #pv.irradiance_set(1000.) ''' e) Set EUT volt-watt parameters to the values specified by Characteristic 1. All other function be turned off. ''' v_pairs = collections.OrderedDict()#{} v_pairs[1] = {'V1': round(1.06 * v_nom, 2), 'V2': round(1.10 * v_nom, 2), 'P1': round(p_rated, 2)} v_pairs[2] = {'V1': round(1.05 * v_nom, 2), 'V2': round(1.10 * v_nom, 2), 'P1': round(p_rated, 2)} v_pairs[3] = {'V1': round(1.09 * v_nom, 2), 'V2': round(1.10 * v_nom, 2), 'P1': round(p_rated, 2)} if absorb_enable == 'Yes': v_pairs[1].add('P2', 0) v_pairs[2].add('P2', p_rated_prime) v_pairs[3].add('P2', p_rated_prime) else: if p_min > (0.2 * p_rated): v_pairs[1]['P2'] = int(0.2 * p_rated) v_pairs[2]['P2'] = int(0.2 * p_rated) v_pairs[3]['P2'] = int(0.2 * p_rated) else: v_pairs[1]['P2'] = int(p_min) v_pairs[2]['P2'] = int(p_min) v_pairs[3]['P2'] = int(p_min) ''' f) Verify volt-watt mode is reported as active and that the correct characteristic is reported. g) Begin the adjustment towards V_h. Step the AC test source voltage to a_v below V_1. t) Repeat steps d) through t) at EUT power set at 20% and 66% of rated power. u) Repeat steps d) through u) for characteristics 2 and 3. v) Test may be repeated for EUT's that can also absorb power using the P' values in the characteristic definition. ''' """ Test start """ for test, vw_curve in vw_curves.iteritems(): ts.log('Starting test with VW mode at %s' % (test)) v_steps_dict = collections.OrderedDict() # 1547.1 : v_steps_up = [(v_pairs[vw_curve]['V1'] - a_v), # step g (v_pairs[vw_curve]['V1'] + a_v), # step h (v_pairs[vw_curve]['V2'] + v_pairs[vw_curve]['V1']) / 2, # step i v_pairs[vw_curve]['V2'] - a_v, # step j v_pairs[vw_curve]['V2'] + a_v, # step k v_max - a_v] # step l v_steps_down = [v_pairs[vw_curve]['V2'] + a_v, # step m v_pairs[vw_curve]['V2'] - a_v, # step n (v_pairs[vw_curve]['V1'] + v_pairs[vw_curve]['V2']) / 2, # step o v_pairs[vw_curve]['V1'] + a_v, # step p v_pairs[vw_curve]['V1'] - a_v, # step q v_min + a_v] # step s for i in range(len(v_steps_up)): if v_steps_up[i] > v_max: v_steps_up[i] = v_max elif v_steps_up[i] < v_min: v_steps_up[i] = v_min for i in range(len(v_steps_down)): if v_steps_down[i] > v_max: v_steps_down[i] = v_max elif v_steps_down[i] < v_min: v_steps_down[i] = v_min v_steps_dict['up'] = np.around(v_steps_up, decimals=2) v_steps_dict['down'] = np.around(v_steps_down, decimals=2) ts.log('Testing VW function at the following voltage(up) points %s' % v_steps_dict['up']) ts.log('Testing VW function at the following voltage(down) points %s' % v_steps_dict['down']) for power in pwr_lvls: if pv is not None: # TODO implement IV_curve_config pv.power_set(power) #pv_power_setting = (p_rated * power) / eff[power] #pv.iv_curve_config(pmp=pv_power_setting, vpm=v_in) #pv.irradiance_set(1000.) #ts.log('Set PV simulator power to {} with efficiency at {} %'.format(p_rated * power, eff[power] * 100.)) ts.log_debug('curve points: %s' % v_pairs[vw_curve]) # Configure the data acquisition system ts.log('Starting data capture for power = %s' % power) dataset_filename = 'VW_curve_%s_pwr_%0.2f.csv' % (vw_curve, power) daq.data_capture(True) for direction, v_steps in v_steps_dict.iteritems(): for v_step in v_steps: ts.log(' Recording power at voltage %0.2f V for 4*t_settling = %0.1f sec.' % (v_step, 4 * t_settling[vw_curve])) daq.sc['V_TARGET'] = v_step daq.sc['event'] = 'v_step_{}'.format(direction) p_targ = interpolation_v_p(value=v_step, v_pairs=v_pairs[vw_curve]) grid.voltage(v_step) for i in range(4): daq.sc['event'] = 'v_step_{}'.format(direction) ts.sleep(1 * t_settling[vw_curve]) daq.sc['event'] = 'TR_{}_done'.format(i + 1) daq.data_sample() data = daq.data_capture_read() daq.sc['P_TARGET'] = p_targ # Test result accuracy requirements per IEEE1547-4.2 for Q(V) P_V_passfail = p_v_criteria(v_pairs=v_pairs[vw_curve], a_v=a_v, p_mra=MSA_P, daq=daq, imbalance_resp=imbalance_resp) #data=data) # Test result accuracy requirements per IEEE1547-4.2 for Q(tr) # TODO p_tr_criteria still needs to be implemented ts.log(' Powers targ, min, max: %s, %s, %s' % ( daq.sc['P_TARGET'], daq.sc['P_TARGET_MIN'], daq.sc['P_TARGET_MAX'])) daq.sc['event'] = 'T_settling_done_{}'.format(direction) daq.data_sample() result_summary.write('%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s \n' % (P_V_passfail, ts.config_name(), power * 100., direction, daq.sc['V_TARGET'], daq.sc['V_MEAS'], daq.sc['P_TARGET'], daq.sc['P_MEAS'], daq.sc['P_TARGET_MIN'], daq.sc['P_TARGET_MAX'], dataset_filename)) # create result workbook ts.log('Sampling complete') daq.data_capture(False) ds = daq.data_capture_dataset() ts.log('Saving file: %s' % dataset_filename) ds.to_csv(ts.result_file_path(dataset_filename)) result_params['plot.title'] = os.path.splitext(dataset_filename)[0] ts.result_file(dataset_filename, params=result_params) result = script.RESULT_COMPLETE return result except script.ScriptFail, e: reason = str(e) if reason: ts.log_error(reason)
def volt_vars_mode(vv_curves, vv_response_time, pwr_lvls, v_ref_value): result = script.RESULT_FAIL daq = None v_nom = None grid = None pv = None eut = None chil = None result_summary = None dataset_filename = None try: cat = ts.param_value('eut.cat') cat2 = ts.param_value('eut.cat2') sink_power = ts.param_value('eut.sink_power') p_rated = ts.param_value('eut.p_rated') p_rated_prime = ts.param_value('eut.p_rated_prime') var_rated = ts.param_value('eut.var_rated') s_rated = ts.param_value('eut.s_rated') #absorb_enable = ts.param_value('eut.abs_enabled') # DC voltages v_in_nom = ts.param_value('eut.v_in_nom') #v_min_in = ts.param_value('eut.v_in_min') #v_max_in = ts.param_value('eut.v_in_max') # AC voltages v_nom = ts.param_value('eut.v_nom') v_low = ts.param_value('eut.v_low') v_high = ts.param_value('eut.v_high') p_min = ts.param_value('eut.p_min') p_min_prime = ts.param_value('eut.p_min_prime') phases = ts.param_value('eut.phases') """ Version validation """ p1547.VersionValidation(script_version=ts.info.version) """ A separate module has been create for the 1547.1 Standard """ ActiveFunction = p1547.ActiveFunction(ts=ts, functions=[VV], script_name='Volt-Var', criteria_mode=[True, True, True]) ts.log_debug("1547.1 Library configured for %s" % ActiveFunction.get_script_name()) # result params result_params = ActiveFunction.get_rslt_param_plot() ts.log_debug(result_params) ''' a) Connect the EUT according to the instructions and specifications provided by the manufacturer. ''' ts.log_debug(15*"*"+"HIL initialization"+15*"*") # initialize HIL environment, if necessary chil = hil.hil_init(ts) if chil is not None: chil.config() ts.log_debug(15*"*"+"PVSIM initialization"+15*"*") # pv simulator is initialized with test parameters and enabled pv = pvsim.pvsim_init(ts, support_interfaces={'hil': chil}) if pv is not None: pv.power_set(p_rated) pv.power_on() # Turn on DC so the EUT can be initialized #daq.set_dc_measurement(pv) # send pv obj to daq to get dc measurements ts.sleep(0.5) # DAS soft channels ts.log_debug(15*"*"+"DAS initialization"+15*"*") #das_points = {'sc': ('Q_TARGET', 'Q_TARGET_MIN', 'Q_TARGET_MAX', 'Q_MEAS', 'V_TARGET', 'V_MEAS', 'event')} das_points = ActiveFunction.get_sc_points() # initialize data acquisition system daq = das.das_init(ts, sc_points=das_points['sc'], support_interfaces={'hil': chil}) daq.sc['V_TARGET'] = v_nom daq.sc['Q_TARGET'] = 100 daq.sc['Q_TARGET_MIN'] = 100 daq.sc['Q_TARGET_MAX'] = 100 daq.sc['event'] = 'None' ts.log('DAS device: %s' % daq.info()) ''' b) Set all voltage trip parameters to the widest range of adjustability. Disable all reactive/active power control functions. ''' ts.log_debug(15*"*"+"EUT initialization"+15*"*") eut = der.der_init(ts, support_interfaces={'hil': chil}) if eut is not None: eut.config() ts.log_debug(eut.measurements()) #Deactivating all functions on EUT #eut.deactivate_all_fct() ts.log_debug('Voltage trip parameters set to the widest range: v_min: {0} V, ' 'v_max: {1} V'.format(v_low, v_high)) try: eut.vrt_stay_connected_high(params={'Ena': True, 'ActCrv': 0, 'Tms1': 3000, 'V1': v_high, 'Tms2': 0.16, 'V2': v_high}) except Exception as e: ts.log_error('Could not set VRT Stay Connected High curve. %s' % e) try: eut.vrt_stay_connected_low(params={'Ena': True, 'ActCrv': 0, 'Tms1': 3000, 'V1': v_low, 'Tms2': 0.16, 'V2': v_low}) except Exception as e: ts.log_error('Could not set VRT Stay Connected Low curve. %s' % e) else: ts.log_debug('Set L/HVRT and trip parameters set to the widest range of adjustability possible.') # # Special considerations for CHIL ASGC/Typhoon startup if chil is not None: if eut is not None: if eut.measurements() is not None: inv_power = eut.measurements().get('W') timeout = 120. if inv_power <= p_rated * 0.85: pv.irradiance_set(995) # Perturb the pv slightly to start the inverter ts.sleep(3) eut.connect(params={'Conn': True}) while inv_power <= p_rated * 0.85 and timeout >= 0: ts.log('Inverter power is at %0.1f. Waiting up to %s more seconds or until EUT starts...' % (inv_power, timeout)) ts.sleep(1) timeout -= 1 inv_power = eut.measurements().get('W') if timeout == 0: result = script.RESULT_FAIL raise der.DERError('Inverter did not start.') ts.log('Waiting for EUT to ramp up') ts.sleep(8) ts.log_debug('DAS data_read(): %s' % daq.data_read()) ''' c) Set all AC test source parameters to the nominal operating voltage and frequency. ''' ts.log_debug(15*"*"+"GRIDSIM initialization"+15*"*") grid = gridsim.gridsim_init(ts,support_interfaces={'hil': chil}) # Turn on AC so the EUT can be initialized if grid is not None: grid.voltage(v_nom) if chil is not None: # If using HIL, give the grid simulator the hil object grid.config() # open result summary file result_summary_filename = 'result_summary.csv' result_summary = open(ts.result_file_path(result_summary_filename), 'a+') ts.result_file(result_summary_filename) result_summary.write(ActiveFunction.get_rslt_sum_col_name()) ''' d) Adjust the EUT's available active power to Prated. For an EUT with an input voltage range, set the input voltage to Vin_nom. The EUT may limit active power throughout the test to meet reactive power requirements. For an EUT with an input voltage range. ''' if pv is not None: pv.iv_curve_config(pmp=p_rated, vmp=v_in_nom) pv.irradiance_set(1000.) ''' gg) Repeat steps g) through dd) for characteristics 2 and 3. ''' for vv_curve in vv_curves: ts.log('Starting test with characteristic curve %s' % (vv_curve)) ActiveFunction.reset_curve(vv_curve) ActiveFunction.reset_time_settings(tr=vv_response_time[vv_curve], number_tr=2) v_pairs = ActiveFunction.get_params(function=VV, curve=vv_curve) #ts.log_debug('v_pairs:%s' % v_pairs) ''' ff) Repeat test steps d) through ee) at EUT power set at 20% and 66% of rated power. ''' for power in pwr_lvls: ActiveFunction.reset_pwr(power) if pv is not None: pv_power_setting = (p_rated * power) pv.iv_curve_config(pmp=pv_power_setting, vmp=v_in_nom) pv.irradiance_set(1000.) # Special considerations for CHIL ASGC/Typhoon startup # # Why does it need to appear twice, shouldn't this be at the driver level if chil is not None: if eut is not None: if eut.measurements() is not None: inv_power = eut.measurements().get('W') timeout = 120. if inv_power <= pv_power_setting * 0.85: pv.irradiance_set(995) # Perturb the pv slightly to start the inverter ts.sleep(3) eut.connect(params={'Conn': True}) while inv_power <= pv_power_setting * 0.85 and timeout >= 0: ts.log('Inverter power is at %0.1f. Waiting up to %s more seconds or until EUT starts...' % (inv_power, timeout)) ts.sleep(1) timeout -= 1 inv_power = eut.measurements().get('W') if timeout == 0: result = script.RESULT_FAIL raise der.DERError('Inverter did not start.') ts.log('Waiting for EUT to ramp up') ts.sleep(8) ''' ee) Repeat test steps e) through dd) with Vref set to 1.05*VN and 0.95*VN, respectively. ''' for v_ref in v_ref_value: ts.log('Setting v_ref at %s %% of v_nom' % (int(v_ref * 100))) #Setting grid to vnom before test if grid is not None: grid.voltage(v_nom) if eut is not None: ''' e) Set EUT volt-var parameters to the values specified by Characteristic 1. All other function should be turned off. Turn off the autonomously adjusting reference voltage. ''' # Activate volt-var function with following parameters # SunSpec convention is to use percentages for V and Q points. vv_curve_params = { 'v': [(v_pairs['V1'] / v_nom) , (v_pairs['V2'] / v_nom) , (v_pairs['V3'] / v_nom), (v_pairs['V4'] / v_nom) ], 'var': [(v_pairs['Q1'] / s_rated), (v_pairs['Q2'] / s_rated) , (v_pairs['Q3'] / s_rated) , (v_pairs['Q4'] / s_rated)], 'vref': v_ref, 'RmpPtTms': vv_response_time[vv_curve] } ts.log_debug('Sending VV points: %s' % vv_curve_params) eut.volt_var(params={'Ena': True, 'ACTCRV': vv_curve, 'curve': vv_curve_params}) # TODO autonomous vref adjustment to be included # eut.autonomous_vref_adjustment(params={'Ena': False}) ''' f) Verify volt-var mode is reported as active and that the correct characteristic is reported. ''' ts.log_debug('Initial EUT VV settings are %s' % eut.volt_var()) if chil is not None: ts.log('Start simulation of CHIL') chil.start_simulation() v_steps_dict = ActiveFunction.create_vv_dict_steps(v_ref=v_ref) dataset_filename = 'VV_%s_PWR_%d_vref_%d' % (vv_curve, power * 100, v_ref*100) ActiveFunction.reset_filename(filename=dataset_filename) #ts.log('------------{}------------'.format(dataset_filename)) # Start the data acquisition systems daq.data_capture(True) for step_label, v_step in v_steps_dict.items(): ts.log('Voltage step: setting Grid simulator voltage to %s (%s)' % (v_step, step_label)) ActiveFunction.start(daq=daq, step_label=step_label) step_dict = {'V': v_step} if grid is not None: grid.voltage(step_dict['V']) ActiveFunction.record_timeresponse(daq=daq) ActiveFunction.evaluate_criterias(daq=daq, step_dict=step_dict) result_summary.write(ActiveFunction.write_rslt_sum()) ts.log('Sampling complete') dataset_filename = dataset_filename + ".csv" daq.data_capture(False) ds = daq.data_capture_dataset() ts.log('Saving file: %s' % dataset_filename) ds.to_csv(ts.result_file_path(dataset_filename)) result_params['plot.title'] = dataset_filename.split('.csv')[0] ts.result_file(dataset_filename, params=result_params) result = script.RESULT_COMPLETE except script.ScriptFail as e: reason = str(e) if reason: ts.log_error(reason) except Exception as e: if dataset_filename is not None: dataset_filename = dataset_filename + ".csv" daq.data_capture(False) ds = daq.data_capture_dataset() ts.log('Saving file: %s' % dataset_filename) ds.to_csv(ts.result_file_path(dataset_filename)) result_params['plot.title'] = dataset_filename.split('.csv')[0] ts.result_file(dataset_filename, params=result_params) ts.log_error('Test script exception: %s' % traceback.format_exc()) finally: if daq is not None: daq.close() if pv is not None: pv.close() if grid is not None: if v_nom is not None: grid.voltage(v_nom) grid.close() if chil is not None: chil.close() if eut is not None: #eut.volt_var(params={'Ena': False}) eut.close() if result_summary is not None: result_summary.close() return result
def volt_watt_mode_imbalanced_grid(imbalance_resp, vw_curves = 1): result = script.RESULT_FAIL daq = None data = None grid = None pv = None eut = None chil = None result_summary = None try: # result params result_params = { 'plot.title': 'title_name', 'plot.x.title': 'Time (sec)', 'plot.x.points': 'TIME', 'plot.y.points': 'V_TARGET,V_MEAS', 'plot.y.title': 'Voltage (V)', 'plot.V_TARGET.point': 'True', 'plot.y2.points': 'P_TARGET,P_MEAS', 'plot.P_TARGET.point': 'True', 'plot.P_TARGET.min_error': 'P_TARGET_MIN', 'plot.P_TARGET.max_error': 'P_TARGET_MAX', } cat = ts.param_value('eut.cat') cat2 = ts.param_value('eut.cat2') sink_power = ts.param_value('eut.sink_power') p_rated = ts.param_value('eut.p_rated') p_rated_prime = ts.param_value('eut.p_rated_prime') s_rated = ts.param_value('eut.s_rated') eff = { 1.00: ts.param_value('eut.efficiency_100') / 100, 0.66: ts.param_value('eut.efficiency_66') / 100, 0.20: ts.param_value('eut.efficiency_20') / 100 } absorb_enable = ts.param_value('eut.abs_enabled') # DC voltages v_nom_in = ts.param_value('eut.v_in_nom') v_min_in = ts.param_value('eut.v_in_min') v_max_in = ts.param_value('eut.v_in_max') # AC voltages v_nom = ts.param_value('eut.v_nom') v_min = ts.param_value('eut.v_low') v_max = ts.param_value('eut.v_high') p_min = ts.param_value('eut.p_min') p_min_prime = ts.param_value('eut.p_min_prime') phases = ts.param_value('eut.phases') pf_settling_time = ts.param_value('eut.pf_settling_time') imbalance_resp = ts.param_value('eut.imbalance_resp') # Pass/fail accuracies pf_msa = ts.param_value('eut.pf_msa') # According to Table 3-Minimum requirements for manufacturers stated measured and calculated accuracy MSA_Q = 0.05 * s_rated MSA_P = 0.05 * s_rated MSA_V = 0.01 * v_nom a_v = 1.5 * MSA_V ''' a) Connect the EUT according to the instructions and specifications provided by the manufacturer. ''' # initialize HIL environment, if necessary chil = hil.hil_init(ts) if chil is not None: chil.config() # grid simulator is initialized with test parameters and enabled grid = gridsim.gridsim_init(ts) # Turn on AC so the EUT can be initialized if grid is not None: grid.voltage(v_nom) # pv simulator is initialized with test parameters and enabled pv = pvsim.pvsim_init(ts) pv.power_set(p_rated) pv.power_on() # Turn on DC so the EUT can be initialized # DAS soft channels das_points = {'sc': ('P_TARGET', 'P_TARGET_MIN', 'P_TARGET_MAX', 'P_MEAS', 'V_TARGET', 'V_MEAS', 'event')} # initialize data acquisition system daq = das.das_init(ts, sc_points=das_points['sc']) daq.sc['P_TARGET'] = 100 daq.sc['P_TARGET_MIN'] = 100 daq.sc['P_TARGET_MAX'] = 100 daq.sc['V_TARGET'] = v_nom daq.sc['event'] = 'None' ts.log('DAS device: %s' % daq.info()) ''' b) Set all voltage trip parameters to the widest range of adjustability. Disable all reactive/active power control functions. ''' # it is assumed the EUT is on eut = der.der_init(ts) if eut is not None: vw_curve_params = {'v': [v_start, v_stop], 'w': [100., 0], 'DeptRef': 'W_MAX_PCT'} vw_params = {'Ena': True, 'ActCrv': 1, 'curve': vw_curve_params} eut.volt_watt(params=vw_params) ts.log_debug('Initial EUT VW settings are %s' % eut.volt_watt()) ''' c) Set all AC test source parameters to the nominal operating voltage and frequency. ''' if grid is not None: grid.voltage(v_nom) # open result summary file result_summary_filename = 'result_summary.csv' result_summary = open(ts.result_file_path(result_summary_filename), 'a+') ts.result_file(result_summary_filename) result_summary.write( 'Result,Test Name,Power Level,Iteration,direction,V_target,V_actual,Power_target,Power_actual,P_min,P_max,Dataset File\n') ''' d) Adjust the EUT's available active power to Prated. For an EUT with an input voltage range, set the input voltage to Vin_nom. The EUT may limit active power throughout the test to meet reactive power requirements. For an EUT with an input voltage range, repeat steps d) through o) for Vin_min and Vin_max. ''' # if pv is not None: # TODO implement IV_curve_config # pv.iv_curve_config(pmp=p_rated, vpm=v_nom) # pv.iv_curve_config(pmp=p_rated, vpm=v_in) # pv.irradiance_set(1000.) ''' e) Set EUT volt-watt parameters to the values specified by Characteristic 1. All other function be turned off. ''' v_pairs = collections.OrderedDict() # {} v_pairs[1] = {'V1': round(1.06 * v_nom, 2), 'V2': round(1.10 * v_nom, 2), 'P1': round(p_rated, 2)} if absorb_enable == 'Yes': v_pairs[1].add('P2', 0) else: if p_min > (0.2 * p_rated): v_pairs[1]['P2'] = int(0.2 * p_rated) else: v_pairs[1]['P2'] = int(p_min) ''' f) Verify volt-var mode is reported as active and that the correct characteristic is reported. g) Once steady state is reached, begin the adjustment of phase voltages. t) Repeat steps d) through t) at EUT power set at 20% and 66% of rated power. u) Repeat steps d) through u) for characteristics 2 and 3. v) Test may be repeated for EUT's that can also absorb power using the P' values in the characteristic definition. ''' """ Test start """ for imbalance_response in imbalance_resp: ts.log('Starting imbalance test with VW mode at %s' % (imbalance_response)) if pv is not None: # TODO implement IV_curve_config pv.power_set(power) # pv_power_setting = (p_rated * power) / eff[power] # pv.iv_curve_config(pmp=pv_power_setting, vpm=v_in) # pv.irradiance_set(1000.) # ts.log('Set PV simulator power to {} with efficiency at {} %'.format(p_rated * power, eff[power] * 100.)) # Configure the data acquisition system ts.log('Starting data capture for power = %s' % power) dataset_filename = 'VW_curve_%s_pwr_%0.2f.csv' % (vw_curve, power) daq.data_capture(True) ts.log_debug('curve points: %s' % v_pairs[vw_curve]) ''' l) For multiphase units, step the AC test source voltage to Case A from Table 23. Table 23 - Imbalanced Voltage Test Cases 12 +----------------------------------------------+-----------------------------------------------+ | Symmetrical Components | Phasor Components | +----------------------------------------------+-----------------------------------------------+ | Zero Sequence | Positive Seq | Negative Seq | Phase A | Phase B | Phase C | | Mag | Angle | Mag | Angle | Mag | Angle | Mag | Angle| Mag | Angle | Mag | Angle | +-------+-----+---------+-----+--------+-------+-------+-------+------+-------+-------+-------+--------+ |Case A | 0.0 | 0.0 | 1.0 | 0.0 | 0.07 | 0 | 1.070 | 0.0 | 0.967 | 123.6 | 0.967 | -123.6 | +-------+-----+---------+-----+--------+-------+-------+-------+------+-------+-------+-------+--------+ |Case B | 0.0 | 0.0 | 1.0 | 0.0 | 0.09 | 180 | 0.910 | 0.0 | 1.048 | 115.7 | 1.048 | -115.7 | +-------+-----+---------+-----+--------+-------+-------+-------+------+-------+-------+-------+--------+ |Case C | 0.0 | 0.0 | 1.0 | 0.0 | 0.05 | 0 | 1.050 | 0.0 | 0.976 | 122.5 | 0.976 | -122.5 | +-------+-----+---------+-----+--------+-------+-------+-------+------+-------+-------+-------+--------+ |Case D | 0.0 | 0.0 | 1.0 | 0.0 | 0.05 | 180 | 0.950 | 0.0 | 1.026 | 117.6 | 1.026 | -117.6 | +-------+-----+---------+-----+--------+-------+-------+-------+------+-------+-------+-------+--------+ For tests with imbalanced, three-phase voltages, the manufacturer shall state whether the EUT responds to individual phase voltages, or the average of the three-phase effective (RMS) values or the positive sequence of voltages. For EUTs that respond to individual phase voltages, the response of each individual phase shall be evaluated. For EUTs that response to the average of the three-phase effective (RMS) values mor the positive sequence of voltages, the total three-phase reactive and active power shall be evaluated. ''' ''' Step i) For multiphase units, step the AC test source voltage to Case A from Table 23 ''' if grid is not None: grid.config_asymmetric_phase_angles(mag=[1.07 * v_nom, 0.967 * v_nom, 0.967 * v_nom], angle=[0., 123.6, -123.6]) daq.sc['event'] = 'Case A' ts.sleep(4 * pf_settling_time) daq.sc['event'] = 'T_settling_done' # Test result accuracy for CPF daq.data_sample() data = daq.data_capture_read() # Test result accuracy requirements per IEEE1547-4.2 for Q(V) P_V_passfail = p_v_criteria(v_pairs=v_pairs[vw_curve], a_v=a_v, p_mra=MSA_P, daq=daq, imbalance_resp=imbalance_response) # Test result accuracy requirements per IEEE1547-4.2 for Q(tr) # TODO p_tr_criteria still needs to be implemented ts.log(' Powers targ, min, max: %s, %s, %s' % ( daq.sc['P_TARGET'], daq.sc['P_TARGET_MIN'], daq.sc['P_TARGET_MAX'])) result_summary.write('%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s \n' % (P_V_passfail, ts.config_name(), power * 100., direction, daq.sc['V_TARGET'], daq.sc['V_MEAS'], daq.sc['P_TARGET'], daq.sc['P_MEAS'], daq.sc['P_TARGET_MIN'], daq.sc['P_TARGET_MAX'], dataset_filename)) ''' Step j) For multiphase units, step the AC test source voltage to VN. ''' if grid is not None: grid.voltage(v_nom) daq.sc['event'] = 'Step J' ts.sleep(4 * pf_settling_time) daq.sc['event'] = 'T_settling_done' # Test result accuracy for CPF daq.data_sample() data = daq.data_capture_read() # Test result accuracy requirements per IEEE1547-4.2 for Q(V) P_V_passfail = p_v_criteria(v_pairs=v_pairs[vw_curve], a_v=a_v, p_mra=MSA_P, daq=daq, imbalance_resp=imbalance_response) # Test result accuracy requirements per IEEE1547-4.2 for Q(tr) # TODO p_tr_criteria still needs to be implemented ts.log(' Powers targ, min, max: %s, %s, %s' % ( daq.sc['P_TARGET'], daq.sc['P_TARGET_MIN'], daq.sc['P_TARGET_MAX'])) result_summary.write('%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s \n' % (P_V_passfail, ts.config_name(), power * 100., direction, daq.sc['V_TARGET'], daq.sc['V_MEAS'], daq.sc['P_TARGET'], daq.sc['P_MEAS'], daq.sc['P_TARGET_MIN'], daq.sc['P_TARGET_MAX'], dataset_filename)) ''' Step k) For multiphase units, step the AC test source voltage to Case B from Table 23 ''' if grid is not None: grid.config_asymmetric_phase_angles(mag=[0.910 * v_nom, 1.048 * v_nom, 1.048 * v_nom], angle=[0., 115.7, -115.7]) daq.sc['event'] = 'Case B' ts.sleep(4 * pf_settling_time) daq.sc['event'] = 'T_settling_done' # Test result accuracy for CPF daq.data_sample() data = daq.data_capture_read() # Test result accuracy requirements per IEEE1547-4.2 for Q(V) P_V_passfail = p_v_criteria(v_pairs=v_pairs[vw_curve], a_v=a_v, p_mra=MSA_P, daq=daq, imbalance_resp=imbalance_response) # Test result accuracy requirements per IEEE1547-4.2 for Q(tr) # TODO p_tr_criteria still needs to be implemented ts.log(' Powers targ, min, max: %s, %s, %s' % ( daq.sc['P_TARGET'], daq.sc['P_TARGET_MIN'], daq.sc['P_TARGET_MAX'])) result_summary.write('%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s \n' % (P_V_passfail, ts.config_name(), power * 100., direction, daq.sc['V_TARGET'], daq.sc['V_MEAS'], daq.sc['P_TARGET'], daq.sc['P_MEAS'], daq.sc['P_TARGET_MIN'], daq.sc['P_TARGET_MAX'], dataset_filename)) ''' Step l) For multiphase units, step the AC test source voltage to VN. ''' if grid is not None: grid.voltage(v_nom) daq.sc['event'] = 'Step J' ts.sleep(4 * pf_settling_time) daq.sc['event'] = 'T_settling_done' # Test result accuracy for CPF daq.data_sample() data = daq.data_capture_read() # Test result accuracy requirements per IEEE1547-4.2 for Q(V) P_V_passfail = p_v_criteria(v_pairs=v_pairs[vw_curve], a_v=a_v, p_mra=MSA_P, daq=daq, imbalance_resp=imbalance_response) # Test result accuracy requirements per IEEE1547-4.2 for Q(tr) # TODO p_tr_criteria still needs to be implemented ts.log(' Powers targ, min, max: %s, %s, %s' % ( daq.sc['P_TARGET'], daq.sc['P_TARGET_MIN'], daq.sc['P_TARGET_MAX'])) result_summary.write('%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s \n' % (P_V_passfail, ts.config_name(), power * 100., direction, daq.sc['V_TARGET'], daq.sc['V_MEAS'], daq.sc['P_TARGET'], daq.sc['P_MEAS'], daq.sc['P_TARGET_MIN'], daq.sc['P_TARGET_MAX'], dataset_filename)) # create result workbook ts.log('Sampling complete') daq.data_capture(False) ds = daq.data_capture_dataset() ts.log('Saving file: %s' % dataset_filename) ds.to_csv(ts.result_file_path(dataset_filename)) result_params['plot.title'] = os.path.splitext(dataset_filename)[0] ts.result_file(dataset_filename, params=result_params) result = script.RESULT_COMPLETE return result except script.ScriptFail, e: reason = str(e) if reason: ts.log_error(reason)
def volt_vars_mode(vv_curves, vv_response_time, pwr_lvls, v_ref_value): result = script.RESULT_FAIL daq = None v_nom = None grid = None pv = None eut = None chil = None result_summary = None dataset_filename = None try: cat = ts.param_value('eut.cat') cat2 = ts.param_value('eut.cat2') sink_power = ts.param_value('eut.sink_power') p_rated = ts.param_value('eut.p_rated') p_rated_prime = ts.param_value('eut.p_rated_prime') var_rated = ts.param_value('eut.var_rated') s_rated = ts.param_value('eut.s_rated') #absorb_enable = ts.param_value('eut.abs_enabled') # DC voltages v_in_nom = ts.param_value('eut.v_in_nom') #v_min_in = ts.param_value('eut.v_in_min') #v_max_in = ts.param_value('eut.v_in_max') # AC voltages v_nom = ts.param_value('eut.v_nom') v_low = ts.param_value('eut.v_low') v_high = ts.param_value('eut.v_high') p_min = ts.param_value('eut.p_min') p_min_prime = ts.param_value('eut.p_min_prime') phases = ts.param_value('eut.phases') """ A separate module has been create for the 1547.1 Standard """ lib_1547 = p1547.module_1547(ts=ts, aif='VV') ts.log_debug("1547.1 Library configured for %s" % lib_1547.get_test_name()) # result params result_params = lib_1547.get_rslt_param_plot() ''' a) Connect the EUT according to the instructions and specifications provided by the manufacturer. ''' # initialize HIL environment, if necessary chil = hil.hil_init(ts) if chil is not None: chil.config() # pv simulator is initialized with test parameters and enabled pv = pvsim.pvsim_init(ts) if pv is not None: pv.power_set(p_rated) pv.power_on() # Turn on DC so the EUT can be initialized # DAS soft channels # TODO : add to library 1547 das_points = { 'sc': ('Q_TARGET', 'Q_TARGET_MIN', 'Q_TARGET_MAX', 'Q_MEAS', 'V_TARGET', 'V_MEAS', 'event') } # initialize data acquisition system daq = das.das_init(ts, sc_points=das_points['sc']) daq.sc['V_TARGET'] = v_nom daq.sc['Q_TARGET'] = 100 daq.sc['Q_TARGET_MIN'] = 100 daq.sc['Q_TARGET_MAX'] = 100 daq.sc['event'] = 'None' ts.log('DAS device: %s' % daq.info()) ''' b) Set all voltage trip parameters to the widest range of adjustability. Disable all reactive/active power control functions. ''' eut = der.der_init(ts) if eut is not None: eut.config() ts.log_debug(eut.measurements()) eut.volt_var(params={'Ena': False}) eut.volt_watt(params={'Ena': False}) eut.fixed_pf(params={'Ena': False}) ts.log_debug( 'Voltage trip parameters set to the widest range: v_min: {0} V, ' 'v_max: {1} V'.format(v_low, v_high)) try: eut.vrt_stay_connected_high( params={ 'Ena': True, 'ActCrv': 0, 'Tms1': 3000, 'V1': v_high, 'Tms2': 0.16, 'V2': v_high }) except Exception, e: ts.log_error( 'Could not set VRT Stay Connected High curve. %s' % e) try: eut.vrt_stay_connected_low( params={ 'Ena': True, 'ActCrv': 0, 'Tms1': 3000, 'V1': v_low, 'Tms2': 0.16, 'V2': v_low }) except Exception, e: ts.log_error('Could not set VRT Stay Connected Low curve. %s' % e)
def test_run(): result = script.RESULT_FAIL try: """ Configuration """ # Initiliaze VW EUT specified parameters variables mode = ts.param_value('vw.mode') irr = ts.param_value('vw.irr') """ Equipment Configuration """ # initialize pv simulator pv = pvsim.pvsim_init(ts) p_rated = ts.param_value('eut.p_rated') pv.power_set(p_rated) pv.power_on() # power on at p_rated """ Test Configuration """ # list of active tests vw_curves = collections.OrderedDict() t_settling = [0,0,0,0] if mode == 'Imbalanced grid': if ts.param_value('vw.imbalance_resp_1') == 'Enable': imbalance_resp.append('individual phase voltages') if ts.param_value('vw.imbalance_resp_2') == 'Enable': imbalance_resp.append('average of the three-phase effective (RMS)') if ts.param_value('vw.imbalance_resp_3') == 'Enable': imbalance_resp.append('the positive sequence of voltages') else: irr = ts.param_value('vw.irr') if ts.param_value('vw.test_1') == 'Enabled': vw_curves['characteristic 1'] = 1 t_settling[1]=ts.param_value('vw.test_1_t_r') if ts.param_value('vw.test_2') == 'Enabled': vw_curves['characteristic 2'] = 2 t_settling[2]=ts.param_value('vw.test_2_t_r') if ts.param_value('vw.test_3') == 'Enabled': vw_curves['characteristic 3'] = 3 t_settling[3]=ts.param_value('vw.test_3_t_r') #List of power level for tests if irr == '20%': pwr_lvls = [0.20] elif irr == '66%': pwr_lvls = [0.66] elif irr == '100%': pwr_lvls = [1.00] else: pwr_lvls = [1.00, 0.66, 0.20] # ts.log_debug('power_lvl_dictionary:%s' % (pwr_lvls)) # ts.log_debug('%s' % (vw_curves)) if mode == 'Imbalanced grid': result = volt_watt_mode_imbalanced_grid(imbalance_resp=imbalance_resp) else: result = volt_watt_mode(vw_curves=vw_curves, t_settling=t_settling, pwr_lvls=pwr_lvls) return result except script.ScriptFail, e: reason = str(e) if reason: ts.log_error(reason)