|
32 | 32 |
|
33 | 33 | __all__ = ['NonlinearIOSystem', 'InterconnectedSystem', 'nlsys', |
34 | 34 | 'input_output_response', 'find_eqpt', 'linearize', |
35 | | - 'interconnect', 'connection_table'] |
| 35 | + 'interconnect', 'connection_table', 'find_operating_point'] |
36 | 36 |
|
37 | 37 |
|
38 | 38 | class NonlinearIOSystem(InputOutputSystem): |
@@ -1663,6 +1663,60 @@ def ivp_rhs(t, x): |
1663 | 1663 | success=soln.success, message=message) |
1664 | 1664 |
|
1665 | 1665 |
|
| 1666 | +class OperatingPoint(object): |
| 1667 | + """A class for representing the operating point of a nonlinear I/O system. |
| 1668 | +
|
| 1669 | + The ``OperatingPoint`` class stores the operating point of a nonlinear |
| 1670 | + system, which consists of the state and input for a nonlinear system. |
| 1671 | + The main use for this class is as the return object for the |
| 1672 | + :func:`find_operating_point` function. |
| 1673 | +
|
| 1674 | + Attributes |
| 1675 | + ---------- |
| 1676 | + xop : array |
| 1677 | + State vector at the operating point. |
| 1678 | + uop : array |
| 1679 | + Input vector at the operating point. |
| 1680 | + result : :class:`scipy.optimize.OptimizeResult`, optional |
| 1681 | + Result from the :func:`scipy.optimize.root` function, if available. |
| 1682 | +
|
| 1683 | + """ |
| 1684 | + def __init__( |
| 1685 | + self, xop, uop=None, yop=None, result=None, |
| 1686 | + return_y=False, return_result=False): |
| 1687 | + self.xop = xop |
| 1688 | + self.uop = uop |
| 1689 | + |
| 1690 | + if yop is None and return_y and not return_result: |
| 1691 | + raise SystemError("return_y specified by no y0 value") |
| 1692 | + self.yop = yop |
| 1693 | + self.return_y = return_y |
| 1694 | + |
| 1695 | + if result is None and return_result: |
| 1696 | + raise SystemError("return_result specified by no result value") |
| 1697 | + self.result = result |
| 1698 | + self.return_result = return_result |
| 1699 | + |
| 1700 | + # Implement iter to allow assigning to a tuple |
| 1701 | + def __iter__(self): |
| 1702 | + if self.return_y and self.return_result: |
| 1703 | + return iter((self.xop, self.uop, self.yop, self.result)) |
| 1704 | + elif self.return_y: |
| 1705 | + return iter((self.xop, self.uop, self.yop)) |
| 1706 | + elif self.return_result: |
| 1707 | + return iter((self.xop, self.uop, self.result)) |
| 1708 | + else: |
| 1709 | + return iter((self.xop, self.uop)) |
| 1710 | + |
| 1711 | + # Implement (thin) getitem to allow access via legacy indexing |
| 1712 | + def __getitem__(self, index): |
| 1713 | + return list(self.__iter__())[index] |
| 1714 | + |
| 1715 | + # Implement (thin) len to emulate legacy return value |
| 1716 | + def __len__(self): |
| 1717 | + return len(list(self.__iter__())) |
| 1718 | + |
| 1719 | + |
1666 | 1720 | def find_operating_point( |
1667 | 1721 | sys, x0, u0=None, y0=None, t=0, params=None, |
1668 | 1722 | iu=None, iy=None, ix=None, idx=None, dx0=None, root_method=None, |
@@ -1946,6 +2000,15 @@ def rootfun(z): |
1946 | 2000 | z = (x, u, sys._out(t, x, u)) |
1947 | 2001 |
|
1948 | 2002 | # Return the result based on what the user wants and what we found |
| 2003 | + if return_result or result.success: |
| 2004 | + return OperatingPoint( |
| 2005 | + z[0], z[1], z[2], result, return_y, return_result) |
| 2006 | + else: |
| 2007 | + # Something went wrong, don't return anything |
| 2008 | + return OperatingPoint( |
| 2009 | + None, None, None, result, return_y, return_result) |
| 2010 | + |
| 2011 | + # TODO: remove code when ready |
1949 | 2012 | if not return_y: |
1950 | 2013 | z = z[0:2] # Strip y from result if not desired |
1951 | 2014 | if return_result: |
|
0 commit comments