Пример #1
0
    def __new__(cls, ID):
        self = super().__new__(cls)

        #: [Register] Contains all System objects as attributes.
        self.system = Registry()

        #: [Register] Contains all Unit objects as attributes.
        self.unit = Registry()

        #: [Register] Contains all Stream objects as attributes.
        self.stream = Registry()

        #: [str] ID of flowsheet.
        self._ID = ID
        self.flowsheet.__dict__[ID] = self
        return self
Пример #2
0
class Flowsheet:
    """
    Create a Flowsheet object which stores references to all stream, unit,
    and system objects. For a tutorial on flowsheets, visit
    :doc:`tutorial/Managing_flowsheets`.
	
    """
    
    line = "Flowsheet"
    
    #: [Flowsheets] All flowsheets.
    flowsheet = Flowsheets()
    
    def __new__(cls, ID):        
        self = super().__new__(cls)
        
        #: [Register] Contains all System objects as attributes.
        self.system = Registry()
        
        #: [Register] Contains all Unit objects as attributes.
        self.unit = Registry()
        
        #: [Register] Contains all Stream objects as attributes.
        self.stream = Registry()
        
        #: [str] ID of flowsheet.
        self._ID = ID
        self.flowsheet.__dict__[ID] = self
        return self
    
    def __reduce__(self):
        return self.from_registries, self.registries
    
    def __setattr__(self, key, value):
        if hasattr(self, '_ID'):
            raise TypeError(f"'{type(self).__name__}' object does not support attribute assignment")
        else:
            super().__setattr__(key, value)
    
    @property
    def ID(self):
        return self._ID
    
    @classmethod
    def from_registries(cls, stream, unit, system):
        flowsheet = super().__new__(cls)
        flowsheet.stream = stream
        flowsheet.unit = unit
        flowsheet.system = system
        return flowsheet
    
    @property
    def registries(self):
        return (self.stream, self.unit, self.system)
    
    def clear(self, reset_ticket_numbers=True):
        for registry in self.registries: registry.clear()
        self.flowsheet.clear()
        if reset_ticket_numbers:
            for i in (Stream, Unit, System): i.ticket_numbers.clear()
    
    def discard(self, ID):
        for registry in self.registries:
            if ID in registry: 
                registry.discard(ID)
                return
    
    def remove_unit_and_associated_streams(self, ID):
        stream_registry = self.stream
        unit = self.unit.pop(ID)
        for inlet in unit._ins:
            if inlet.source: continue
            stream_registry.discard(inlet)
        for outlet in unit._outs:
            if outlet._sink: continue
            stream_registry.discard(outlet)
    
    def update(self, flowsheet):
        for registry, other_registry in zip(self.registries, flowsheet.registries):
            registry.data.update(other_registry.data)
    
    def to_dict(self):
        return {**self.stream.data,
                **self.unit.data,
                **self.system.data}
    
    @classmethod
    def from_flowsheets(cls, ID, flowsheets):
        """Return a new flowsheet with all registered objects from the given flowsheets."""
        new = cls(ID)
        isa = isinstance
        for flowsheet in flowsheets:
            if isa(flowsheet, str):
                flowsheet = cls.flowsheet[flowsheet]
            new.update(flowsheet)
        return new
    
    def diagram(self, kind=None, file=None, format=None, display=True,
                number=None, profile=None, label=None, **graph_attrs):
        """
        Display all units and attached streams.
        
        Parameters
        ----------
        kind : int or string, optional
            * 0 or 'cluster': Display all units clustered by system.
            * 1 or 'thorough': Display every unit within the path.
            * 2 or 'surface': Display only elements listed in the path.
            * 3 or 'minimal': Display a single box representing all units.
        file : str, display in console by default
            File name to save diagram.
        format : str
            File format (e.g. "png", "svg"). Defaults to 'png'.
        display : bool, optional
            Whether to display diagram in console or to return the graphviz 
            object.
        number : bool, optional
            Whether to number unit operations according to their 
            order in the system path.
        profile : bool, optional
            Whether to clock the simulation time of unit operations.
        label : bool, optional
            Whether to label the ID of streams with sources and sinks.
            
        """
        return self.create_system(None).diagram(kind or 'thorough', file, format,
                                                display, number, profile, label,
                                                **graph_attrs)
    
    def create_system(self, ID="", feeds=None, ends=(), facility_recycle=None,
                      operating_hours=None, lang_factor=None):
        """
        Create a System object from all units and streams defined in the flowsheet.
        
        Parameters
        ----------
        ID : str, optional
            Name of system.
        feeds : Iterable[:class:`~thermosteam.Stream`], optional
            All feeds to the system. Specify this argument if only a section 
            of the complete system is wanted as it may disregard some units.
        ends : Iterable[:class:`~thermosteam.Stream`], optional
            End streams of the system which are not products. Specify this
            argument if only a section of the complete system is wanted, or if 
            recycle streams should be ignored.
        facility_recycle : :class:`~thermosteam.Stream`, optional
            Recycle stream between facilities and system path. This argument
            defaults to the outlet of a BlowdownMixer facility (if any).
        operating_hours : float, optional
            Number of operating hours in a year. This parameter is used to
            compute convinience properties such as utility cost and material cost
            on a per year basis. 
        lang_factor : float, optional
            Lang factor for getting fixed capital investment from 
            total purchase cost. If no lang factor, installed equipment costs are 
            estimated using bare module factors.
        
        """
        return System.from_units(ID, self.unit, feeds, ends, facility_recycle,
                                 operating_hours, lang_factor)
    
    def create_network(self, feeds=None, ends=()):
        """
        Create a Network object from all units and streams defined in the flowsheet.
        
        Parameters
        ----------
        feeds : Iterable[:class:`~thermosteam.Stream`]
            Feeds to the process.
        ends : Iterable[:class:`~thermosteam.Stream`]
            End streams of the system which are not products. Specify this argument
			if only a section of the system is wanted, or if recycle streams should be 
			ignored.
        
        """
        feeds = feeds_from_units(self.unit)
        if feeds:
            sort_feeds_big_to_small(feeds)
            feedstock, *feeds = feeds
            network = Network.from_feedstock(feedstock, feeds, ends)
        else:
            network = Network([])
        return network
    
    def __call__(self, ID):
        """
		Return requested biosteam item or all items with given Unit subclass.
    
        Parameters
        ----------
        ID : str or type
            ID of the requested item or Unit subclass.
    
        """
        isa = isinstance
        if isa(ID, str):
            ID = ID.replace(' ', '_')
            obj = (self.stream.search(ID)
                   or self.unit.search(ID)
                   or self.system.search(ID))
            if not obj: raise LookupError(f"no registered item '{ID}'")
            return obj
        elif issubclass(ID, bst.Unit):
            cls = ID
            return [i for i in self.unit if isa(i, cls)]
        else:
            raise ValueError('ID must be either a string or a Unit subclass')
    
    def __str__(self):
        return self.ID
    
    def __repr__(self):
        return f'<{type(self).__name__}: {self.ID}>'
