def init(self): system = self.system t0, _ = elapsed() self.models = system.find_models('pflow') self.converged = False self.inc = None self.A = None self.niter = None self.mis = [1] self.x_sol = None self.y_sol = None self.system.set_var_arrays(self.models, inplace=True, alloc=False) self.system.init(self.models, routine='pflow') # force compile if numba is on - improves timing accuracy if system.config.numba: system.f_update(self.models) system.g_update(self.models) system.j_update(models=self.models) _, s1 = elapsed(t0) logger.info('Power flow initialized in %s.', s1) return system.dae.xy
def run(self): ret = False system = self.system if system.PFlow.converged is False: logger.warning('Power flow not solved. Eig analysis will not continue.') return ret else: if system.TDS.initialized is False: system.TDS._initialize() system.TDS._implicit_step() if system.dae.n == 0: logger.warning('No dynamic model. Eig analysis will not continue.') return ret t1, s = elapsed() logger.info('-> Eigenvalue Analysis:') self.calc_state_matrix() self.calc_part_factor() if not self.system.files.no_output: self.write_report() if system.options.get('state_matrix') is True: self.export_state_matrix() if self.config.plot: self.plot() ret = True _, s = elapsed(t1) logger.info('Eigenvalue analysis finished in {:s}.'.format(s)) return ret
def save_output(self, npz=True): """ Save the simulation data into two files: a lst file and a npz file. Parameters ---------- npz : bool True to save in npz format; False to save in npy format. Returns ------- bool True if files are written. False otherwise. """ if self.system.files.no_output: return False else: t0, _ = elapsed() self.system.dae.write_lst(self.system.files.lst) if npz is True: self.system.dae.write_npz(self.system.files.npz) else: self.system.dae.write_npy(self.system.files.npy) _, s1 = elapsed(t0) logger.info(f'TDS outputs saved in {s1}.') return True
def _initialize(self): """ Initialize the status, storage and values for TDS. Returns ------- array-like The initial values of xy. """ t0, _ = elapsed() system = self.system self._reset() system.set_address(models=self.tds_models) system.set_dae_names(models=self.tds_models) system.dae.resize_array() system.dae.clear_ts() system.store_sparse_pattern(models=self.pflow_tds_models) system.store_adder_setter(models=self.pflow_tds_models) system.vars_to_models() system.initialize(self.tds_models) system.store_switch_times(self.tds_models) self.initialized = self.test_initialization() _, s1 = elapsed(t0) if self.initialized is True: logger.info(f"Initialization successful in {s1}.") else: logger.info(f"Initialization error in {s1}.") if system.dae.n == 0: logger.warning('No dynamic component loaded.') return system.dae.xy
def run(self, **kwargs): ret = False system = self.system if system.PFlow.converged is False: logger.warning( 'Power flow not solved. Eig analysis will not continue.') return ret else: if system.TDS.initialized is False: system.TDS._initialize() system.TDS._implicit_step() if system.dae.n == 0: logger.warning('No dynamic model. Eig analysis will not continue.') return ret t1, s = elapsed() logger.info('-> Eigenvalue Analysis:') self.calc_state_matrix() self.calc_part_factor() self.dump_results() if self.config.plot: self.plot() ret = True _, s = elapsed(t1) logger.info('Eigenvalue analysis finished in {:s}.'.format(s)) return ret
def save_output(self, npz=True): """ Save the simulation data into two files: a `.lst` file and a `.npz` file. This function saves the output regardless of the `files.no_output` flag. Parameters ---------- npz : bool True to save in npz format; False to save in npy format. Returns ------- bool True if files are written. False otherwise. """ t0, _ = elapsed() self.system.dae.write_lst(self.system.files.lst) if npz is True: np_file = self.system.files.npz self.system.dae.write_npz(self.system.files.npz) else: np_file = self.system.files.npy self.system.dae.write_npy(self.system.files.npy) _, s1 = elapsed(t0) logger.info('Outputs to "%s" and "%s".', self.system.files.lst, np_file) logger.info('Outputs written in %s.', s1) return True
def prepare(quick=False, **kwargs): t0, _ = elapsed() logger.info('Numeric code preparation started...') system = System() system.prepare(quick=quick) _, s = elapsed(t0) logger.info(f'Successfully generated numerical code in {s}.') return True
def run(self, **kwargs): """ Full Newton-Raphson method. Returns ------- bool convergence status """ system = self.system self.summary() self.init() if system.dae.m == 0: logger.error("Loaded case contains no power flow element.") system.exit_code = 1 return False t0, _ = elapsed() self.niter = 0 while True: mis = self.nr_step() logger.info(f'{self.niter}: |F(x)| = {mis:<10g}') if mis < self.config.tol: self.converged = True break elif self.niter > self.config.max_iter: break elif np.isnan(mis).any(): logger.error('NaN found in solution. Convergence not likely') self.niter = self.config.max_iter + 1 break elif mis > 1e4 * self.mis[0]: logger.error('Mismatch increased too fast. Convergence not likely.') break self.niter += 1 _, s1 = elapsed(t0) if not self.converged: if abs(self.mis[-1] - self.mis[-2]) < self.config.tol: max_idx = np.argmax(np.abs(system.dae.xy)) name = system.dae.xy_name[max_idx] logger.error('Mismatch is not correctable possibly due to large load-generation imbalance.') logger.error(f'Largest mismatch on equation associated with <{name}>') else: logger.error(f'Power flow failed after {self.niter + 1} iterations for {system.files.case}.') else: logger.info(f'Converged in {self.niter+1} iterations in {s1}.') if self.config.init_tds: system.TDS.init() if self.config.report: system.PFlow.report() system.exit_code = 0 if self.converged else 1 return self.converged
def init(self): """ Initialize the status, storage and values for TDS. Returns ------- array-like The initial values of xy. """ t0, _ = elapsed() system = self.system if self.initialized: return system.dae.xy self._reset() self._load_pert() # Note: # calling `set_address` on `system.exist.pflow_tds` will point all variables # to the new array after extending `dae.y` system.set_address(models=system.exist.pflow_tds) system.set_dae_names(models=system.exist.tds) system.dae.clear_ts() system.store_sparse_pattern(models=system.exist.pflow_tds) system.store_adder_setter(models=system.exist.pflow_tds) system.vars_to_models() # Initialize `system.exist.tds` only to avoid Bus overwriting power flow solutions system.init(system.exist.tds) system.store_switch_times(system.exist.tds) # Build mass matrix into `self.Teye` self.Teye = spdiag(system.dae.Tf.tolist()) self.qg = np.zeros(system.dae.n + system.dae.m) self.initialized = self.test_init() # if `dae.n == 1`, `calc_h_first` depends on new `dae.gy` self.calc_h() _, s1 = elapsed(t0) if self.initialized is True: logger.info(f"Initialization was successful in {s1}.") else: logger.error(f"Initialization failed in {s1}.") if system.dae.n == 0: tqdm.write('No dynamic component loaded.') return system.dae.xy
def run(self, verbose=False): """ Run the implicit numerical integration for TDS. Parameters ---------- verbose : bool verbosity flag for single integration steps """ system = self.system dae = self.system.dae config = self.config self.summary() self._initialize() self.pbar = tqdm(total=100, ncols=70, unit='%') t0, _ = elapsed() while (system.dae.t < self.config.tf) and (not self.busted): if self.calc_h() == 0: logger.error( "Time step calculated to zero. Simulation terminated.") break if self._implicit_step(): # store values dae.ts.store_txyz( dae.t, dae.xy, self.system.get_z(models=self.pflow_tds_models)) dae.t += self.h # show progress in percentage perc = max( min((dae.t - config.t0) / (config.tf - config.t0) * 100, 100), 0) if perc >= self.next_pc: self.pbar.update(1) self.next_pc += 1 # check if the next step is critical time if self.is_switch_time(): self._last_switch_t = system.switch_times[self._switch_idx] system.switch_action(self.pflow_tds_models) system.vars_to_models() self.pbar.close() _, s1 = elapsed(t0) logger.info(f'Simulation completed in {s1}.') system.TDS.save_output() # load data into ``TDS.plotter`` in the notebook mode if is_notebook(): self.load_plotter()
def run(self): """ Full Newton-Raphson method Returns ------- """ system = self.system logger.info('-> Power flow calculation with Newton Raphson method:') self._initialize() if system.dae.m == 0: logger.error("Loaded case file contains no element.") return False t0, _ = elapsed() self.niter = 0 while True: mis = self.nr_step() logger.info(f'{self.niter}: |F(x)| = {mis:<10g}') if mis < self.config.tol: self.converged = True break elif self.niter > self.config.max_iter: break elif mis > 1e4 * self.mis[0]: logger.error( 'Mismatch increased too fast. Convergence not likely.') break self.niter += 1 _, s1 = elapsed(t0) if not self.converged: if abs(self.mis[-1] - self.mis[-2]) < self.config.tol: max_idx = np.argmax(np.abs(system.dae.xy)) name = system.dae.xy_name[max_idx] logger.error( 'Mismatch is not correctable possibly due to large load-generation imbalance.' ) logger.error( f'Largest mismatch on equation associated with <{name}>') else: logger.error( f'Power flow failed after {self.niter + 1} iterations for {system.files.case}.' ) else: logger.info(f'Converged in {self.niter+1} iterations in {s1}.') if self.config.report: system.PFlow.write_report() return self.converged
def run(self, **kwargs): succeed = False system = self.system self.singular_idx = np.array([], dtype=int) if system.PFlow.converged is False: logger.warning( 'Power flow not solved. Eig analysis will not continue.') return succeed else: if system.TDS.initialized is False: system.TDS.init() system.TDS._itm_step() if system.dae.n == 0: logger.error('No dynamic model. Eig analysis will not continue.') else: if sum(system.dae.Tf != 0) != len(system.dae.Tf): self.singular_idx = np.argwhere(np.equal( system.dae.Tf, 0.0)).ravel().astype(int) logger.info( f"System contains {len(self.singular_idx)} zero time constants. " ) logger.debug([system.dae.x_name[i] for i in self.singular_idx]) self.x_name = np.array(system.dae.x_name) self.x_name = np.delete(self.x_name, self.singular_idx) self.summary() t1, s = elapsed() self.calc_state_matrix() self.remove_singular_rc() self.calc_part_factor() if not self.system.files.no_output: self.report() if system.options.get('state_matrix') is True: self.export_state_matrix() if self.config.plot: self.plot() _, s = elapsed(t1) logger.info('Eigenvalue analysis finished in {:s}.'.format(s)) succeed = True system.exit_code = 0 if succeed else 1 return succeed
def parse(system): """ Parse input file with the given format in `system.files.input_format`. Returns ------- bool True if successful; False otherwise. """ t, _ = elapsed() # exit when no input format is given if not system.files.input_format: if not guess(system): logger.error( 'Input format is not specified and cannot be inferred.') return False # try parsing the base case file logger.info(f'Parsing input file "{system.files.case}"') input_format = system.files.input_format parser = importlib.import_module('.' + input_format, __name__) if not parser.read(system, system.files.case): logger.error( f'Error parsing case file {system.files.fullname} with {input_format} format parser.' ) return False
def parse(system): """ Parse input file with the given format in `system.files.input_format`. Returns ------- bool True if successful; False otherwise. """ t, _ = elapsed() # exit when no input format is given if not system.files.input_format: if not guess(system): logger.error('Input format unknown for file "%s".', system.files.case) return False # try parsing the base case file logger.info('Parsing input file "%s"...', system.files.case) input_format = system.files.input_format parser = importlib.import_module('.' + input_format, __name__) if not parser.read(system, system.files.case): logger.error('Error parsing file "%s" with <%s> parser.', system.files.fullname, input_format) return False
def dump(system, output_format, full_path=None, overwrite=False, **kwargs): """ Dump the System data into the requested output format. Parameters ---------- system System object output_format : str Output format name. 'xlsx' will be used if is not an instance of `str`. Returns ------- bool True if successful; False otherwise. """ if system.files.no_output: logger.info('no_output is True. Case dump not processed.') return False if (output_format is None) or (output_format is True): output_format = 'xlsx' output_ext = get_output_ext(output_format) if output_ext == '': return False if full_path is not None: system.files.dump = full_path else: system.files.dump = os.path.join(system.files.output_path, system.files.name + '.' + output_ext) writer = importlib.import_module('.' + output_format, __name__) t, _ = elapsed() ret = writer.write(system, system.files.dump, overwrite=overwrite, **kwargs) _, s = elapsed(t) if ret: logger.info(f'Format conversion completed in {s}.') return True else: logger.error('Format conversion failed.') return False
def dump(system, output_format): if system.files.no_output: return if output_format is None: output_format = 'xlsx' outfile = system.files.dump writer = importlib.import_module('.' + output_format, __name__) t, _ = elapsed() ret = writer.write(system, outfile) _, s = elapsed(t) if ret: logger.info(f'Converted file {system.files.dump} written in {s}.') else: logger.error('Format conversion aborted.')
def save_output(self): """ Save the simulation data into two files: a lst file and a npy file. Returns ------- bool True if files are written. False otherwise. """ if self.system.files.no_output: return False else: t0, _ = elapsed() self.system.dae.write_lst(self.system.files.lst) self.system.dae.write_npy(self.system.files.npy) _, s1 = elapsed(t0) logger.info(f'TDS outputs saved in {s1}.') return True
def prepare(quick=False, incremental=False, cli=False, **kwargs): """ Run code generation. Returns ------- System object """ t0, _ = elapsed() logger.info('Numeric code generation started...') system = System() system.prepare(quick=quick, incremental=incremental) _, s = elapsed(t0) logger.info(f'Successfully generated numerical code in {s}.') if cli is True: return 0 else: return system
def run(self, **kwargs): """ Run small-signal stability analysis. """ succeed = False system = self.system if system.PFlow.converged is False: logger.warning( 'Power flow not solved. Eig analysis will not continue.') return succeed if system.TDS.initialized is False: system.TDS.init() system.TDS.itm_step() if system.dae.n == 0: logger.error('No dynamic model. Eig analysis will not continue.') else: self.summary() t1, s = elapsed() self.calc_As() self.calc_part_factor() _, s = elapsed(t1) logger.info('Eigenvalue analysis finished in {:s}.'.format(s)) if not self.system.files.no_output: self.report() if system.options.get('state_matrix') is True: self.export_state_matrix() if self.config.plot: self.plot() succeed = True if not succeed: system.exit_code += 1 return succeed
def init(self): """ Initialize the status, storage and values for TDS. Returns ------- array-like The initial values of xy. """ t0, _ = elapsed() system = self.system if self.initialized: return system.dae.xy self._reset() self._load_pert() system.set_address(models=system.exist.tds) system.set_dae_names(models=system.exist.tds) system.dae.clear_ts() system.store_sparse_pattern(models=system.exist.pflow_tds) system.store_adder_setter(models=system.exist.pflow_tds) system.vars_to_models() system.init(system.exist.tds) system.store_switch_times(system.exist.tds) self.eye = spdiag([1] * system.dae.n) self.Teye = spdiag(system.dae.Tf.tolist()) * self.eye self.qg = np.zeros(system.dae.n + system.dae.m) self.calc_h() self.initialized = self.test_init() _, s1 = elapsed(t0) if self.initialized is True: logger.info(f"Initialization was successful in {s1}.") else: logger.error(f"Initialization failed in {s1}.") if system.dae.n == 0: tqdm.write('No dynamic component loaded.') return system.dae.xy
def run(self, **kwargs): ret = False system = self.system if system.PFlow.converged is False: logger.warning( 'Power flow not solved. Eig analysis will not continue.') return ret else: if system.TDS.initialized is False: system.TDS.init() if system.dae.n == 0: logger.warning('No dynamic model. Eig analysis will not continue.') return ret if sum(system.dae.Tf != 0) != len(system.dae.Tf): logger.error( "System contains zero time constant. Eigenvalue analysis cannot continue." ) return ret self.summary() t1, s = elapsed() self.calc_state_matrix() self.calc_part_factor() if not self.system.files.no_output: self.report() if system.options.get('state_matrix') is True: self.export_state_matrix() if self.config.plot: self.plot() ret = True _, s = elapsed(t1) logger.info('Eigenvalue analysis finished in {:s}.'.format(s)) return ret
def run(filename, input_path='', ncpu=1, verbose=20, **kwargs): if is_interactive(): config_logger(file=False, stream_level=verbose) cases = _find_cases(filename, input_path) system = None t0, _ = elapsed() if len(cases) == 1: system = run_case(cases[0], **kwargs) else: _run_multiprocess(cases, ncpu, **kwargs) t0, s0 = elapsed(t0) if len(cases) == 1: logger.info(f'-> Single process finished in {s0}.') elif len(cases) >= 2: logger.info(f'-> Multiple processes finished in {s0}.') return system
def run(self, **kwargs): """ Run small-signal stability analysis. """ succeed = False system = self.system if not self._pre_check(): system.exit_code += 1 return False self.summary() t1, s = elapsed() self.calc_As() self.mu, self.pfactors, self.N, self.W = self.calc_pfactor() self._store_stats() _, s = elapsed(t1) logger.info(' Positive %6g', self.n_positive) logger.info(' Zeros %6g', self.n_zeros) logger.info(' Negative %6g', self.n_negative) logger.info('Eigenvalue analysis finished in {:s}.'.format(s)) if not self.system.files.no_output: self.report() if system.options.get('state_matrix'): self.export_mat() if self.config.plot: self.plot() succeed = True if not succeed: system.exit_code += 1 return succeed
def parse(system): """ Parse input file with the given format in system.files.input_format """ t, _ = elapsed() # exit when no input format is given if not system.files.input_format: if not guess(system): logger.error( 'Input format is not specified and cannot be inferred.') return False input_format = system.files.input_format add_format = system.files.add_format # exit if the format parser could not be imported try: parser = importlib.import_module('.' + input_format, __name__) dmparser = importlib.import_module('.' + 'dome', __name__) if add_format: addparser = importlib.import_module('.' + add_format, __name__) except ImportError: logger.error( f'Parser for {input_format} format not found. Program will exit.') return False # try parsing the base case file logger.info('Parsing input file <{:s}>'.format(system.files.case)) if not parser.read(system, system.files.case): logger.error( 'Error parsing case file {:s} with {:s} format parser.'.format( system.files.fullname, input_format)) return False
def run(self, no_summary=False, **kwargs): """ Run time-domain simulation using numerical integration. The default method is the Implicit Trapezoidal Method (ITM). """ system = self.system dae = self.system.dae config = self.config succeed = False resume = False if system.PFlow.converged is False: logger.warning( 'Power flow not solved. Simulation will not continue.') system.exit_code += 1 return succeed # load from csv is provided if self.from_csv is not None: self.data_csv = self._load_csv(self.from_csv) if no_summary is False and (system.dae.t == 0): self.summary() # only initializing at t=0 allows to continue when `run` is called again. if system.dae.t == 0: self.init() else: # resume simulation resume = True logger.debug("Resuming simulation from t=%.4fs.", system.dae.t) self._calc_h_first() logger.debug( "Initial step size for resumed simulation is h=%.4fs.", self.h) if system.options.get("init") is True: logger.debug("Initialization only is requested and done") return self.initialized if is_notebook(): self.pbar = tqdm_nb(total=100, unit='%', file=sys.stdout, disable=self.config.no_tqdm) else: self.pbar = tqdm(total=100, unit='%', ncols=80, ascii=True, file=sys.stdout, disable=self.config.no_tqdm) if resume: perc = round((dae.t - config.t0) / (config.tf - config.t0) * 100, 2) self.last_pc = perc self.pbar.update(perc) self.qrt_start = time.time() self.headroom = 0.0 # write variable list file at the beginning if not system.files.no_output: system.dae.write_lst(self.system.files.lst) t0, _ = elapsed() while (system.dae.t - self.h < self.config.tf) and (not self.busted): # call perturbation file if specified if self.callpert is not None: self.callpert(dae.t, system) step_status = False # call the stepping method of the integration method (or data replay) if self.data_csv is None: step_status = self.itm_step() # compute for the current step else: step_status = self._csv_step() # record number of iterations and success flag if system.config.save_stats: self.call_stats.append( (system.dae.t.tolist(), self.niter, step_status)) if step_status: if config.save_every != 0: if config.save_every == 1: dae.store() else: if dae.kcount % config.save_every == 0: dae.store() # offload if exceeds `max_store` if self.config.limit_store and len( dae.ts._ys) >= self.config.max_store: # write to file if enabled if not system.files.no_output: self.save_output() logger.info( "Offload data from memory to file for t=%.2f - %.2f sec", dae.ts.t[0], dae.ts.t[-1]) # clear storage in memory anyway dae.ts.reset() self.streaming_step() if self.check_criteria() is False: self.err_msg = 'Violated stability criteria. To turn off, set [TDS].criteria = 0.' self.busted = True # check if the next step is critical time self.do_switch() self.calc_h() dae.t += self.h dae.kcount += 1 # show progress in percentage perc = max( min((dae.t - config.t0) / (config.tf - config.t0) * 100, 100), 0) perc = round(perc, 2) perc_diff = perc - self.last_pc if perc_diff >= 1: self.pbar.update(perc_diff) self.last_pc = self.last_pc + perc_diff # quasi-real-time check and wait (except for the last step) if config.qrt and self.h > 0: rt_end = self.qrt_start + self.h * config.kqrt # if the ending time has passed t_overrun = time.time() - rt_end if t_overrun > 0: logger.debug( 'Simulation over-run for %4.4g msec at t=%4.4g s.', 1000 * t_overrun, dae.t) else: self.headroom += (rt_end - time.time()) while time.time() - rt_end < 0: time.sleep(1e-4) self.qrt_start = time.time() else: if self.calc_h() == 0: self.err_msg = "Time step reduced to zero. Convergence is not likely." self.busted = True break if self.busted: logger.error(self.err_msg) logger.error("Simulation terminated at t=%.4f s.", system.dae.t) system.exit_code += 1 elif system.dae.t == self.config.tf: succeed = True # success flag system.exit_code += 0 self.pbar.update(100 - self.last_pc) else: system.exit_code += 1 # removed `pbar` so that System object can be serialized self.pbar.close() self.pbar = None t1, s1 = elapsed(t0) self.exec_time = t1 - t0 logger.info('Simulation to t=%.2f sec completed in %s.', config.tf, s1) if config.qrt: logger.debug('QRT headroom time: %.4g s.', self.headroom) # in case of resumed simulations, # manually unpack data to update arrays in `dae.ts` # disable warning in case data has just been dumped system.dae.ts.unpack(warn_empty=False) if (not system.files.no_output) and (config.save_mode == 'auto'): t0, _ = elapsed() self.save_output() _, s1 = elapsed(t0) np_file = self.system.files.npz logger.info('Outputs to "%s" and "%s".', self.system.files.lst, np_file) logger.info('Outputs written in %s.', s1) # end data streaming if system.config.dime_enabled: system.streaming.finalize() # load data into `TDS.plotter` in a notebook or in an interactive mode if is_notebook() or is_interactive(): self.load_plotter() return succeed
def run(self, no_pbar=False, no_summary=False, **kwargs): """ Run time-domain simulation using numerical integration. The default method is the Implicit Trapezoidal Method (ITM). Parameters ---------- no_pbar : bool True to disable progress bar no_summary : bool, optional True to disable the display of summary """ system = self.system dae = self.system.dae config = self.config succeed = False resume = False if system.PFlow.converged is False: logger.warning('Power flow not solved. Simulation will not continue.') system.exit_code += 1 return succeed # load from csv is provided if self.from_csv is not None: self.data_csv = self._load_csv(self.from_csv) if no_summary is False and (system.dae.t == 0): self.summary() # only initializing at t=0 allows to continue when `run` is called again. if system.dae.t == 0: self.init() else: # resume simulation resume = True logger.debug("Resuming simulation from t=%.4fs.", system.dae.t) self._calc_h_first() logger.debug("Initial step size for resumed simulation is h=%.4fs.", self.h) self.pbar = tqdm(total=100, ncols=70, unit='%', file=sys.stdout, disable=no_pbar) if resume: perc = round((dae.t - config.t0) / (config.tf - config.t0) * 100, 0) self.next_pc = perc + 1 self.pbar.update(perc) self.qrt_start = time.time() self.headroom = 0.0 t0, _ = elapsed() while (system.dae.t - self.h < self.config.tf) and (not self.busted): if self.callpert is not None: self.callpert(dae.t, system) step_status = False # call the stepping method of the integration method (or data replay) if self.data_csv is None: step_status = self.itm_step() # compute for the current step else: step_status = self._csv_step() if step_status: dae.store() self.streaming_step() # check if the next step is critical time self.do_switch() self.calc_h() dae.t += self.h # show progress in percentage perc = max(min((dae.t - config.t0) / (config.tf - config.t0) * 100, 100), 0) if perc >= self.next_pc: self.pbar.update(1) self.next_pc += 1 # quasi-real-time check and wait (except for the last step) if config.qrt and self.h > 0: rt_end = self.qrt_start + self.h * config.kqrt # if the ending time has passed if time.time() - rt_end > 0: logger.debug('Simulation over-run at t=%4.4g s.', dae.t) else: self.headroom += (rt_end - time.time()) while time.time() - rt_end < 0: time.sleep(1e-4) self.qrt_start = time.time() else: if self.calc_h() == 0: self.err_msg = "Time step reduced to zero. Convergence is not likely." self.busted = True break self.pbar.close() delattr(self, 'pbar') # removed `pbar` so that System object can be serialized if self.busted: logger.error(self.err_msg) logger.error("Simulation terminated at t=%.4f s.", system.dae.t) system.exit_code += 1 elif system.dae.t == self.config.tf: succeed = True # success flag system.exit_code += 0 else: system.exit_code += 1 _, s1 = elapsed(t0) logger.info('Simulation completed in %s.', s1) if config.qrt: logger.debug('QRT headroom time: %.4g s.', self.headroom) # need to unpack data in case of resumed simulations. system.dae.ts.unpack() if not system.files.no_output: self.save_output() # end data streaming if system.config.dime_enabled: system.streaming.finalize() # load data into `TDS.plotter` in a notebook or in an interactive mode if is_notebook() or is_interactive(): self.load_plotter() return succeed
def init(self): """ Initialize the status, storage and values for TDS. Returns ------- array-like The initial values of xy. """ t0, _ = elapsed() system = self.system if self.initialized: return system.dae.xy self.reset() self._load_pert() # restore power flow solutions system.dae.x[:len(system.PFlow.x_sol)] = system.PFlow.x_sol system.dae.y[:len(system.PFlow.y_sol)] = system.PFlow.y_sol # Note: # calling `set_address` on `system.exist.pflow_tds` will point all variables # to the new array after extending `dae.y`. system.set_address(models=system.exist.pflow_tds) system.set_dae_names(models=system.exist.tds) system.dae.clear_ts() system.store_sparse_pattern(models=system.exist.pflow_tds) system.store_adder_setter(models=system.exist.pflow_tds) system.store_no_check_init(models=system.exist.pflow_tds) system.vars_to_models() system.init(system.exist.tds, routine='tds') # only store switch times when not replaying CSV data if self.data_csv is None: system.store_switch_times(system.exist.tds) # Build mass matrix into `self.Teye` self.Teye = spdiag(system.dae.Tf.tolist()) self.qg = np.zeros(system.dae.n + system.dae.m) self.initialized = True # test if residuals are close enough to zero if self.config.test_init: self.test_ok = self.test_init() # discard initialized values and use that from CSV if provided if self.data_csv is not None: system.dae.x[:] = self.data_csv[0, 1:system.dae.n + 1] system.dae.y[:] = self.data_csv[0, system.dae.n + 1:system.dae.n + system.dae.m + 1] system.vars_to_models() # connect to data streaming server if system.streaming.dimec is None: system.streaming.connect() if system.config.dime_enabled: # send out system data using DiME self.streaming_init() self.streaming_step() # if `dae.n == 1`, `calc_h_first` depends on new `dae.gy` self.calc_h() # allocate for internal variables self.x0 = np.zeros_like(system.dae.x) self.y0 = np.zeros_like(system.dae.y) self.f0 = np.zeros_like(system.dae.f) _, s1 = elapsed(t0) logger.info("Initialization for dynamics completed in %s.", s1) if self.test_ok is True: logger.info("Initialization was successful.") elif self.test_ok is False: logger.error("Initialization failed!!") else: logger.warning("Initialization results were not verified.") if system.dae.n == 0: tqdm.write('No differential equation detected.') return system.dae.xy
def run(self, **kwargs): """ Full Newton-Raphson method. Returns ------- bool convergence status """ system = self.system if self.config.check_conn == 1: self.system.connectivity() self.summary() self.init() if system.dae.m == 0: logger.error("Loaded case contains no power flow element.") system.exit_code = 1 return False t0, _ = elapsed() self.niter = 0 while True: mis = self.nr_step() logger.info('%d: |F(x)| = %.10g', self.niter, mis) if mis < self.config.tol: self.converged = True break if self.niter > self.config.max_iter: break if np.isnan(mis).any(): logger.error('NaN found in solution. Convergence not likely') self.niter = self.config.max_iter + 1 break if mis > 1e4 * self.mis[0]: logger.error('Mismatch increased too fast. Convergence not likely.') break self.niter += 1 _, s1 = elapsed(t0) if not self.converged: if abs(self.mis[-1] - self.mis[-2]) < self.config.tol: max_idx = np.argmax(np.abs(system.dae.xy)) name = system.dae.xy_name[max_idx] logger.error('Mismatch is not correctable possibly due to large load-generation imbalance.') logger.error('Largest mismatch on equation associated with <%s>', name) else: logger.error('Power flow failed after %d iterations for "%s".', self.niter + 1, system.files.case) else: logger.info('Converged in %d iterations in %s.', self.niter + 1, s1) # make a copy of power flow solutions self.x_sol = system.dae.x.copy() self.y_sol = system.dae.y.copy() if self.config.init_tds: system.TDS.init() if self.config.report: system.PFlow.report() system.exit_code = 0 if self.converged else 1 return self.converged
if not system.files.input_format: if not guess(system): logger.error('Input format unknown for file "%s".', system.files.case) return False # try parsing the base case file logger.info('Parsing input file "%s"...', system.files.case) input_format = system.files.input_format parser = importlib.import_module('.' + input_format, __name__) if not parser.read(system, system.files.case): logger.error('Error parsing file "%s" with <%s> parser.', system.files.fullname, input_format) return False _, s = elapsed(t) logger.info('Input file parsed in %s.', s) # Try parsing the addfile t, _ = elapsed() if system.files.addfile: logger.info('Parsing additional file "%s"...', system.files.addfile) add_format = system.files.add_format add_parser = importlib.import_module('.' + add_format, __name__) if not add_parser.read_add(system, system.files.addfile): logger.error('Error parsing addfile "%s" with %s parser.', system.files.addfile, input_format) return False _, s = elapsed(t) logger.info('Addfile parsed in %s.', s)
def run(self, no_pbar=False, no_summary=False, **kwargs): """ Run the implicit numerical integration for TDS. Parameters ---------- no_pbar : bool True to disable progress bar no_summary : bool, optional True to disable the display of summary """ system = self.system dae = self.system.dae config = self.config succeed = False resume = False if system.PFlow.converged is False: logger.warning('Power flow not solved. Simulation will not continue.') system.exit_code += 1 return succeed if no_summary is False: self.summary() # only initializing at t=0 allows to continue when `run` is called again. if system.dae.t == 0: self.init() else: # resume simulation resume = True self.pbar = tqdm(total=100, ncols=70, unit='%', file=sys.stdout, disable=no_pbar) if resume: perc = round((dae.t - config.t0) / (config.tf - config.t0) * 100, 0) self.next_pc = perc + 1 self.pbar.update(perc) t0, _ = elapsed() while (system.dae.t < self.config.tf) and (not self.busted): if self.callpert is not None: self.callpert(dae.t, system) if self._itm_step(): # simulate the current step # store values dae.ts.store_txyz(dae.t.tolist(), dae.xy, self.system.get_z(models=system.exist.pflow_tds), ) # check if the next step is critical time self.do_switch() self.calc_h() dae.t += self.h # show progress in percentage perc = max(min((dae.t - config.t0) / (config.tf - config.t0) * 100, 100), 0) if perc >= self.next_pc: self.pbar.update(1) self.next_pc += 1 else: if self.calc_h() == 0: self.err_msg = "Time step reduced to zero. Convergence is not likely." self.busted = True break self.pbar.close() delattr(self, 'pbar') # removed `pbar` so that System object can be dilled if self.busted: logger.error(self.err_msg) logger.error(f"Simulation terminated at t={system.dae.t:.4f}.") system.exit_code += 1 elif system.dae.t == self.config.tf: succeed = True # success flag system.exit_code += 0 else: system.exit_code += 1 _, s1 = elapsed(t0) logger.info(f'Simulation completed in {s1}.') system.TDS.save_output() # load data into `TDS.plotter` in a notebook or in an interactive mode if is_notebook() or is_interactive(): self.load_plotter() return succeed