# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/07_modelling.mypvfactors.ipynb (unless otherwise specified).

__all__ = ['system_def', 'get_index', 'plot_idx', 'fit_and_plot', 'join_sun', 'get_data', 'fn_build_report',
           'run_pvfactors_simulation', 'individual_report']

# Cell
from ..imports import *
from ..utils.tmy import *
import pvfactors
from pvfactors.geometry import OrderedPVArray
from pvfactors.engine import PVEngine
import pvlib

# Cell
def system_def(albedo: float=0.4,
               n_modules_vertically: int=2,
               module_size: tuple=(1.69,1.01),
               h_ground: float=1,
               surface_tilt: int=38,
               axis_azimuth: int=0,
               surface_azimuth: int=180,
               n_pvrows: int=1,
               tracking: bool=False,
               gcr: float=0.5):
    "A wrapper function to define the pvfactors pvarray_parameters dictionary"
    w_m, h_m = module_size  #width x height of module
    if not tracking: h_center = h_ground + np.sin(2*np.pi*surface_tilt/360)*h_m
    else: h_center = h_ground
    pvarray_parameters = {
        'n_pvrows': n_pvrows,            # number of pv rows
        'pvrow_height': h_center,        # height of pvrows (measured at center / torque tube)
        'pvrow_width': n_modules_vertically * h_m,         # width of pvrows
        'tracking':tracking,
        'axis_azimuth': axis_azimuth,       # azimuth angle of rotation axis
        'surface_tilt': surface_tilt,      # tilt of the pv rows
        'surface_azimuth': surface_azimuth,   # azimuth of the pv rows front surface
        'albedo':albedo,
        'gcr': gcr,               # ground coverage ratio,
        'rho_front_pvrow': 0.075,  # pv row front surface reflectivity
        'rho_back_pvrow': 0.075,    # pv row back surface reflectivity
        'cut':{
            i: {'front': 1,'back': 7} for i in range(n_pvrows) # discretize the front  PV row into 1 segments and back in 7
    }
    }
    return pvarray_parameters

# Cell
def get_index(df, date): return df.index.get_loc(date)

# Cell
def plot_idx(pvarray, idx=4550, with_index=False, figsize=(10,3)):
    "Plot a pvarray at idx"
    f, ax = plt.subplots(figsize=figsize)
    pvarray.plot_at_idx(idx, ax, with_surface_index=with_index)
#     ax.set_xlim(-5, 5)
    return ax

# Cell
def fit_and_plot(data:DataFrame, pvarray_params:dict, date:Union[str, int], with_index:bool=False):
    "Method to debug a pvfactors pvarray, plots the model at idx"
    data_idx = data.loc[date] if type(date) is str else data.iloc[date]
    params = {'axis_azimuth': pvarray_params['axis_azimuth'],
             'gcr': pvarray_params['gcr'],
             'n_pvrows': pvarray_params['n_pvrows'],
             'pvrow_height': pvarray_params['pvrow_height'],
             'pvrow_width': pvarray_params['pvrow_width'],
             'solar_azimuth': data_idx['azimuth'],
             'solar_zenith': data_idx['zenith'],
             'surface_azimuth': pvarray_params['surface_azimuth'],
             'surface_tilt': pvarray_params['surface_tilt'],
             'cut':pvarray_params['cut']}
    pvarray = OrderedPVArray.fit_from_dict_of_scalars(params)
    ax = plot_idx(pvarray, 0, with_index)
    if type(date) is int:
        date = data.index[date]
        date = f'{date.hour}h {date.day} {date.month_name()[0:3]} {date.year}'
    ax.set_title(date)
    return pvarray

# Cell
def join_sun(tmy_data: DataFrame, sunpos:DataFrame, pvarray_params:dict)->DataFrame:
    "Concats meteo data with sunpos"
    data = pd.DataFrame(index=tmy_data.index)
    data['ghi'] = tmy_data.ghi
    data['dni'] = tmy_data.dni
    data['dhi'] = tmy_data.dhi
    data['zenith'] = sunpos.zenith
    data['azimuth'] = sunpos.azimuth
    data['elevation'] = sunpos.elevation
    data['surface_tilt'] = pvarray_params['surface_tilt']
    data['surface_azimuth'] = pvarray_params['surface_azimuth']
    data['albedo'] =  pvarray_params['albedo']
    #doing some patching
    idxs = (data.zenith<90) & (data.ghi<10)
    data.loc[idxs, 'zenith'] = 91.
    return data

# Cell
def get_data(tmy_file:File, pvarray_params:dict, reader:str='pvgis', coerce_year:int=2019):
    "A convenience method to read PVGIS and generic tmy files"
    if reader == 'pvgis':
        gps_data, _, tmy_data = read_pvgis(tmy_file, coerce_year=coerce_year)
    else:
        gps_data, tmy_data = read_tmy(tmy_file, coerce_year=coerce_year)

    sunpos = (pvlib
             .solarposition
             .get_solarposition(tmy_data.index,
                                gps_data['Latitude'],
                                gps_data['Longitude'],
                                gps_data['Elevation'])
             )

    data = join_sun(tmy_data, sunpos, pvarray_params)
    if pvarray_params['tracking']:
        back_track = False
        if pvarray_params['n_pvrows']>1:
            back_track = True
        tracking = (pvlib
                    .tracking
                    .singleaxis(apparent_zenith=data.zenith,
                                apparent_azimuth=data.azimuth,
                                axis_azimuth=pvarray_params['axis_azimuth'],
                                backtrack=back_track,
                                gcr=pvarray_params['gcr'])
                    .fillna(0))
        data.surface_azimuth = tracking.surface_azimuth
        data.surface_tilt = tracking.surface_tilt
    return data

# Cell
fn_build_report=lambda pvarray: pvarray

# Cell
def run_pvfactors_simulation(data: DataFrame, pvarray_params: dict)->OrderedPVArray:
    "Fit and run simulation"
    pvarray = OrderedPVArray.init_from_dict(pvarray_params)
    engine = PVEngine(pvarray)
    pvarray = engine.fit(data.index,
            data.dni,
            data.dhi,
            data.zenith,
            data.azimuth,
            data.surface_tilt,
            data.surface_azimuth,
            data.albedo)
    fn_build_report=lambda pvarray: pvarray
    return engine.run_full_mode(fn_build_report)

# Cell
def individual_report(pvarray:OrderedPVArray, row:int=0, index:pd.Index=None)->DataFrame:
    "Recover individual backface measures"
    back_surface_list = pvarray.ts_pvrows[row].back.list_segments
    index = ifnone(index, range(back_surface_list[0].length.shape[0]))
    irr_dict = {f'qinc_{idx}':s.get_param_weighted('qinc') for idx, s in enumerate(back_surface_list)}
    irr_dict.update({'qinc_front': pvarray.ts_pvrows[row].front.get_param_weighted('qinc'),
                    'qinc_back': pvarray.ts_pvrows[row].back.get_param_weighted('qinc')})
    return pd.DataFrame(irr_dict, index=index)