Пример #3
0
class Flowsheet:
    """
    Create a Flowsheet object which stores references to all stream, unit,
    and system objects. For a tutorial on flowsheets, visit
    :doc:`tutorial/Managing_flowsheets`.
	"""
    line = "Flowsheet"

    #: [Register] All flowsheets.
    flowsheet = Flowsheets()

    def __new__(cls, ID):
        self = super().__new__(cls)

        #: [Register] Contains all System objects as attributes.
        self.system = Registry()

        #: [Register] Contains all Unit objects as attributes.
        self.unit = Registry()

        #: [Register] Contains all Stream objects as attributes.
        self.stream = Registry()

        #: [str] ID of flowsheet.
        self._ID = ID
        self.flowsheet.__dict__[ID] = self
        return self

    def __reduce__(self):
        return self.from_registries, self.registries

    def __setattr__(self, key, value):
        if hasattr(self, '_ID'):
            raise TypeError(
                f"'{type(self).__name__}' object does not support attribute assignment"
            )
        else:
            super().__setattr__(key, value)

    def view(self):
        """
        Create an interactive process flowsheet diagram
        that autorefreshes itself.
        """
        widget = FlowsheetWidget(self)
        widget.show()
        return widget

    @property
    def ID(self):
        return self._ID

    @classmethod
    def from_registries(cls, stream, unit, system):
        flowsheet = super().__new__(cls)
        flowsheet.stream = stream
        flowsheet.unit = unit
        flowsheet.system = system
        return flowsheet

    @property
    def registries(self):
        return (self.stream, self.unit, self.system)

    def discard(self, ID):
        for registry in self.registries:
            if ID in registry:
                registry.discard(ID)
                break

    def update(self, flowsheet):
        for registry, other_registry in zip(self.registries,
                                            flowsheet.registries):
            registry.__dict__.update(other_registry.__dict__)

    @classmethod
    def from_flowsheets(cls, ID, flowsheets):
        """Return a new flowsheet with all registered objects from the given flowsheets."""
        new = cls(ID)
        isa = isinstance
        for flowsheet in flowsheets:
            if isa(flowsheet, str):
                flowsheet = cls.flowsheet[flowsheet]
            new.update(flowsheet)
        return new

    def diagram(self, kind='surface', file=None, format='svg', **graph_attrs):
        """Display all units and attached streams.
        
        Parameters
        ----------
        kind='surface' : Must be one of the following
            * **'thorough':** Thoroughly display every unit.
            * **'surface':** Display units and recycle systems.
            * **'minimal':** Minimally display flowsheet as a box with feeds and products.
        
        """
        if kind == 'thorough':
            f = digraph_from_units_and_streams(self.unit,
                                               self.stream,
                                               format=format,
                                               **graph_attrs)
        elif kind == 'surface':
            f = self._surface_digraph(format, **graph_attrs)
        elif kind == 'minimal':
            f = minimal_digraph(self.ID, self.units, self.streams,
                                **graph_attrs)
        else:
            raise ValueError(
                f"kind must be either 'thorough', 'surface', or 'minimal'.")
        finalize_digraph(f, file, format)

    def _surface_digraph(self, format, **graph_attrs):
        surface_units = set(self.unit)
        old_unit_connections = set()
        for i in self.system:
            if i.recycle and not any(sub.recycle for sub in i.subsystems):
                surface_units.difference_update(i.units)
                update_surface_units(i.ID, i.streams, i.units, surface_units,
                                     old_unit_connections)

        f = digraph_from_units(surface_units)
        for u, ins, outs in old_unit_connections:
            u._ins[:] = ins
            u._outs[:] = outs
        return f

    def create_system(self, ID="", feeds=None, ends=(), facility_recycle=None):
        """
        Create a System object from all units and streams defined in the flowsheet.
        
        Parameters
        ----------
        ID : str, optional
            Name of system.
        feeds : Iterable[:class:`thermosteam.Stream`], optional
            Feeds to the process ordered in decreasing priority. The 
            first feed should be your feedstock.
        ends : Iterable[:class:`~thermosteam.Stream`], optional
            End streams of the system which are not products. Specify this argument
			if only a section of the system is wanted, or if recycle streams should be 
			ignored.
        facility_recycle : [:class:`~thermosteam.Stream`], optional
            Recycle stream between facilities and system path. This argument
            defaults to the outlet of a BlowdownMixer facility (if any).
        
        """
        if not feeds:
            feeds = get_feeds_from_streams(self.stream)
            sort_feeds_big_to_small(feeds)
        if feeds:
            feedstock, *feeds = feeds
            facilities = self.get_facilities()
            if not facility_recycle:
                facility_recycle = find_blowdown_recycle(facilities)
            system = System.from_feedstock(ID, feedstock, feeds, facilities,
                                           ends, facility_recycle)
        else:
            system = System(ID, ())
        return system

    def create_network(self, feeds=None, ends=()):
        """
        Create a Network object from all units and streams defined in the flowsheet.
        
        Parameters
        ----------
        ends : Iterable[:class:`~thermosteam.Stream`]
            End streams of the system which are not products. Specify this argument
			if only a section of the system is wanted, or if recycle streams should be 
			ignored.
        
        """
        feeds = get_feeds_from_streams(self.stream)
        if feeds:
            sort_feeds_big_to_small(feeds)
            feedstock, *feeds = feeds
            network = Network.from_feedstock(feedstock, feeds, ends)
        else:
            network = Network([])
        return network

    def create_path(self, feeds=None, ends=()):
        isa = isinstance
        network = self.create_network(feeds, ends)
        net2sys = System.from_network
        return tuple([(net2sys('', i) if isa(i, Network) else i)
                      for i in network.path])

    def get_facilities(self):
        isa = isinstance
        return [i for i in self.unit if isa(i, Facility)]

    def __call__(self, ID):
        """
		Return requested biosteam item.
    
        Parameters
        ----------
        ID : str
              ID of the requested item.
    
        """
        ID = ID.replace(' ', '_')
        obj = (self.stream.search(ID) or self.unit.search(ID)
               or self.system.search(ID))
        if not obj: raise LookupError(f"no registered item '{ID}'")
        return obj

    def __str__(self):
        return self.ID

    def __repr__(self):
        return f'<{type(self).__name__}: {self.ID}>'
