Source code for seapy.base

"""
Base
====

The Base module contains some of the foundations of the SEA framework, like the :class:`Base` class.

Base
----

.. autoclass:: seapy.base.Base


LinkedList
----------

.. autoclass:: seapy.base.LinkedList

Descriptors
-----------

.. autoclass:: seapy.base.Spectrum
.. autoclass:: seapy.base.Name
.. autoclass:: seapy.base.Link
.. autoclass:: seapy.base.MaterialLink
.. autoclass:: seapy.base.ComponentLink
.. autoclass:: seapy.base.JunctionLink
.. autoclass:: seapy.base.SubsystemFromLink
.. autoclass:: seapy.base.SubsystemToLink
.. autoclass:: seapy.base.SubsystemLink


.. autoclass:: seapy.base.NameWarning

.. autoclass:: seapy.base.MetaBase

"""

import abc
import math
import cmath
import numpy as np
import logging

import warnings
from weakref import WeakSet

from .tools import plot

from tabulate import tabulate
import pandas as pd


#class Link(object):
    #"""
    #One-to-Many link from `local` to `remotes`.
    
    #Descriptor.
    #"""
    
    #one = ''
    #"""
    #Name of the attribute this instance is assigned to.
    #"""
        
    #many = ''
    #"""
    #Name of the attribute where this link refers to.
    #"""
        
    #def __get__(self, instance, owner):
        #try:
            #return instance.system.getObject( instance.__dict__[self.one] )
        #except KeyError:
            #return None
        
    #def __set__(self, instance, value):
        
        #"""Log that we are changing the link."""
        #logging.info("Setting {} of {} to {}".format(self.one, instance.name, value))
        
        #"""Get the name of the new object that we link to."""
        #name = value if isinstance(value, str) else value.name
        #del value
        
        #"""Get the object that we link to."""
        #obj = instance.system.getObject(name)

            
        #try:
            #if getattr(instance, self.one) is not None:
                #logging.info("Changing %s of %s from %s to %s. Removing old reference.", one, instance.name, getattr(instance, self.one).name, name)
                #for item in getattr(getattr(instance, one), self.many):
                    #if item.name == instance.name:
                        #getattr(getattr(instance, one), self.many).remove(item)
        #except AttributeError:  # When self.one has not yet been set.
            #pass
        
        #"""We save the  name of the object that is assigned, not the object itself."""
        #instance.__dict__[self.one] = name

        #"""Add name of object in LinkedList of object."""
        #try:
            #l = obj.__dict__[self.many]
        #except KeyError: # In case the list doesn't exist yet...
            #l = obj.__dict__[self.many] = list()
        #l.append(instance.name)
            
    #def __delete__(self, instance):
        #setattr(instance, self.one, None)








class SubsystemExcitationLink(Link):
    """
    Link from excitation to subsystem.
    """
    attribute = "subsystem"
    reference = "linked_excitations"


#class Linked(object):
    
    #attribute = ''
    
    #def __get__(self, instance, owner):
        
        #pass


