Esempio n. 1
0
    def unpack(self, df=False):
        """
        Unpack stored data in `_xy` and `_z` into arrays `t`, `xy`, and `z`.

        Parameters
        ----------
        df : bool
            True to construct DataFrames `self.df` and `self.df_z` (time-consuming).
        """
        if df is True:
            self.df = pd.DataFrame.from_dict(self._xy,
                                             orient='index',
                                             columns=self.dae.xy_name)
            self.t = self.df.index.to_numpy()
            self.xy = self.df.to_numpy()

            self.df_z = pd.DataFrame.from_dict(self._z,
                                               orient='index',
                                               columns=self.dae.z_name)
            self.z = self.df_z.to_numpy()
        else:
            n_steps = len(self._xy)
            self.t = np.array(list(self._xy.keys()))
            self.xy = np.zeros((n_steps, self.dae.m + self.dae.n))
            self.z = np.zeros((n_steps, self.dae.o))

            for idx, xy in enumerate(self._xy.values()):
                self.xy[idx, :] = xy

            for idx, z in enumerate(self._z.values()):
                self.z[idx, :] = z
Esempio n. 2
0
    def get(self,
            src: str,
            idx,
            attr: str = 'v',
            allow_none=False,
            default=0.0):
        """
        Based on the indexer, get the `attr` field of the `src` parameter or variable.

        Parameters
        ----------
        src : str
            param or var name
        idx : array-like
            device idx
        attr
            The attribute of the param or var to retrieve
        allow_none : bool
            True to allow None values in the indexer
        default : float
            If `allow_none` is true, the default value to use for None indexer.

        Returns
        -------
        The requested param or variable attribute
        """
        self._check_src(src)
        self._check_idx(idx)

        n = len(idx)
        if n == 0:
            return np.zeros(0)

        ret = [''] * n
        _type_set = False
        models = self.idx2model(idx, allow_none=allow_none)

        for i, idx in enumerate(idx):
            if models[i] is not None:
                uid = models[i].idx2uid(idx)
                instance = models[i].__dict__[src]
                val = instance.__dict__[attr][uid]
            else:
                val = default

            # deduce the type for ret
            if not _type_set:
                if isinstance(val, str):
                    ret = [''] * n
                else:
                    ret = np.zeros(n)
                _type_set = True

            ret[i] = val

        return ret
Esempio n. 3
0
 def list2array(self, n):
     """
     Allocate memory for storage arrays.
     """
     super().list2array(n)
     if self.mode == 'step':
         self._v_mem = np.zeros((n, self.delay + 1))
         self.t = np.zeros(self.delay + 1)
     else:
         self._v_mem = np.zeros((n, 1))
Esempio n. 4
0
    def link_external(self, ext_model):
        """
        Update variable addresses provided by external models

        This method sets attributes including `parent_model`,
        `parent_instance`, `uid`, `a`, `n`, `e_code` and
        `v_code`. It initializes the `e` and `v` to zero.

        Returns
        -------
        None

        Parameters
        ----------
        ext_model : Model
            Instance of the parent model
        """
        self.parent = ext_model

        if isinstance(ext_model, GroupBase):
            if self.indexer.n > 0 and isinstance(self.indexer.v[0],
                                                 (list, np.ndarray)):
                self._n = [len(i) for i in self.indexer.v
                           ]  # number of elements in each sublist
                self._idx = np.concatenate(
                    [np.array(i) for i in self.indexer.v])
            else:
                self._n = [len(self.indexer.v)]
                self._idx = self.indexer.v

            self.a = ext_model.get(src=self.src, idx=self._idx,
                                   attr='a').astype(int)
            self.n = len(self.a)
            self.v = np.zeros(self.n)
            self.e = np.zeros(self.n)

        else:
            original_var = ext_model.__dict__[self.src]

            if self.indexer is not None:
                uid = ext_model.idx2uid(self.indexer.v)
            else:
                uid = np.arange(ext_model.n, dtype=int)

            self._n = [len(uid)]
            if len(uid) > 0:
                self.a = original_var.a[uid]
            else:
                self.a = np.array([], dtype=int)

            # set initial v and e values to zero
            self.n = len(self.a)
            self.v = np.zeros(self.n)
            self.e = np.zeros(self.n)
Esempio n. 5
0
    def _set_arrays_alloc(self):
        """
        Allocate for internal v and e arrays that cannot
        share memory with dae arrays.
        """

        if not self.v_inplace:
            self.v = np.zeros(self.n)

        if not self.e_inplace:
            self.e = np.zeros(self.n)