Пример #4
0
class Flowsheet:
    """
    Create a Flowsheet object which stores references to all stream, unit,
    and system objects. For a tutorial on flowsheets, visit
    :doc:`tutorial/Managing_flowsheets`.
	
    """

    line = "Flowsheet"

    #: [Register] All flowsheets.
    flowsheet = Flowsheets()

    def __new__(cls, ID):
        self = super().__new__(cls)

        #: [Register] Contains all System objects as attributes.
        self.system = Registry()

        #: [Register] Contains all Unit objects as attributes.
        self.unit = Registry()

        #: [Register] Contains all Stream objects as attributes.
        self.stream = Registry()

        #: [str] ID of flowsheet.
        self._ID = ID
        self.flowsheet.__dict__[ID] = self
        return self

    def __reduce__(self):
        return self.from_registries, self.registries

    def __setattr__(self, key, value):
        if hasattr(self, '_ID'):
            raise TypeError(
                f"'{type(self).__name__}' object does not support attribute assignment"
            )
        else:
            super().__setattr__(key, value)

    @property
    def ID(self):
        return self._ID

    @classmethod
    def from_registries(cls, stream, unit, system):
        flowsheet = super().__new__(cls)
        flowsheet.stream = stream
        flowsheet.unit = unit
        flowsheet.system = system
        return flowsheet

    @property
    def registries(self):
        return (self.stream, self.unit, self.system)

    def clear(self):
        for registry in self.registries:
            registry.clear()

    def discard(self, ID):
        for registry in self.registries:
            if ID in registry:
                registry.discard(ID)
                break

    def update(self, flowsheet):
        for registry, other_registry in zip(self.registries,
                                            flowsheet.registries):
            registry.data.update(other_registry.data)

    def to_dict(self):
        return {**self.stream.data, **self.unit.data, **self.system.data}

    @classmethod
    def from_flowsheets(cls, ID, flowsheets):
        """Return a new flowsheet with all registered objects from the given flowsheets."""
        new = cls(ID)
        isa = isinstance
        for flowsheet in flowsheets:
            if isa(flowsheet, str):
                flowsheet = cls.flowsheet[flowsheet]
            new.update(flowsheet)
        return new

    def diagram(self,
                kind=None,
                file=None,
                format=None,
                display=True,
                **graph_attrs):
        """
        Display all units and attached streams.
        
        Parameters
        ----------
        kind='surface' : 'cluster', 'thorough', 'surface', or 'minimal'
            * **'cluster':** Display all units clustered by system.
            * **'thorough':** Display every unit within the path.
            * **'surface':** Display only elements listed in the path.
            * **'minimal':** Display path as a box.
        file : str, display in console by default
            File name to save diagram.
        format : str
            File format (e.g. "png", "svg"). Defaults to 'png'.
        display : bool, optional
            Whether to display diagram in console or to return the graphviz 
            object.
            
        """
        return self.create_system(None).diagram(kind or 'thorough', file,
                                                format, display)

    def create_system(self, ID="", feeds=None, ends=(), facility_recycle=None):
        """
        Create a System object from all units and streams defined in the flowsheet.
        
        Parameters
        ----------
        ID : str, optional
            Name of system.
        feeds : Iterable[:class:`~thermosteam.Stream`], optional
            All feeds to the system. Specify this argument if only a section 
            of the complete system is wanted as it may disregard some units.
        ends : Iterable[:class:`~thermosteam.Stream`], optional
            End streams of the system which are not products. Specify this
            argument if only a section of the complete system is wanted, or if 
            recycle streams should be ignored.
        facility_recycle : :class:`~thermosteam.Stream`, optional
            Recycle stream between facilities and system path. This argument
            defaults to the outlet of a BlowdownMixer facility (if any).
        
        """
        return System.from_units(ID, self.unit, feeds, ends, facility_recycle)

    def create_network(self, feeds=None, ends=()):
        """
        Create a Network object from all units and streams defined in the flowsheet.
        
        Parameters
        ----------
        feeds : Iterable[:class:`~thermosteam.Stream`]
            Feeds to the process.
        ends : Iterable[:class:`~thermosteam.Stream`]
            End streams of the system which are not products. Specify this argument
			if only a section of the system is wanted, or if recycle streams should be 
			ignored.
        
        """
        feeds = feeds_from_units(self.unit)
        if feeds:
            sort_feeds_big_to_small(feeds)
            feedstock, *feeds = feeds
            network = Network.from_feedstock(feedstock, feeds, ends)
        else:
            network = Network([])
        return network

    def __call__(self, ID):
        """
		Return requested biosteam item.
    
        Parameters
        ----------
        ID : str
              ID of the requested item.
    
        """
        ID = ID.replace(' ', '_')
        obj = (self.stream.search(ID) or self.unit.search(ID)
               or self.system.search(ID))
        if not obj: raise LookupError(f"no registered item '{ID}'")
        return obj

    def __str__(self):
        return self.ID

    def __repr__(self):
        return f'<{type(self).__name__}: {self.ID}>'