[docs]class LinkedList(object): """ Receiving Many part of One-to-Many link. """ attribute = '' """ Name of attribute of owner of this object. """ def __get__(self, instance, owner): #try: items = instance.__dict__[self.attribute] #except KeyError: #instance.__dict__[self.attribute] = WeakSet() #items = instance.__dict__[self.attribute] yield from items #print (l) ##yield from (instance.system.getObject(item) for item in l if item in (obj.name for obj in instance.system.objects)) ##print( l) #for item in l: ##print (instance.system) #try: #obj = instance.system.getObject(item) #except ReferenceError: #obj = instance.system.getObject(next(l)) #yield obj ##yield from (instance.system.getObject(item) for item in l) #items = [instance.system.getObject(item) for item in l] #yield from items #def __set__(self, instance, value): #if instance.__dict__[attribute] is None: #instance[attribute] = value #else: #raise ValueError("Does not allows to be set again.") def __set__(self, instance, value): raise ValueError("Cannot set this attribute.")
[docs]class NameWarning(Warning): """ Duplicate name warning. """ pass
[docs]class Name(object): """ Unique Name descriptor. This data descriptor checks with :class:`seapy.system.System` whether a name is not yet taken. In case the name is taken an integer is added to it. """ #def __init__(self): #pass def __get__(self, instance, owner): try: return instance.__dict__['name'] except (KeyError, AttributeError) as e: return None def __set__(self, instance, value): if instance.name is None: """Set unique name.""" names = (obj.name for obj in instance.system.objects) if value in names: msg = 'Name {} is not unique.'.format(str(value)) warnings.warn(msg, NameWarning) value += '1' instance.__dict__['name'] = value else: raise ValueError("Cannot change name.")
[docs]class MetaBase(type): """Metaclass that prepares :class:`Base`. This metaclass * sets :attr:`LinkedList.attribute` * sets :attr:`Spectrum.attribute` """ def __new__(cls, name, bases, attrs): #print(name) #print(bases) #print(attrs) for key, value in attrs.items(): if isinstance(value, LinkedList): value.attribute = key # Inform LinkedList of attribute name. if isinstance(value, Spectrum): value.attribute = key # Inform Spectrum of attribute name. return super(MetaBase, cls).__new__(cls, name, bases, attrs) #def __init__(cls, name, bases, attrs): ## find all descriptors, auto-set their labels ##print( cls) ##for key, value in attrs.items(): ##print(key, value) ##if isinstance(value, LinkedList): ##pass ###key = list() ##print (key, value) ##attrs[key] = WeakSet() ##cls.__dict__[key] = WeakSet() ###setattr(cls, key, WeakSet()) ##print(getattr(cls, key)) ##print(key) ##value.label = key #super(MetaBase, cls).__init__(name, bases, attrs)
[docs]class Spectrum(object): """Class capable of containing spectral values. Descriptor. """
[docs] def __init__(self, dtype='float64'): self.dtype = dtype
def __get__(self, instance, cls): if instance is None: return self else: value = instance.__dict__[self.attribute] try: length = instance.system.frequency.amount if length != len(value): if len(value) == 1: value = np.ones(length) * value else: raise AttributeError except AttributeError: pass return value #return instance.__dict__[self.attribute] def __set__(self, instance, value): if not isinstance(value, np.ndarray): # Check whether it is an array. raise TypeError('Expected an ndarray') if not self.dtype == value.dtype: # Check whether the array is of the right type. value = value.astype(self.dtype) try: length = instance.system.frequency.amount if not len(value) == length: # Check the size of the array. raise TypeError('Expected array of length %i', length) except AttributeError: # spectrum size is not yet available. Store despite possible length issue. pass instance.__dict__[self.attribute] = value def __delete__(self, instance): del instance.__dict__[self.attribute]
[docs]class Base(object, metaclass=MetaBase):#, metaclass=abc.ABCMeta): """ Abstract Base Class for all components, junctions, materials, subsystems, couplings and excitation. """
[docs] def __init__(self, name, system, **properties): """Constructor. :param name: Identifier of object :type name: string :param system: system objects belongs to :type system: :class:`seapy.system.System` :param properties: Optional properties :type properties: dict """ #print (self.__class__) #for key, value in self.__class__.__dict__.items(): #print(key, value) #for key, value in self.__dict__.items(): #print( key, value) #if isinstance(v, LinkedList): #value.label = key super().__init__() self.system = system """Reference to System this object belongs to.""" self.name = name """Unique identifier of object.""" """Every LinkedList contains a WeakSet.""" for cl in self.__class__.__mro__: for key, value in cl.__dict__.items(): if isinstance(value, LinkedList): self.__dict__[key] = WeakSet() for cl in self.__class__.__mro__: for key, value in cl.__dict__.items(): if isinstance(value, Spectrum): self.__dict__[key] = np.zeros(self.system.frequency.amount) #for key, value in self.__class__.__dict__.items(): ##if isinstance(value, LinkedList): #print(key, value) #for key, value in self.__dict__.items(): ##if isinstance(value, LinkedList): #print(key, value) if properties: for key, value in properties.items(): #if key in self.__class__.__dict__.keys(): #if hasattr(self, key): setattr(self, key, value) logging.info("Constructor %s: Created object %s of type %s", self.name, self.name, str(type(self)))
def __del__(self): """Destructor. """ pass def __str__(self): return "{}({})".format(self.SORT, self.name) name = Name() """ Name of object. The name of the object is unique. .. seealso:: :class:`Name` """ @abc.abstractmethod
[docs] def disable(self): """ Disable this object. """
@abc.abstractmethod
[docs] def enable(self): """ Enable this object. """
_DEPENDENCIES = [] """ Dependencies of object on other objects. """ @property
[docs] def included(self, extended=False): """ Indicates whether the object is included in the analysis. :param extended: Whether to show a list of dependencies with outcomes. :type extended: bool :rtype: bool or list """ if extended: return [(dep, getattr(getattr(self, dep), 'included')) for dep in self._DEPENDENCIES] + [('enabled', self.enabled)] else: return self.enabled and all([getattr(getattr(self, dep), 'included') for dep in self._DEPENDENCIES])
_enabled = True """ Container for storing whether the object is enabled or not. """ @property
[docs] def enabled(self): """ Switch indicating whether the object is enabled. :returns: A boolean indicating whether the object is enabled (`True`) or not (`False`) :rtype: :func:`bool` """ return self._enabled
@property
[docs] def frequency(self): """ Frequency. .. seealso:: :meth:`seapy.system.System.frequency` """ return self.system.frequency #def _get_spectrum(self): #pass #def _set_spectrum(self, x): #pass #def newSpectrum(self, name): #setattr(self, '_' + name, Spectrum(self.frequency)) #setattr(self, name, property(fget=_get_spectrum, fset=_set_spectrum))
@property
[docs] def classname(self): """Name of class of the object. This is an alias for ``obj.__class__.__name__``. """ return self.__class__.__name__
[docs] def plot(self, quantity, yscale='linear'): """Plot `quantity`. :seealso: :func:`seapy.tools.plot` """ return plot(self.frequency.center, [getattr(self, quantity)], quantity, self.frequency.enabled, yscale=yscale)
[docs] def info(self, attributes=None): """Return dataframe.""" data = {attr: getattr(self, attr) for attr in attributes if hasattr(self, attr)} df = pd.DataFrame(data, index=self.frequency.center.astype('int')).T return df #def info(self, attributes=None, tablefmt='simple'): #"""Information about the object.""" #header = ['Attribute'] + self.frequency.center.astype("int").astype("str").tolist() #data = ([attr] + (getattr(self, attr)*np.ones(self.frequency.amount)).tolist() for attr in attributes) #return tabulate(data, headers=header, tablefmt=tablefmt)