Esempio n. 6
0
    def set_address(self, addr):
        """
        Set the address of this variables

        Parameters
        ----------
        addr : array-like
            The assigned address for this variable
        """
        self.a = addr
        self.n = len(self.a)
        self.v = np.zeros(self.n)
        self.e = np.zeros(self.n)
Esempio n. 7
0
    def store_sparse_pattern(self,
                             models: Optional[Union[str, List,
                                                    OrderedDict]] = None):
        models = self._get_models(models)
        self._call_models_method('store_sparse_pattern', models)

        # add variable jacobian values
        for j_name in self.dae.jac_name:
            ii, jj, vv = list(), list(), list()

            # for `gy` matrix, always make sure the diagonal is reserved
            # It is a safeguard if the modeling user omitted the diagonal
            # term in the equations
            if j_name == 'gy':
                ii.extend(np.arange(self.dae.m))
                jj.extend(np.arange(self.dae.m))
                vv.extend(np.zeros(self.dae.m))

            # logger.debug(f'Jac <{j_name}>, row={ii}')

            for mdl in models.values():
                row_idx = mdl.row_of(f'{j_name}')
                col_idx = mdl.col_of(f'{j_name}')

                # logger.debug(f'Model <{name}>, row={row_idx}')
                ii.extend(row_idx)
                jj.extend(col_idx)
                vv.extend(np.zeros(len(np.array(row_idx))))

                # add the constant jacobian values
                for row, col, val in mdl.zip_ijv(f'{j_name}c'):
                    ii.extend(row)
                    jj.extend(col)

                    if isinstance(val, (float, int)):
                        vv.extend(val * np.ones(len(row)))
                    elif isinstance(val, (list, np.ndarray)):
                        vv.extend(val)
                    else:
                        raise TypeError(
                            f'Unknown type {type(val)} in constant jacobian {j_name}'
                        )

            if len(ii) > 0:
                ii = np.array(ii).astype(int)
                jj = np.array(jj).astype(int)
                vv = np.array(vv).astype(float)

            self.dae.store_sparse_ijv(j_name, ii, jj, vv)
            self.dae.build_pattern(j_name)
Esempio n. 8
0
    def resize_array(self):
        """
        Resize arrays to the new `m` and `n`

        Returns
        -------

        """
        self.x = np.append(self.x, np.zeros(self.n - len(self.x)))
        self.y = np.append(self.y, np.zeros(self.m - len(self.y)))
        self.z = np.append(self.z, np.zeros(self.o - len(self.z)))

        self.f = np.append(self.f, np.zeros(self.n - len(self.f)))
        self.g = np.append(self.g, np.zeros(self.m - len(self.g)))
Esempio n. 9
0
    def __init__(self,
                 u,
                 mode='step',
                 delay=0,
                 name=None,
                 tex_name=None,
                 info=None):
        Discrete.__init__(self, name=name, tex_name=tex_name, info=info)

        if mode not in ('step', 'time'):
            raise ValueError(
                f'mode {mode} is invalid. Must be in "step" or "time"')

        self.u = u
        self.mode = mode
        self.delay = delay
        self.export_flags = ['v']
        self.export_flags_tex = ['v']

        self.t = np.array([0])
        self.v = np.array([0])
        self._v_mem = np.zeros((0, 1))
        self.rewind = False

        self.has_check_var = True
Esempio n. 10
0
    def setUp(self) -> None:
        self.n = 5  # number of input values to delay
        self.step = 2  # steps of delay
        self.time = 1.0  # delay period in second

        self.data = DummyValue(0)
        self.data.v = np.zeros(self.n)

        self.dstep = Delay(u=self.data, mode='step', delay=self.step)
        self.dstep.list2array(self.n)

        self.dtime = Delay(u=self.data, mode='time', delay=self.time)
        self.dtime.list2array(self.n)

        self.avg = Average(u=self.data, mode='step', delay=1)
        self.avg.list2array(self.n)

        self.v = self.dstep.v
        self.vt = self.dtime.v
        self.va = self.avg.v

        self.n_forward = 5
        self.tstep = 0.2
        self.dae_t = 0
        self.k = 0.2
Esempio n. 11
0
    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
