sysloss.system
==============

.. py:module:: sysloss.system

.. autoapi-nested-parse::

   :py:class:`~system.System` is the primary class for power analysis.



Classes
-------

.. autoapisummary::

   sysloss.system.System


Module Contents
---------------

.. py:class:: System(name: str, source: sysloss.components.Source, *, group: str = '', rail: str = '')

   System to be analyzed.

   The first component of a system must always be a :py:class:`~components.Source`.

   :param name: System name.
   :type name: str
   :param source: Source component.
   :type source: Source
   :param group: Group name, for grouping components together.
   :type group: str, optional
   :param rail: Voltage rail name of output voltage.
   :type rail: str, optional

   :raises ValueError: If `source` is not a Source class.

   .. rubric:: Examples

   >>> sys = System("System name", Source("Vin", vo=12.0))


   .. py:method:: from_file(fname: str)
      :classmethod:


      Load system from .json file.

      The .json file must previously have been saved with :py:meth:`~system.System.save`.

      :param fname: File name.
      :type fname: str

      :returns: A new `System` instance.
      :rtype: System

      :raises ValueError: If file was created by a newer version of sysLoss.

      .. rubric:: Examples

      >>> sys = System.from_file("my_system.json")



   .. py:method:: add_comp(parent: str | list, *, comp, group: str = '', rail: str = '')

      Add component to system.

      :param parent: Name of parent component(s) or power rail(s) to connect to.
      :type parent: str | list
      :param comp: Component (from :py:mod:`~sysloss.components`).
      :type comp: component
      :param group: Group name, for grouping components together.
      :type group: str, optional
      :param rail: Voltage rail name of output voltage (not applicable on loads). Must be unique.
      :type rail: str, optional

      :raises ValueError: If parent does not allow connection to component, or name is already used.

      .. rubric:: Examples

      >>> sys.add_comp("Vin", comp=Converter("Buck", vo=1.8, eff=0.87), rail="IO_1V8")
      >>> sys.add_comp("Buck", comp=PLoad("MCU", pwr=0.015), group="Main")
      >>> sys.add_comp(["Vbatt", "USB_5V"], comp=PMux("Power mux", rs=[0.35, 0.4]))



   .. py:method:: add_source(source: sysloss.components.Source, *, group: str = '', rail: str = '')

      Add an additional Source to the system.

      :param source: Source component.
      :type source: Source
      :param group: Group name, for grouping components together.
      :type group: str, optional
      :param rail: Voltage rail name of output voltage. Must be unique.
      :type rail: str, optional

      :raises ValueError: If `source` is not a Source class or name is already used.

      .. rubric:: Examples

      >>> sys.add_source(Source("5V", vo=5.0, limits={"io": [0.0, 0.5]}))



   .. py:method:: change_comp(name: str, *, comp, group: str = '', rail: str = '')

      Replace component.

      The new component can be of same type (parameter change), or a new type
      given that the parent component accepts the connection.

      :param name: Name of component to be changed.
      :type name: str
      :param comp: Component (from :py:mod:`~sysloss.components`).
      :type comp: component
      :param group: Group name, for grouping components together.
      :type group: str, optional
      :param rail: Voltage rail name of output voltage (not applicable on loads). Must be unique.
      :type rail: str, optional

      :raises ValueError: If trying to change a `source` component to a different type, or
          if the target component does not exist or
          if the parent does not accept a connection to the new component or
          if the name is already used.

      .. rubric:: Examples

      >>> sys.change_comp("Buck", comp=LinReg("LDO", vo=1.8))



   .. py:method:: del_comp(name: str, *, del_childs: bool = True)

      Delete component.

      Deleting the last component (Source) in a system is not allowed.
      In a system with multiple sources, a source can only be deleted
      together with its child components.

      :param name: Name of component to delete.
      :type name: str
      :param del_childs: Delete all child components as well., by default True
      :type del_childs: bool, optional

      :raises ValueError: If the component does not exist, is the last source or
          a source with `del_childs` set to False.

      .. rubric:: Examples

      >>> sys.del_comp("Buck", del_childs=True)



   .. py:method:: tree(name='')

      Print the tree structure of the system.

      The tree object is generated with `Rich <https://rich.readthedocs.io>`_.

      :param name: Name of component to start with. If not given, print the entire system., by default ""
      :type name: str, optional

      :raises ValueError: If the component name is invalid.

      .. rubric:: Examples

      >>> sys.tree()
      >>> sys.tree("5V")



   .. py:method:: solve(*, vtol: float = 1e-06, itol: float = 1e-06, maxiter: int = 10000, quiet: bool = True, phase: str = '', energy: bool = False, ta: float = 25.0, tags: dict = {}) -> pandas.DataFrame

      Analyze steady-state of system.

      :param vtol: Voltage tolerance., by default 1e-6
      :type vtol: float, optional
      :param itol: Current tolerance., by default 1e-6
      :type itol: float, optional
      :param maxiter: Maximum number of iterations., by default 10000
      :type maxiter: int, optional
      :param quiet: Do not print # of iterations used., by default True
      :type quiet: bool, optional
      :param phase: Load phase to analyze (all if not specified), by default ""
      :type phase: str, optional
      :param energy: Show energy consumption per 24h., by default False
      :type energy: bool, optional
      :param ta: Ambient temperature, by default 25.0°C
      :type ta: float, optional
      :param tags: Tag-value pairs that will be added to the results table
      :type tags: dict, optional

      :returns: Analysis result.
      :rtype: pd.DataFrame

      :raises ValueError: If the specified phase is not defined. See :py:meth:`~system.System.set_phases`.
      :raises RuntimeError: If a steady-state solution has not been found after `maxiter` iterations.

      .. rubric:: Examples

      >>> sys.solve()
      >>> sys.solve(energy=True)
      >>> sys.solve(itol=1.0e-7, tags={"Column name": value})



   .. py:method:: rail_rep(*, vtol: float = 1e-06, itol: float = 1e-06, maxiter: int = 10000, quiet: bool = True, phase: str = '', energy: bool = False, ta: float = 25.0, tags: dict = {}) -> pandas.DataFrame

      Voltage rail report.

      The rail report first calls .solve(), then summarizes current, power and losses
      per voltage rail.

      :param \*: Parameters are identical to :py:meth:`~system.System.solve`.

      :returns: Analysis result. If no voltage rails have been defined, the result is identical
                to that returned by :py:meth:`~system.System.solve`.
      :rtype: pd.DataFrame

      :raises ValueError: If the specified phase is not defined. See :py:meth:`~system.System.set_phases`.
      :raises RuntimeError: If a steady-state solution has not been found after `maxiter` iterations.

      .. rubric:: Examples

      >>> sys.rail_rep()



   .. py:method:: params(limits: bool = False) -> pandas.DataFrame

      Return component parameters.

      :param limits: Include limits., by default False
      :type limits: bool, optional

      :returns: System component parameters.
      :rtype: pd.DataFrame

      .. rubric:: Examples

      >>> sys.params()
      >>> sys.params(limits=True)



   .. py:method:: limits() -> pandas.DataFrame

      Return component limits.

      A blank cell in the returned Pandas dataframe indicates that default limits apply.

      :returns: System component limits.
      :rtype: pd.DataFrame

      .. rubric:: Examples

      >>> sys.limits()



   .. py:method:: set_sys_phases(phases: dict)

      Define system level load phases.

      :param phases: A dict defining the system level load phases.
                     Each entry in the form '"name": duration(s)'.
      :type phases: dict

      :raises ValueError: If the dict contains less than two load phases or 'N/A' is used as
          a phase name.

      .. rubric:: Examples

      >>> sys.set_sys_phases({"sleep": 120.0, "transmit": 0.1, "move": 5.5})



   .. py:method:: get_sys_phases() -> dict

      Get the system level load phases.

      :returns: System load phases. Empty dict if no phases have been defined.
      :rtype: dict

      .. rubric:: Examples

      >>> sys.get_sys_phases()
      {"sleep": 120.0, "transmit": 0.1, "move": 5.5}



   .. py:method:: set_comp_phases(name: str, phase_conf: dict | list)

      Define component load phases.

      Components that have no load phases defined are always active.

      The :py:class:`~components.Source`, :py:class:`~components.Converter`, :py:class:`~components.Linreg`,
      :py:class:`~components.PSwitch` and :py:class:`~components.PMux`
      components support a list of active phases, and go
      into sleep mode if not active. In sleep mode, all components connected to the
      output are automatically turned off.

      The load components supports a dict with specific load values for each phase. If
      a phase is not included in the dict, the load is turned off in that phase.

      :param name: Component name.
      :type name: str
      :param phase_conf: Phase configuration.
      :type phase_conf: dict | list

      :raises ValueError: Component does not exist, or phase configuration is invalid.

      .. rubric:: Examples

      >>> sys.set_comp_phases("Buck", ["transmit", "move"])
      >>> sys.set_comp_phases("MCU", {"sleep":1e-6, "transmit":0.15, "move":0.085})



   .. py:method:: phases() -> pandas.DataFrame

      Return load phases and parameters for all system components.

      :returns: DataFrame with parameters and load phases.
      :rtype: pd.DataFrame

      .. rubric:: Examples

      >>> sys.phases()



   .. py:method:: save(fname: str, *, indent: int = 4)

      Save system as a .json file.

      :param fname: Filename.
      :type fname: str
      :param indent: Indentation to use in .json file, by default 4
      :type indent: int, optional

      .. rubric:: Examples

      >>> sys.save("System 42.json", indent=3)



   .. py:method:: plot_interp(name: str, *, cmap: matplotlib.colors.Colormap = 'viridis', inpdata: bool = True, plot3d: bool = False) -> matplotlib.figure.Figure | None

      Plot 1D or 2D interpolation data.

      If a component has a parameter defined as either 1D or 2D interpolation data,
      a figure is returned with linear interpolation shown. 1D data is plotted as an
      interpolated line. 2D data can be shown as 2D color map or 3D surface. The
      interpolated data is extended to +/-15% outside of input data points.

      :param name: Name of component.
      :type name: str
      :param cmap: Colormap to use for 3D-plot., by default "viridis"
      :type cmap: matplotlib.colors.Colormap, optional
      :param inpdata: Show input data (as red dots)., by default True
      :type inpdata: bool, optional
      :param plot3d: Plot 3D surface (for 2D data), by default False
      :type plot3d: bool, optional

      :returns: Interpolated parameter data figure. If the component does not have interpolation
                data, `None` is returned.
      :rtype: matplotlib.figure.Figure | None

      :raises ValueError: If component name is not found.

      .. rubric:: Examples

      >>> fig1 = sys.plot_interp("Buck", cmap="magma")
      >>> fig2 = sys.plot_interp("Buck", inpdata=False, plot3d=True)



   .. py:method:: batt_life(battery: str, *, cutoff: float, pfunc: Callable[[], tuple[float, float, float]], dfunc: Callable[[float, float], tuple[float, float, float]], tags: dict = {}) -> pandas.DataFrame

      Estimate battery life.

      Battery life estimation requires an external battery model. The battery model is
      accessed using two callback functions - one for battery probing and one for
      battery depletion. If system load phases have been defined, the estimation process
      loops through the phases until the battery is depleted or the cutoff voltage has been
      reached. In a system without load phases the process depletes the battery in ~1000
      time steps.

      :param battery: Name of battery (source) to be depleted
      :type battery: str
      :param cutoff: End simulation when battery voltage reaches cutoff or capacity is depleted,
                     whichever comes first.
      :type cutoff: float
      :param pfunc: Battery probe callback function, must return tuple with remaining capacity (Ah),
                    battery voltage (V) and battery impedance (Ohm)
      :type pfunc: Callable[[], tuple[float, float, float]]
      :param dfunc: Battery deplete callback function. Function arguments are time (s) and
                    current (A). Must return tuple with same format as pfunc.
      :type dfunc: Callable[[float, float], tuple[float, float, float]]
      :param tags: Tag-value pairs that will be added to the results table
      :type tags: dict, optional

      :returns: Battery depletion data.
      :rtype: pd.DataFrame

      :raises ValueError: If battery name is not found or component is not a source.

      .. rubric:: Examples

      >>> sys.batt_life("LiPo 3.7V", cutoff=2.9, pfunc=my_probe_func, dfunc=my_deplete_func)



