import six
from th_component import THSuperComponent
from utilities.ur import units
[docs]class THSystem(object):
    """This class models:
    - lumped capacitance model for slab geometry
    - 1-D heat diffusion in spherical geometry, for heat
      generation at any radius in the sphere,
    - advective heat transfer by fluid
    - convective heat transfer at the solid surface to fluid.
    """
    def __init__(self, kappa, components):
        self.kappa = kappa
        self.components = components
[docs]    def comp_from_name(self, name):
        """Returns the component with the matching name
        """
        for comp in self.components:
            if comp.name == name:
                return comp
        # uh oh, none was found
        msg = "There is no component with the name: "
        msg += name
        raise KeyError(msg)
 
[docs]    def dtempdt(self, component, power, omegas, t_idx):
        '''compute the derivative dtemperature/dt
        :param component: name of the component that dtemp/dt is calculated
        :type component: str
        :param power: nuclear power density
        :type power: float
        :param omegas: decay heat nuclear data
        :type omegas: list
        :param t_idx: the timestep that dtempdt is calculated for
        :type t_idx: int
        :return: value of dtemp/dt
        :rtype: float, kelvin/s
        '''
        to_ret = 0.0
        if isinstance(component, THSuperComponent):
            #  return 0 for superComponent, doesn't calculate the temperature
            #  variation of superComponent
            return to_ret*units.kelvin/units.second
        else:
            cap = (component.rho(t_idx).magnitude*component.cp.magnitude)
            if component.sph and component.ri.magnitude == 0.0:
                Qcent = self.BC_center(component, t_idx)
                to_ret -= Qcent/cap
            for interface, d in six.iteritems(component.convBC):
                env = self.comp_from_name(interface)
                QconvBC = self.convBoundary(component,
                                            t_b=component.T[t_idx].magnitude,
                                            t_env=env.T[t_idx].magnitude,
                                            h=d["h"].h(env.rho(t_idx),
                                                       env.mat.mu),
                                            R=d["R"])
                to_ret -= QconvBC/cap
            if component.heatgen:
                to_ret += self.heatgen(component, power, omegas)/cap
            for interface, d in six.iteritems(component.cond):
                env = self.comp_from_name(interface)
                if component.sph:
                    Qcond = self.conductionFVM(component, env, t_idx)
                else:
                    Qcond = self.conduction_slab(component, env, t_idx,
                                                 L=d["L"],
                                                 A=d["area"])
                to_ret -= Qcond/cap
            for interface, d in six.iteritems(component.conv):
                env = self.comp_from_name(interface)
                if isinstance(env, THSuperComponent):
                    Tr = env.compute_tr(component.T[t_idx].magnitude,
                                        env.sub_comp[-2].T[t_idx].magnitude,
                                        h=d['h'].h(component.rho(t_idx),
                                                   component.mat.mu).magnitude)
                    Qconv = self.convection(t_b=component.T[t_idx].magnitude,
                                            t_env=Tr,
                                            h=d['h'].h(component.rho(t_idx),
                                                       component.mat.mu),
                                            A=d['area'])
                    assert (Qconv*(component.T[t_idx].magnitude-Tr)) >= 0, '''
                    convection from %s to %s, from low temperature %f to
                    high %f is not physical: %f''' % (
                        component.name, env.name, component.T[t_idx].magnitude,
                        Tr, Qconv.magnitude)
                else:
                    Qconv = self.convection(t_b=component.T[t_idx].magnitude,
                                            t_env=env.T[t_idx].magnitude,
                                            h=d['h'].h(component.rho(t_idx),
                                                       component.mat.mu),
                                            A=d['area'])
                    assert (Qconv*(component.T[t_idx]-env.T[t_idx])).magnitude >= 0, \
                        
'convection from %s to %s, %fc to %fc is not physical' \
                        
% (component.name, env.name,
                           component.T[t_idx].magnitude,
                           env.T[t_idx].magnitude)
                to_ret -= Qconv/cap/component.vol.magnitude
            for name, d in six.iteritems(component.adv):
                Qadv = self.advection(component,
                                      t_idx,
                                      t_in=d['t_in'].magnitude,
                                      m_flow=d['m_flow'],
                                      cp=d['cp'])
                to_ret -= Qadv/cap/component.vol.magnitude
            return to_ret*units.kelvin/units.seconds
 
[docs]    def BC_center(self, component, t_idx):
        '''Volumetric conductive heat flux Qconduction from the center of a
        sphere to the first boundary
        (conduction without interface) in watts/meter**3
        :param component: name of the inner most component(mesh element)
        :type component: str
        :param t_idx: timestep at which Qconduction is calculated
        :type t_idx: int
        :return : Qcondction
        :rtype:float, dimensionless
        '''
        conductivity = component.k.magnitude
        T_b = component.T[t_idx].magnitude
        dr = (component.ro - component.ri).magnitude
        return (conductivity*T_b)/(dr**2)
 