Esempio n. 12
0
    def set_arrays(self, dae):
        """
        Set the equation and values arrays.

        It slicing into DAE (when contiguous) or allocating new memory (when not contiguous).

        Parameters
        ----------
        dae : DAE
            Reference to System.dae
        """
        slice_idx = slice(self.a[0], self.a[-1] + 1)
        if self.v_inplace:
            self.v = dae.__dict__[self.v_code][slice_idx]
        else:
            self.v = np.zeros(self.n)

        if self.e_inplace:
            self.e = dae.__dict__[self.e_code][slice_idx]
        else:
            self.e = np.zeros(self.n)
Esempio n. 13
0
    def get(self, src: str, idx, attr: str = 'v'):
        """
        Based on the indexer, get the `attr` field of the `src` parameter or variable.

        Parameters
        ----------
        src : str
            param or var name
        idx : array-like

        attr
            The attribute of the param or var to retrieve

        Returns
        -------
        The requested param or variable attribute
        """
        self._check_src(src)
        self._check_idx(idx)

        n = len(idx)
        if n == 0:
            return np.zeros(0)

        ret = None
        models = self.idx2model(idx)
        for i, idx in enumerate(idx):
            uid = models[i].idx2uid(idx)
            instance = models[i].__dict__[src]
            val = instance.__dict__[attr][uid]

            # deduce the type for ret
            if ret is None:
                if isinstance(val, str):
                    ret = [''] * n
                else:
                    ret = np.zeros(n)
            ret[i] = val

        return ret
Esempio n. 14
0
    def setUp(self) -> None:
        self.n = 5
        self.data = DummyValue(0)
        self.data.v = np.zeros(self.n)

        self.der = Derivative(u=self.data)
        self.der.list2array(self.n)
        self.v = self.der.v

        self.n_forward = 10
        self.k = 0.2
        self.t_step = 0.1
        self.dae_t = 0
Esempio n. 15
0
    def link_external(self, ext_model):
        """
        Method to be called by ``System`` for getting values from the external model or group.

        Parameters
        ----------
        ext_model
            An instance of a model or group provided by System
        """
        # set initial v values to zero
        self.v = np.zeros(self.n)
        if self.n == 0:
            return

        # the same `get` api for Group and Model
        self.v = ext_model.get(src=self.src, idx=self.indexer.v, attr='v')
Esempio n. 16
0
    def v(self):
        """
        Return the reduced values from the reduction function in an array

        Returns
        -------
        The array ``self._v`` storing the reduced values
        """
        if self._v is None:
            self._v = np.zeros(len(self.ref.v))
            idx = 0
            for i, v in enumerate(self.ref.v):
                self._v[i] = self.fun(self.u.v[idx:idx + len(v)])
                idx += len(v)
            return self._v
        else:
            return self._v
Esempio n. 17
0
    def v(self):
        """
        Return the values of the repeated values in a sequantial 1-D array

        Returns
        -------
        The array, ``self._v`` storing the repeated values
        """
        if self._v is None:
            self._v = np.zeros(len(list_flatten(self.ref.v)))
            idx = 0
            for i, v in enumerate(self.ref.v):
                self._v[idx:idx + len(v)] = self.u.v[i]
                idx += len(v)
            return self._v
        else:
            return self._v
Esempio n. 18
0
    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
Esempio n. 19
0
    def store_sparse_pattern(self, models: Optional[Union[str, List, OrderedDict]] = None):
        """
        Collect and store the sparsity pattern of Jacobian matrices.

        This is a runtime function specific to cases.

        Notes
        -----
        For `gy` matrix, always make sure the diagonal is reserved.
        It is a safeguard if the modeling user omitted the diagonal
        term in the equations.
        """
        models = self._get_models(models)
        self._call_models_method('store_sparse_pattern', models)

        # add variable jacobian values
        for jname in jac_names:
            ii, jj, vv = list(), list(), list()

            if jname == 'gy':
                ii.extend(np.arange(self.dae.m))
                jj.extend(np.arange(self.dae.m))
                vv.extend(np.zeros(self.dae.m))

            for mdl in models.values():
                for row, col, val in mdl.triplets.zip_ijv(jname):
                    ii.extend(row)
                    jj.extend(col)
                    vv.extend(np.zeros_like(row))
                for row, col, val in mdl.triplets.zip_ijv(jname + 'c'):
                    # process constant Jacobians separately
                    ii.extend(row)
                    jj.extend(col)
                    vv.extend(val * np.ones_like(row))

            if len(ii) > 0:
                ii = np.array(ii, dtype=int)
                jj = np.array(jj, dtype=int)
                vv = np.array(vv, dtype=float)

            self.dae.store_sparse_ijv(jname, ii, jj, vv)
            self.dae.build_pattern(jname)
