#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""MainBreaker model.
Models a main breaker on an frc robot. Uses data from datasheet
"""
import logging
import pandas as pd
import numpy as np
from scipy import optimize
from frc_rekt.helpers import get_file_encoding, plot_func
# Pandas options
pd.set_option('max_rows', 121)
pd.set_option('max_columns', 132)
pd.set_option('expand_frame_repr', False)
# just a convenience, so we dont have to type np.poly.poly
POLY = np.polynomial.polynomial
[docs]class MainBreaker(object): # pylint: disable=too-many-instance-attributes
"""Model of a Mainbreaker."""
def __init__(self, ambient_temp=25):
"""MainBreaker.
:param ambient_temp: The ambient temperature of the breaker
:type ambient_temp: int float
"""
self._logger = logging.getLogger(__name__)
self.rated_current = 120.0
self.ambient_temp = ambient_temp
self._temp_derate_min_frames = self._get_temp_derate_frames()
self._trip_time_frames = self._get_trip_time_frames()
self._generate_functions()
self._logger.debug('Main Breaker created at %s degrees C',
self.ambient_temp)
@staticmethod
def _get_file_name(datatype='temp_derate', boundary='min'):
directory = 'data/data_sheets'
filename = '120-main-breaker-{0}-{1}.csv'.format(datatype, boundary)
path = '{0}/{1}'.format(directory, filename)
encoding = get_file_encoding(path)
return path, encoding
def _get_frame(self, datatype='temp_derate', boundary='min'):
file_path, encoding = self._get_file_name(
datatype=datatype, boundary=boundary)
self._logger.debug('Opening dataframe: %s', file_path)
d_frame = pd.DataFrame(
pd.read_csv(file_path, encoding=encoding, comment='#')
) # The cast to DataFrame is due to bug: https://github.com/PyCQA/pylint/issues/1161
self._logger.debug('Opened dataframe: %s', d_frame)
return d_frame
def _get_temp_derate_frames(self):
frames = {
'min': self._get_frame(datatype='temp_derate', boundary='min'),
'max': self._get_frame(datatype='temp_derate', boundary='max')
}
return frames
def _get_trip_time_frames(self):
frames = {
'min': self._get_frame(datatype='trip_time', boundary='min'),
'max': self._get_frame(datatype='trip_time', boundary='max')
}
return frames
@staticmethod
def _fit_func_factory(a=None, b=None, c=None, d=None, e=None):
# Specific with correction
if a and b and c and d and e:
def func(x):
"""Specific Function."""
return a * ((b * (x + c))**d) + e
# Specific without correction
elif a and b and c and d:
def func(x):
"""Specific Function."""
return a * ((b * (x + c))**d)
# Generic, which we have scipy.optimize.curve_fit run on
# scipy.optimize.curve_fit will then give us a, b, c, d,
# however, scipy.optimize has touble with e. We correct e "by hand"
# at the end.
else:
def func(x, a, b, c, d):
"""Unparameterized Function."""
return a * ((b * (x + c))**d)
return func
def _generate_functions(self, plot=False):
self.trip_time_min = self._generate_func(
datatype='trip_time',
boundary='min',
plot=plot,
fit_func_factory=self._fit_func_factory)
self.trip_time_max = self._generate_func(
datatype='trip_time',
boundary='max',
plot=plot,
fit_func_factory=self._fit_func_factory)
self.temp_derate_min = self._generate_func(
datatype='temp_derate', boundary='min', plot=plot)
self.temp_derate_max = self._generate_func(
datatype='temp_derate', boundary='max', plot=plot)
@staticmethod
def _generate_poly_fit(x, y, deg=3):
coefs = POLY.polyfit(x, y, deg)
func = POLY.Polynomial(coefs)
return func
@staticmethod
def _generate_func_fit(func_factory, x, y):
popt, pcov = optimize.curve_fit(func_factory(), x, y)
logging.debug(popt)
logging.debug(pcov)
# Static shift to have the end condition be nice
unshifted_func = func_factory(*popt)
end_diff = y.iloc[-1] - unshifted_func(x.iloc[-1])
logging.debug("end diff: %s", end_diff)
popt_shifted = np.append(popt, end_diff)
return func_factory(*popt_shifted)
def _generate_func(self,
datatype='trip_time',
boundary='min',
plot=False,
fit_func_factory=None):
d_frame = self._get_frame(datatype=datatype, boundary=boundary)
logging.debug("d_frame to fit: %s", d_frame)
x = d_frame[str(d_frame.columns[0])]
y = d_frame[str(d_frame.columns[1])]
if not fit_func_factory:
fitted_func = self._generate_poly_fit(x, y)
else:
fitted_func = self._generate_func_fit(fit_func_factory, x, y)
if plot:
plot_func(d_frame, fitted_func, title='main_breaker')
return fitted_func
[docs] def trip_time(self, current):
"""Trip time min, max in seconds for given amount of current."""
current_percent = current / self.rated_current
return self.trip_time_min(current_percent), self.trip_time_max(
current_percent)
[docs] def temperature_derate(self, temp):
"""Trip time derating, based on temperature."""
temp_c = (temp - 32) / 1.8
return self.temp_derate_min(temp_c), self.temp_derate_max(temp_c)