def txyz(self): """ Return the values of [t, x, y, z] in an array. """ self.unpack() if len(self._z): return np.hstack((self.t.reshape((-1, 1)), self.xy, self.z)) else: return np.hstack((self.t.reshape((-1, 1)), self.xy))
def _run_odeint(self, tspan, x0=None, asolver=None, verbose=False, h=0.05, hmax=0, hmin=0): """ Run integration with ``scipy.odeint``. Warnings -------- Function is NOT working. The time-based switching is not handled correctly. """ self._initialize() if x0 is None: x0 = self.system.dae.x times = np.arange(tspan[0], tspan[1], h) # build critical time list tcrit = np.hstack([np.linspace(i, i+0.5, 100) for i in self.system.switch_times]) ret = odeint(self._solve_ivp_wrapper, x0, times, tfirst=True, args=(asolver, verbose), full_output=True, hmax=hmax, hmin=hmin, tcrit=tcrit ) # store the last step algebraic variables self.system.dae.store_yt_single() self.system.dae.store_xt_array(ret[0], times) return ret
def txyz(self): """ Return the values of [t, x, y, z] in an array. """ self.df = pd.DataFrame.from_dict(self._data, orient='index', columns=self.dae.xy_name) self.t = self.df.index.to_numpy() self.xy = self.df.to_numpy() if len(self._z): self.df_z = pd.DataFrame.from_dict(self._z, orient='index', columns=self.dae.z_name) self.z = self.df_z.to_numpy() return np.hstack((self.t.reshape((-1, 1)), self.xy, self.z)) else: return np.hstack((self.t.reshape((-1, 1)), self.xy))
def test_init(self): """ Test if the TDS initialization is successful. This function update ``dae.f`` and ``dae.g`` and checks if the residuals are zeros. """ system = self.system # fg_update is called in TDS.init() system.j_update(models=system.exist.pflow_tds) # reset diff. RHS where `check_init == False` system.dae.f[system.no_check_init] = 0.0 # warn if variables are initialized at limits if system.config.warn_limits: for model in system.exist.pflow_tds.values(): for item in model.discrete.values(): item.warn_init_limit() if np.max(np.abs(system.dae.fg)) < self.config.tol: logger.debug('Initialization tests passed.') return True # otherwise, show suspect initialization error fail_idx = np.ravel(np.where(abs(system.dae.fg) >= self.config.tol)) nan_idx = np.ravel(np.where(np.isnan(system.dae.fg))) bad_idx = np.hstack([fail_idx, nan_idx]) fail_names = [system.dae.xy_name[int(i)] for i in fail_idx] nan_names = [system.dae.xy_name[int(i)] for i in nan_idx] bad_names = fail_names + nan_names title = 'Suspect initialization issue! Simulation may crash!' err_data = { 'Name': bad_names, 'Var. Value': system.dae.xy[bad_idx], 'Eqn. Mismatch': system.dae.fg[bad_idx], } tab = Tab( title=title, header=err_data.keys(), data=list(map(list, zip(*err_data.values()))), ) logger.error(tab.draw()) if system.options.get('verbose') == 1: breakpoint() system.exit_code += 1 return False
def check_var(self, dae_t, *args, **kwargs): # Storage: # Output values is in the first col. # Latest values are stored in /appended to the last column self.rewind = False if dae_t == 0: self._v_mem[:] = self.u.v[:, None] elif dae_t < self.t[-1]: self.rewind = True self.t[-1] = dae_t self._v_mem[:, -1] = self.u.v elif dae_t == self.t[-1]: self._v_mem[:, -1] = self.u.v elif dae_t > self.t[-1]: if self.mode == 'step': self.t[:-1] = self.t[1:] self.t[-1] = dae_t self._v_mem[:, :-1] = self._v_mem[:, 1:] self._v_mem[:, -1] = self.u.v else: self.t = np.append(self.t, dae_t) self._v_mem = np.hstack((self._v_mem, self.u.v[:, None])) if dae_t - self.t[0] > self.delay: t_interp = dae_t - self.delay idx = np.argmax(self.t >= t_interp) - 1 v_interp = interp_n2(t_interp, self.t[idx:idx + 2], self._v_mem[:, idx:idx + 2]) self.t[idx] = t_interp self._v_mem[:, idx] = v_interp self.t = np.delete(self.t, np.arange(0, idx)) self._v_mem = np.delete(self._v_mem, np.arange(0, idx), axis=1) self.v[:] = self._v_mem[:, 0]
def _implicit_step(self): """ Integrate for a single given step. This function has an internal Newton-Raphson loop for algebraized semi-explicit DAE. The function returns the convergence status when done but does NOT progress simulation time. Returns ------- bool Convergence status in ``self.converged``. """ system = self.system dae = self.system.dae self.mis = [] self.niter = 0 self.converged = False self.x0 = np.array(dae.x) self.y0 = np.array(dae.y) self.f0 = np.array(dae.f) while True: system.e_clear(models=self.pflow_tds_models) system.l_update_var(models=self.pflow_tds_models) system.f_update(models=self.pflow_tds_models) system.g_update(models=self.pflow_tds_models) system.l_check_eq(models=self.pflow_tds_models) system.l_set_eq(models=self.pflow_tds_models) system.fg_to_dae() # lazy jacobian update if dae.t == 0 or self.niter > 3 or (dae.t - self._last_switch_t < 0.2): system.j_update(models=self.pflow_tds_models) self.solver.factorize = True # solve trapezoidal rule integration In = spdiag([1] * dae.n) self.Ac = sparse([[In - self.h * 0.5 * dae.fx, dae.gx], [-self.h * 0.5 * dae.fy, dae.gy]], 'd') # reset q as well q = dae.x - self.x0 - self.h * 0.5 * (dae.f + self.f0) for item in system.antiwindups: if len(item.x_set) > 0: for key, val in item.x_set: np.put(q, key[np.where(item.zi == 0)], 0) qg = np.hstack((q, dae.g)) inc = self.solver.solve(self.Ac, -matrix(qg)) # check for np.nan first if np.isnan(inc).any(): logger.error(f'NaN found in solution. Convergence not likely') self.niter = self.config.max_iter + 1 self.busted = True break # reset really small values to avoid anti-windup limiter flag jumps inc[np.where(np.abs(inc) < 1e-12)] = 0 # set new values dae.x += np.ravel(np.array(inc[:dae.n])) dae.y += np.ravel(np.array(inc[dae.n: dae.n + dae.m])) system.vars_to_models() # calculate correction mis = np.max(np.abs(inc)) self.mis.append(mis) self.niter += 1 # converged if mis <= self.config.tol: self.converged = True break # non-convergence cases if self.niter > self.config.max_iter: logger.debug(f'Max. iter. {self.config.max_iter} reached for t={dae.t:.6f}, ' f'h={self.h:.6f}, mis={mis:.4g} ' f'({system.dae.xy_name[np.argmax(inc)]})') break if mis > 1000 and (mis > 1e8 * self.mis[0]): logger.error(f'Error increased too quickly. Convergence not likely.') self.busted = True break if not self.converged: dae.x = np.array(self.x0) dae.y = np.array(self.y0) dae.f = np.array(self.f0) system.vars_to_models() return self.converged
def fg(self): """Return a concatenated array of [f, g].""" return np.hstack((self.f, self.g))
def xyz(self): """Return a concatenated array of [x, y].""" return np.hstack((self.x, self.y, self.z))