[docs]    def convBoundary(self, component, t_b, t_env, h, R):
        '''calculate heat transfer through convective boundray condition
        for the mesh element at the surface of the spherical Supercomponent
        (watts)
        :param component: name of the outer most solid component
        :type component: str
        :param t_b: temperature of the body
        :type t_b: float
        :param t_env: temperature of the environment(fluid)
        :type t_env: float
        :param h: convective heat transfer coefficient
        :type h: float
        :param R: outer radius of the component
        :type R: float
        :return: dimensionless quantity of Qconv
        :rtype: float
        '''
        r_b = component.ro.magnitude
        k = component.k.magnitude
        dr = component.ri.magnitude-component.ro.magnitude
        T_R = (-h.magnitude/k*t_env + t_b/dr)/(1/dr-h.magnitude/k)
        to_ret = 1/r_b*k*(r_b*t_b-R.magnitude*T_R)/dr**2
        return to_ret
 
[docs]    def heatgen(self, component, power, omegas):
        """
        calculate heat transfer by conduction(watts/m3)
        :param component: name of the component
        :type component: str
        :param power: normalized nuclear power generated in the component
        i.e.: power*power_tot = power (in watts)
        :type power: float
        """
        return (power*component.power_tot.magnitude *
                (1-self.kappa) + sum(omegas))/component.vol.magnitude
 
[docs]    def conductionFVM(self, component, env, t_idx, L=0.0*units.meter,
                      k=0.0*units.meter, A=0.0*units.meter**2):
        """
        compute volumetric conductive heat transfer by conduction(watts/m3)
        :param component: name of the component
        :type component: str
        :param env: name of the environment
        :type env: str
        :param t_idx: time step that conduction heat is computed
        :type t_idx: int
        :param L: conduction distance
        :type L: float, units meter
        :pram k: conductivity
        :type k: float, units w/mk
        :return: Qond, dimemsionless quantity
        :rtype: float
        """
        T_b = component.T[t_idx].magnitude
        T_env = env.T[t_idx].magnitude
        r_b = component.ro.magnitude
        r_env = env.ro.magnitude
        dr = (component.ro-component.ri).magnitude
        k = component.k.magnitude
        return k/r_b * (r_b * T_b - r_env * T_env)/(dr**2)
 
[docs]    def conduction_slab(self, component, env, t_idx, L,
                        A):
        """
        compute volumetric heat transfer by conduction(watts/m3)
        :param component: name of the component
        :type component: str
        :param env: name of the environment
        :type env: str
        :param t_idx: time step that conduction heat is computed
        :type t_idx: int
        :return: Qond, dimemsionless quantity
        :rtype: float
        """
        assert(A.magnitude>0)
        assert(L.magnitude>0)
        T_b = component.T[t_idx].magnitude
        T_env = env.T[t_idx].magnitude
        num = (T_b-T_env)
        k = component.k
        denom = (L/(k*A)).magnitude
        return num/denom
 
[docs]    def advection(self, component, t_idx, t_in, m_flow, cp):
        ''' calculate heat transfer by advection in watts
        :param component: name of the component
        :type component: str
        :param t_idx: time step that conduction heat is computed
        :type t_idx: int
        :param t_in: inlet temperature
        :type t_in: float
        :param m_flow: mass flow rate though the control volume
        :type m_flow: float
        :param cp: specific heat capacity of the fluid
        :type cp: float
        :return: dimemsionless quantity of Qadvective
        :rtype: float
        '''
        #check if the temperature is the initial temperature 0degC
        #set Qadv=0 in this case for computation stability
        if component.T[t_idx] == 0*units.degC.to('kelvin'):
            Qadv = 0
        else:
            t_out = component.T[t_idx].magnitude*2.0 - t_in
            Qadv = m_flow.magnitude*cp.magnitude*(t_out-t_in)
        return Qadv
 
[docs]    def mass_trans(self, t_b, t_inlet, H, u):
        """
        :param t_b: The temperature of the body
        :type t_b: float.
        :param t_inlet: The temperature of the flow inlet
        :type t_inlet:
        """
        num = 2.0*u*(t_b - t_inlet)
        denom = H
        return num/denom
 
[docs]    def convection(self, t_b, t_env, h, A):
        """
        heat transfer by convection(watts)
        :param t_b: The temperature of the body
        :type t_b: float.
        :param t_env: The temperature of the environment
        :type t_env: float.
        :param h: the heat transfer coefficient between environment and body
        :type h: float.
        :param A: the surface area of heat transfer
        :type A: float.
        """
        num = (t_b-t_env)
        denom = (1.0/(h.magnitude*A.magnitude))
        return num/denom
 
[docs]    def custom(self, t_b, t_env, res):
        num = (t_b-t_env)
        denom = res.to(units.kelvin/units.watt)
        return num/denom
 
[docs]    def record(self, component):
        """a recorder function that calls down to each component.
        used for the th/th_timeseries table
        """
        return self.comp_from_name(component).record()