Esempio n. 20
0
    def link_external(self, ext_model):
        """
        Update variable addresses provided by external models

        This method sets attributes including `parent_model`,
        `parent_instance`, `uid`, `a`, `n`, `e_code` and
        `v_code`. It initializes the `e` and `v` to zero.

        Returns
        -------
        None

        Parameters
        ----------
        ext_model : Model
            Instance of the parent model

        Warnings
        --------
        `link_external` does not check if the ExtVar type is the same
        as the original variable to reduce performance overhead.
        It will be a silent error (a dimension too small error from `dae.build_pattern`)
        if a model uses `ExtAlgeb` to access a `State`, or vice versa.

        """

        self.parent = ext_model

        if isinstance(ext_model, GroupBase):
            # determine the number of elements based on `indexer.v`
            if self.indexer.n > 0 and isinstance(self.indexer.v[0],
                                                 (list, np.ndarray)):
                self._n = [len(i) for i in self.indexer.v
                           ]  # number of elements in each sublist
                self._idx = np.concatenate(
                    [np.array(i) for i in self.indexer.v])
            else:
                self._n = [len(self.indexer.v)]
                self._idx = self.indexer.v

            # use `0` for non-existent addresses (corr. to None in indexer)
            self.a = ext_model.get(
                src=self.src,
                idx=self._idx,
                attr='a',
                allow_none=self.allow_none,
                default=0,
            ).astype(int)

            # check if source var type is the same as this ExtVar
            vcodes = np.array(
                ext_model.get_field(src=self.src,
                                    idx=self._idx,
                                    field='v_code'))
            vcodes = vcodes[vcodes != np.array(None)].astype(str)

            if not all(vcodes == np.array(self.v_code)):
                raise TypeError(
                    "ExtVar <%s.%s> is of type <%s>, but source Vars <%s.%s> may not."
                    % (self.owner.class_name, self.name, self.v_code,
                       ext_model.class_name, self.src))

            self.n = len(self.a)

        else:
            original_var = ext_model.__dict__[self.src]

            if self.allow_none:
                raise NotImplementedError(
                    f"{self.name}: allow_none not implemented for Model")
            if original_var.v_code != self.v_code:
                raise TypeError("Linking %s of %s to %s of %s is not allowed" %
                                (self.name, self.class_name, original_var.name,
                                 original_var.class_name))

            if self.indexer is not None:
                uid = ext_model.idx2uid(self.indexer.v)
            else:
                uid = np.arange(ext_model.n, dtype=int)

            self._n = [len(uid)]
            if len(uid) > 0:
                self.a = original_var.a[uid]
            else:
                self.a = np.array([], dtype=int)

            # set initial v and e values to zero
            self.n = len(self.a)

        self.v = np.zeros(self.n)
Esempio n. 21
0
 def clear_xy(self):
     """Reset variable arrays to empty
     """
     self.x = np.zeros(self.n)
     self.y = np.zeros(self.m)
Esempio n. 22
0
 def list2array(self, n):
     super().list2array(n)
     self._last_v = np.zeros(n)
Esempio n. 23
0
 def assign_memory(self, n):
     """Assign memory for ``self.v`` and set the array to zero."""
     self.v = np.zeros(n, dtype=self.vtype)
Esempio n. 24
0
 def clear_z(self):
     """
     Reset status arrays to empty
     """
     self.z = np.zeros(self.n)
Esempio n. 25
0
    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.vars_to_models()

        system.init(system.exist.tds, routine='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()

        # connect to dime server
        if system.config.dime_enabled:
            if system.streaming.dimec is None:
                system.streaming.connect()

        # 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()

        _, s1 = elapsed(t0)

        if self.initialized is True:
            logger.info(f"Initialization for dynamics was successful in {s1}.")
        else:
            logger.error(f"Initialization for dynamics failed in {s1}.")

        if system.dae.n == 0:
            tqdm.write('No dynamic component loaded.')
        return system.dae.xy
Esempio n. 26
0
    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
Esempio n. 27
0
 def clear_fg(self):
     """Resets equation arrays to empty
     """
     self.f = np.zeros(self.n)
     self.g = np.zeros(self.m)