Advertisement
Guest User

Untitled

a guest
Aug 18th, 2019
166
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 35.55 KB | None | 0 0
  1. # -*- coding: utf-8 -*-
  2. """
  3. INITIAL COMMENTS FROM PATRIZIO (may not be up to date)
  4. Created on Mon Mar  4 11:22:27 2019
  5.  
  6. @author: mangan36
  7. In this version of the framework:
  8. - Subfunctions save to main variables
  9. - Thermal network has been updated (from 3-rd order max to static, via a Nyquist limit analysis)
  10.     - Thermal predictor reduction more accurate
  11. - Wind model based on the work of Imre is considered. To used LUT-based model:
  12.     - Uncomment lines that run this model in "system_solver_"
  13.     - Comment lines that run the model of Imre in "system_solver_"
  14. - Net Heat is calculated from Basic Component "ideal" power (meaning without considering Rs losses). To change this:
  15.     - Uncomment lines that activate the inclusion of Rs in "system_solver_"
  16. - The time vector is assumed to have constant sampling. However, the code can be easily adapted to run with a variable sampling time:
  17.     - In "system_solver_", the last input of "Temperature_Prediction_3rd_order_Simplified", namely "Ts_DT", must be adapted at runtime to be equal to the current timestep (time_vector[sim_index]-time_vector[sim_index-1])
  18.     - In "main", we create the predictor using the function "temperature_predictor_variable_order", that needs as input the sampling time "Ts_DT".
  19.         - In order to properly select the thermal network order (and fulfill with Nyquist theory), the minimum input timestep must be used as input for "temperature_predictor_variable_order"
  20.         - Small difference in sampling time do not create issues. However, if timestep changed significantly (e.g. from 1 sec up to 5 min), some errors and/or accuracy losses might happen
  21.  
  22. N.B. TO RUN PARALLEL, first of all clusters must be started in the anaconda prompt running the command
  23.        ipcluster start -n 4 (where 4 means the number of cores we want to use)
  24.    Also, parallel computation speed has not been fully tested, and it is probably limited by the fact that subfunctions save to main variables
  25. """
  26.  
  27. """
  28. Issues and Possible solutions:
  29.    A) Simulation is slower than in Matlab, mainly because of:
  30.        1. thermal coefficient run-time calculation: "lambdify" creates lambda functions, that are time-consuming to be solved:
  31.            - coefficients should be create as standard functions (as done in matlab by means of the command "matlabFunction")
  32.        2. interpolation is done with "interp1d". It allows extrapolation, needed to avoid big errors, but it is slow:
  33.            - numpy interp is much faster, but do not allow extrapolation. An optimized interpolating function might be programmed
  34.        3. code of wind model of Imre can probably be optimized for higher computation speed
  35.    B) When compared with Spectre:
  36.        1. If wind is not included:
  37.            - Spectre and Matlab/Python results are extremely similar, both in terms of dynamics (no delay or significant difference in behavior) and absolute values:
  38.                - Modeling radiation loss as variable resistance does not induce error
  39.                - The thermal network is solved properly
  40.        2. If wind is included (as LUT-based thermal resistors, since this is the only model implemented in Spectre):
  41.            - Second resolution: results are quite similar
  42.            - Minute resolution or above: some fast dynamics due to wind are obviously lost. It is important to understand:
  43.                - if this spectre-simulated results reflect the wind effect realistically
  44.                - if a different approach (e.g. considering wind as a "dependant source" instead of a variable resistance) leads to more realistic results (or at least to a lower error due to non-simulated dynamics):
  45.                     - Validation with measurements instead of comparison with Spectre is needed for this!
  46.                     - The simulation approach can be adapted accordingly to any new development of the wind cooling model
  47.    C) Wind model of Imre needs to be fixed: sometimes it gives the error "invalid value encountered in double_scalars"
  48.    D) Parallel processing would be probably much faster if sub-function do not save to main variable (each writing slow down computation)?
  49. """
  50.  
  51. import os
  52. import multiprocess
  53. from multiprocess import Pool
  54.  
  55. import numpy as np
  56. import time
  57. from scipy.interpolate import interp1d
  58.  
  59. import matplotlib.pyplot as plt
  60. import scipy.io
  61.  
  62. from core.climatic_data import *
  63. from core.module import *
  64. from core.energy_yield_res import *
  65. from core.wind_model import wind_thermal_res_front_and_back
  66. from core.interpolate import interpolate_row_by_row
  67. import numba
  68. import platform
  69.  
  70. class EnergyYieldSimParams:
  71.     """
  72.    This class encompasses parameters that control the energy yield
  73.    computation.
  74.    """
  75.  
  76.     def __init__(self, nbr_of_cores=1, nbr_points_per_IV_curve=100,
  77.     steps_accuracy=3, first_day_to_simulate=1, min_irradiance=0, debug_mode=False,
  78.     debug_dir="debug"):
  79.         # How many point do we want to have per IV curve
  80.         self.nbr_points_per_IV_curve = nbr_points_per_IV_curve
  81.         # A minimum  value of 2 is recommended
  82.         self.steps_accuracy = steps_accuracy
  83.         self.first_day_to_simulate = first_day_to_simulate
  84.         self.nbr_of_cores = nbr_of_cores
  85.         #Will impact the timestamps at which simulation start (i.e. there is no
  86.         #point to start simulation too soon before sunrise until too far after
  87.         #sunset)
  88.         self.min_irradiance = min_irradiance
  89.         self.debug = DebugEnergyYield(debug_mode, debug_dir)
  90.  
  91. class DebugEnergyYield:
  92.  
  93.     def __init__(self, debug_mode=False, debug_dir="debug"):
  94.  
  95.         self.debug_mode = debug_mode
  96.         if self.debug_mode:
  97.             self.debug_dir = debug_dir
  98.  
  99.             if not os.path.exists(self.debug_dir):
  100.                 try:
  101.                     os.makedirs(self.debug_dir)
  102.                 except OSError as exc:
  103.                     if exc.errno != errno.EEXIST:
  104.                         raise
  105.  
  106.             self.default_name_debug_file_count = 0
  107.  
  108.     def write_debug_mat_file(self, names, values, file_name=None):
  109.  
  110.         assert(self.debug_mode)
  111.  
  112.         if file_name is None:
  113.             file_name = "proc_id_"+str(os.getpid())+"_"+\
  114.             str(self.default_name_debug_file_count)
  115.             self.default_name_debug_file_count = \
  116.             self.default_name_debug_file_count +1
  117.  
  118.         debug_file_path = self.debug_dir+"/"+file_name+".mat"
  119.         dict_to_save = dict(zip(names, values))
  120.         scipy.io.savemat(debug_file_path, dict_to_save)
  121.  
  122. def radiation_thermal_res(T_surface, T_amb, surface_amb_T_diff, cell_area,
  123. horiz_surface_temp_diff, accuracy_dep_idx, day_idx, sim_idx, emissivity):
  124.  
  125.     sigma = Constants.STEFAN_BOLTZMANN
  126.     cell_area_msquared = cell_area/10**4
  127.  
  128.     if type(horiz_surface_temp_diff) == np.ndarray:
  129.         horiz_surface_temp_diff = horiz_surface_temp_diff[day_idx, sim_idx]
  130.  
  131.     horiz_surface_amb_T_diff = T_amb[day_idx][sim_idx]-horiz_surface_temp_diff
  132.  
  133.     return surface_amb_T_diff/(cell_area_msquared*sigma*emissivity*( \
  134.     T_surface[:, accuracy_dep_idx]**4-horiz_surface_amb_T_diff**4))
  135.  
  136. def radiation_thermal_res_front_and_back(T_surface_front, T_surface_back, T_amb,
  137. glass_amb_delta_T, backsheet_amb_delta_T, cell, sky_temp_offset,
  138. ground_temp_offset, accuracy_dep_idx, day_idx, sim_idx):
  139.  
  140.     rad_therm_res_front = radiation_thermal_res(T_surface_front, T_amb,
  141.     glass_amb_delta_T, cell.area, sky_temp_offset, accuracy_dep_idx, day_idx,
  142.     sim_idx, cell.front_glass_emissivity)
  143.     rad_therm_res_back = radiation_thermal_res(T_surface_back, T_amb,
  144.     backsheet_amb_delta_T, cell.area, ground_temp_offset, accuracy_dep_idx,
  145.     day_idx, sim_idx, cell.back_sheet_emissivity)
  146.  
  147.     return (rad_therm_res_front, rad_therm_res_back)
  148.  
  149. def thermal_res_front_and_back(T_amb, T_glass, T_backsheet, wind_speed_front,
  150. wind_speed_back, wind_direction, day_idx, accuracy_dep_idx,
  151. sim_idx, string_of_modules, sky_temp_offset, ground_temp_offset):
  152.  
  153.     cell = string_of_modules.module.cell
  154.  
  155.     glass_amb_delta_T = T_glass[:, accuracy_dep_idx]-T_amb[day_idx, sim_idx]
  156.     backsheet_amb_delta_T = T_backsheet[:, accuracy_dep_idx]- \
  157.     T_amb[day_idx, sim_idx]
  158.  
  159.     R_wind_front, R_wind_back = wind_thermal_res_front_and_back(
  160.     glass_amb_delta_T, backsheet_amb_delta_T, wind_speed_front, wind_speed_back,
  161.     wind_direction, day_idx, sim_idx, string_of_modules)
  162.  
  163.     R_rad_front, R_rad_back = \
  164.     radiation_thermal_res_front_and_back(T_glass, T_backsheet, T_amb,
  165.     glass_amb_delta_T, backsheet_amb_delta_T, cell, sky_temp_offset,
  166.     ground_temp_offset, accuracy_dep_idx, day_idx, sim_idx)
  167.  
  168.     def tot_thermal_res(R_wind, R_rad):
  169.  
  170.         R = np.zeros(R_wind.shape)
  171.  
  172.         R_wind_gt_zero = R_wind > 0
  173.         R_wind_le_zero = np.logical_not(R_wind_gt_zero)
  174.         R_rad_gt_zero = R_rad > 0
  175.         R_rad_le_zero = np.logical_not(R_rad_gt_zero)
  176.  
  177.         R_wind_rad_gt_zero = np.logical_and(R_wind_gt_zero, R_rad_gt_zero)
  178.         R_wind_rad_le_zero = np.logical_and(R_wind_le_zero, R_rad_le_zero)
  179.         only_R_wind_gt_zero = np.logical_and(R_wind_gt_zero, R_rad_le_zero)
  180.         only_R_rad_gt_zero = np.logical_and(R_wind_le_zero, R_rad_gt_zero)
  181.  
  182.         R[R_wind_rad_gt_zero] = (R_wind*R_rad/(R_wind+R_rad))[R_wind_rad_gt_zero]
  183.         R[only_R_wind_gt_zero] = R_wind[only_R_wind_gt_zero]
  184.         R[only_R_rad_gt_zero] = R_rad[only_R_rad_gt_zero]
  185.         R[R_wind_rad_le_zero] = 10**6
  186.  
  187.         return R
  188.  
  189.     R_front_num = tot_thermal_res(R_wind_front, R_rad_front)
  190.     R_back_num = tot_thermal_res(R_wind_back, R_rad_back)
  191.  
  192.     return (R_front_num, R_back_num)
  193.  
  194. #@numba.jit
  195. def energy_yield_for_one_day(climatic_data, string_of_modules, index_min,
  196. index_max, steps_accuracy, nbr_cells_in_basic_comp, sky_temp_offset,
  197. ground_temp_offset, time_step, thermal_predictor, Heat_Irr_mat, Iph_mat,
  198. Vdiode_BC, R_in, R_out, debug, day_index):
  199.  
  200.     irradiance = climatic_data.global_irradiance
  201.     T_amb = climatic_data.ambient_temperature
  202.     wind_direction = climatic_data.wind_direction
  203.     wind_speed_back = climatic_data.wind_speed_back
  204.     wind_speed_front = climatic_data.wind_speed_front
  205.  
  206.     nbr_of_basic_components = irradiance.shape[1]
  207.     nbr_of_timestamps_per_day = irradiance.shape[2]
  208.  
  209.     cell = string_of_modules.module.cell
  210.     cell_elec = cell.electrical_prop
  211.  
  212.     I_BC_mat = np.zeros((nbr_of_basic_components, len(Vdiode_BC)))
  213.     Pmpp_system = np.zeros(nbr_of_timestamps_per_day)
  214.     Vmpp_system = np.zeros(nbr_of_timestamps_per_day)
  215.     Pmpp_BC = np.zeros((nbr_of_basic_components, nbr_of_timestamps_per_day))
  216.  
  217.     T_amb_at_day = T_amb[day_index]
  218.     Tcell_mat = np.reshape(T_amb_at_day, (1, nbr_of_timestamps_per_day))
  219.     Tcell_mat = np.repeat(Tcell_mat, nbr_of_basic_components, axis=0)
  220.     Tglass_mat = Tcell_mat.copy()
  221.     Tbacksheet_mat = Tcell_mat.copy()
  222.     Net_Heat = Heat_Irr_mat[day_index].copy()
  223.  
  224.     debug_names = []
  225.     debug_values = []
  226.  
  227.     for sim_index in range(int(index_min[day_index]),int(index_max[day_index]+1)):
  228.  
  229.         for index_accuracy in range(steps_accuracy):
  230.             if index_accuracy == 0:
  231.                 accuracy_dep_index = sim_index-1
  232.             else:
  233.                 accuracy_dep_index = sim_index
  234.  
  235.             R_front_num, R_back_num = thermal_res_front_and_back(T_amb,
  236.             Tglass_mat, Tbacksheet_mat, wind_speed_front, wind_speed_back,
  237.             wind_direction, day_index, accuracy_dep_index, sim_index,
  238.             string_of_modules, sky_temp_offset, ground_temp_offset)
  239.  
  240.             Ppv_Pred_for_NetHeat = Pmpp_BC[:, accuracy_dep_index]/nbr_cells_in_basic_comp
  241.  
  242.             temp_prediction_3rd_order_with_back_rad_loss(
  243.             Ppv_Pred_for_NetHeat,R_front_num,R_back_num,day_index,
  244.             sim_index,time_step, thermal_predictor, Heat_Irr_mat, Net_Heat,
  245.             T_amb, Tcell_mat, Tglass_mat, Tbacksheet_mat)
  246.  
  247.             I_BC_mat = BC_IV((Iph_mat[day_index, :, sim_index])[:, np.newaxis],
  248.             (Tcell_mat[:, sim_index])[:, np.newaxis], cell_elec,
  249.             nbr_cells_in_basic_comp, Vdiode_BC)
  250.  
  251.             system_obj(R_in, R_out, Vdiode_BC, I_BC_mat, day_index, sim_index,
  252.             Pmpp_system, Vmpp_system, Pmpp_BC, nbr_of_basic_components, debug,
  253.             debug_names, debug_values)
  254.  
  255.             # Uncomment next line to include Rs in Net Heat calculation
  256.             #Pmpp_BC[day_index, :, sim_index] = Pmpp_BC[day_index, :, sim_index]\
  257.             #- Rs*nbr_cells_in_basic_comp*(Pmpp_system[day_index][sim_index]/Vmpp_system[day_index][sim_index])**2
  258.             Net_Heat[:, sim_index] =  Heat_Irr_mat[day_index,: , sim_index] - \
  259.             Pmpp_BC[:, sim_index]/nbr_cells_in_basic_comp
  260.  
  261.             if debug.debug_mode:
  262.                 debug_names.extend(["index_accuracy_"+str(day_index)+"_"+str(sim_index),
  263.                 "R_front_num_"+str(day_index)+"_"+str(sim_index),
  264.                 "R_back_num_"+str(day_index)+"_"+str(sim_index),
  265.                 "Ppv_Pred_for_NetHeat_"+str(day_index)+"_"+str(sim_index),
  266.                 "I_BC_mat_"+str(day_index)+"_"+str(sim_index)])
  267.                 debug_values.extend([index_accuracy, R_front_num,
  268.                 R_back_num, Ppv_Pred_for_NetHeat, I_BC_mat])
  269.  
  270.     if debug.debug_mode:
  271.         debug_names.extend(["Tglass_mat_"+str(day_index), "Tbacksheet_mat_"+str(day_index)])
  272.         debug_values.extend([Tglass_mat, Tbacksheet_mat])
  273.         file_name = "sim_loop_"+str(day_index)
  274.         debug.write_debug_mat_file(debug_names, debug_values, file_name)
  275.     return (Pmpp_system, Vmpp_system, Pmpp_BC, Tcell_mat, Net_Heat)
  276.  
  277.     #print('day #%s done' %(day_index+1))
  278.  
  279. def system_obj(Rin, Rout, voltage, current, day_index, sim_index, Pmpp_system,
  280. Vmpp_system, Pmpp_BC, nbr_of_basic_components, debug, debug_names, debug_values):
  281.  
  282.     # Series connection of BCs' I-V curves
  283.     voltages_at_common_currents, common_currents = IV_series_connection(
  284.     voltage, current)
  285.     voltage_tot = np.sum(voltages_at_common_currents, axis=0)
  286.  
  287.     # Adding total series resistance
  288.     voltage_tot_real = IV_real(Rin, Rout, voltage_tot, common_currents)
  289.     Pmpp = voltage_tot_real*common_currents
  290.     index_max = np.argmax(Pmpp) # find MPP index
  291.     Pmpp_system[sim_index] = Pmpp[index_max] # Calculate MPP Power
  292.     Vmpp_system[sim_index] = voltage_tot_real[index_max] # Calculate MPP voltage
  293.  
  294.     #find operating voltage
  295.     Vmax_BC = voltages_at_common_currents[:, index_max]
  296.     Imax_BC = common_currents[index_max]
  297.     Pmpp_BC[:, sim_index] = Vmax_BC*Imax_BC
  298.  
  299.     if debug.debug_mode:
  300.         debug_names.extend(
  301.         ["voltages_at_common_currents_"+str(day_index)+"_"+str(sim_index),
  302.         "common_currents_"+str(day_index)+"_"+str(sim_index),
  303.         "voltage_tot_"+str(day_index)+"_"+str(sim_index),
  304.         "voltage_tot_real_"+str(day_index)+"_"+str(sim_index),
  305.         "Pmpp_"+str(day_index)+"_"+str(sim_index),
  306.         "Pmpp_max_"+str(day_index)+"_"+str(sim_index),
  307.         "Vmpp_max_"+str(day_index)+"_"+str(sim_index),
  308.         "Vmax_BC_"+str(day_index)+"_"+str(sim_index),
  309.         "Imax_BC_"+str(day_index)+"_"+str(sim_index),
  310.         "Pmpp_BC_"+str(day_index)+"_"+str(sim_index)])
  311.         debug_values.extend([voltages_at_common_currents, common_currents,
  312.         voltage_tot, voltage_tot_real, Pmpp, Pmpp_system[sim_index],
  313.         Vmpp_system[sim_index], Vmax_BC, Imax_BC, Pmpp_BC[:, sim_index]])
  314.  
  315.  
  316. def comp_Vdiode_BC(cell_elec, sim_params, Iph_STC, nbr_cells_in_basic_comp,
  317. nbr_basic_components):
  318.     """
  319.    Basic Component Diode Voltage for I-V curve calculation
  320.    """
  321.  
  322.     #Maximum voltage at which Basic Component's IV curve is evaluated (without Rs) [V]
  323.     Voc_BC_max = cell_elec.V_max_open_circuit*nbr_cells_in_basic_comp
  324.     nbr_points_per_IV_curve = sim_params.nbr_points_per_IV_curve
  325.     Rsh = cell_elec.R_shunt
  326.     Rs = cell_elec.R_series
  327.     Vdiode_BC = np.append(-(Rsh+Rs)*nbr_cells_in_basic_comp*2.5*Iph_STC,
  328.     np.linspace(0,Voc_BC_max+Rs*nbr_cells_in_basic_comp*Iph_STC, nbr_points_per_IV_curve))
  329.     Vdiode_BC = np.reshape(Vdiode_BC, (1, Vdiode_BC.size))
  330.     Vdiode_BC = np.repeat(Vdiode_BC, nbr_basic_components, axis=0)
  331.     return Vdiode_BC
  332.  
  333. def temp_prediction_3rd_order_with_back_rad_loss(Ppv_Pred_for_NetHeat,
  334. R_front_num,R_back_num,day_index, sim_index,Ts, thermal_predictor, Heat_Irr_mat,
  335. Net_Heat, Tamb_mat, Tcell_mat, Tglass_mat, Tbacksheet_mat):
  336.  
  337.     thermal_coefs = np.array(list(map(
  338.     lambda therm_coef_fun: therm_coef_fun(R_front_num, R_back_num,Ts),
  339.     thermal_predictor)))
  340.     T_cell_coefs = thermal_coefs[0:4]
  341.     T_front_coefs = thermal_coefs[4:8]
  342.     T_back_coefs = thermal_coefs[8:12]
  343.     denom_coefs = thermal_coefs[12:15]
  344.  
  345.     prev_sim_idx = sim_index-np.arange(1,4)
  346.     heat_irr_minus_ppv_pred_for_net_heat = Heat_Irr_mat[day_index,:,sim_index]-\
  347.     Ppv_Pred_for_NetHeat
  348.  
  349.     Tcell_mat[:, sim_index] = predict_temperature(T_cell_coefs, denom_coefs,
  350.     heat_irr_minus_ppv_pred_for_net_heat, Net_Heat, Tamb_mat, Tcell_mat,
  351.     day_index, sim_index, prev_sim_idx)
  352.     Tglass_mat[:, sim_index] = predict_temperature(T_front_coefs, denom_coefs,
  353.     heat_irr_minus_ppv_pred_for_net_heat, Net_Heat, Tamb_mat, Tglass_mat,
  354.     day_index, sim_index, prev_sim_idx)
  355.     Tbacksheet_mat[:, sim_index] = predict_temperature(T_back_coefs, denom_coefs,
  356.     heat_irr_minus_ppv_pred_for_net_heat, Net_Heat, Tamb_mat, Tbacksheet_mat,
  357.     day_index, sim_index, prev_sim_idx)
  358.  
  359. def predict_temperature(coefs, coefs_denom, heat_irr_minus_ppv_pred_for_net_heat,
  360. Net_Heat, Tamb_mat, prev_temp, day_index, sim_index, prev_sim_idx):
  361.     delta_T_amb_prev = Tamb_mat[day_index, prev_sim_idx]-prev_temp[:, prev_sim_idx]
  362.     res = coefs[0]*heat_irr_minus_ppv_pred_for_net_heat+Tamb_mat[day_index, sim_index]
  363.     for i in range(0,3):
  364.         res = res+coefs[i+1]*Net_Heat[:, prev_sim_idx[i]]+coefs_denom[i]*delta_T_amb_prev[:,i]
  365.     return res
  366.  
  367. def sky_ground_temp_offsets(string_of_modules):
  368.  
  369.     tilt_angle = string_of_modules.tilt_angle
  370.     cell = string_of_modules.module.cell
  371.     #Sky temperature offset values, weighted by solid angle of sky.
  372.     #Ground is assumed to be at ambient temperature
  373.     offset_front = cell.sky_temp_horiz_surface_temp_diff*\
  374.     (1+np.cos(np.deg2rad(tilt_angle)))/2
  375.     offset_back = cell.sky_temp_horiz_surface_temp_diff*\
  376.     (1+np.cos(np.deg2rad(180-tilt_angle)))/2
  377.     return (offset_front, offset_back)
  378.  
  379. def dispatch_work_among_proc(climatic_data, sim_params, timestamp_index_min,
  380. timestamp_index_max, nbr_cells_in_basic_comp, string_of_modules,
  381. real_sim_nbr_bc_ratio, nbr_sim_modules, debug):
  382.     #dill.settings['recurse'] = True
  383.  
  384.     irradiance = climatic_data.global_irradiance
  385.     time_vector = climatic_data.time_vector
  386.  
  387.     assert(time_vector.shape[0] > 1)
  388.  
  389.     steps_accuracy = sim_params.steps_accuracy
  390.     first_day_to_simulate = sim_params.first_day_to_simulate
  391.     nbr_days_to_simulate = irradiance.shape[0]
  392.     nbr_basic_components = irradiance.shape[1]
  393.     nbr_timestamps_per_day = irradiance.shape[2]
  394.  
  395.     module = string_of_modules.module
  396.     cell = module.cell
  397.     cell_elec = cell.electrical_prop
  398.  
  399.     Iph_STC = cell_elec.I_photo_generated
  400.     Vdiode_BC = comp_Vdiode_BC(cell_elec, sim_params, Iph_STC,
  401.     nbr_cells_in_basic_comp, nbr_basic_components)
  402.  
  403.     #Total heat as a function of irradiation
  404.     Heat_STC = cell.heat_generated_at_standard_cond
  405.     Heat_Irr_mat = Heat_STC*irradiance/1000
  406.     #Iph as a function of irradiation (no thermal dependency assumed)
  407.     Iph_mat = Iph_STC*irradiance/1000
  408.     sky_temp_offset, ground_temp_offset = sky_ground_temp_offsets(
  409.     string_of_modules)
  410.  
  411.     # Sampling time for temperature prediction = sampling time of input
  412.     time_step = time_vector[1]-time_vector[0]
  413.     #thermal_predictor = temperature_predictor_variable_order(time_step,
  414.     #cell.thermal_prop)
  415.     thermal_predictor = \
  416.     cell.thermal_prop.temperature_predictor_variable_order(time_step)
  417.  
  418.     day_indices = list(np.arange(first_day_to_simulate-1,
  419.     first_day_to_simulate+nbr_days_to_simulate-1))
  420.  
  421.     nbr_cores =  sim_params.nbr_of_cores
  422.     if nbr_cores is None:
  423.         nbr_cores = min(multiprocess.cpu_count(), len(day_indices))
  424.  
  425.     #print("Spawning "+str(nbr_cores)+" processes for energy yield computation.")
  426.  
  427.     R_cabling_tot = string_of_modules.R_cabling_per_module*nbr_sim_modules
  428.  
  429.     Rs = cell_elec.R_series
  430.     R_in, R_out = Rs*nbr_cells_in_basic_comp*nbr_basic_components/2+R_cabling_tot
  431.  
  432.     #Useful when multiprocessing done at a "upper level", because
  433.     #daemonic processes are not allowed to have children (even one.)
  434.     if nbr_cores == 1:
  435.         results = list(map(functools.partial(
  436.         energy_yield_for_one_day, climatic_data, string_of_modules,
  437.         timestamp_index_min, timestamp_index_max, steps_accuracy,
  438.         nbr_cells_in_basic_comp, sky_temp_offset, ground_temp_offset, time_step,
  439.         thermal_predictor, Heat_Irr_mat, Iph_mat, Vdiode_BC, R_in, R_out, debug),
  440.         day_indices))
  441.     else:
  442.         proc_pool = Pool(nbr_cores)
  443.         results = list(proc_pool.map(functools.partial(
  444.         energy_yield_for_one_day, climatic_data, string_of_modules,
  445.         timestamp_index_min, timestamp_index_max, steps_accuracy,
  446.         nbr_cells_in_basic_comp, sky_temp_offset, ground_temp_offset, time_step,
  447.         thermal_predictor, Heat_Irr_mat, Iph_mat, Vdiode_BC, R_in, R_out, debug),
  448.         day_indices))
  449.         proc_pool.close()
  450.  
  451.     Pmpp_system = np.zeros((nbr_days_to_simulate, nbr_timestamps_per_day))
  452.     Vmpp_system = np.zeros((nbr_days_to_simulate, nbr_timestamps_per_day))
  453.     Pmpp_BC = np.zeros(irradiance.shape)
  454.     T_cell = np.zeros(irradiance.shape)
  455.     Net_Heat = np.zeros(irradiance.shape)
  456.  
  457.     for day_index in day_indices:
  458.         day_res = results[day_index]
  459.         Pmpp_system[day_index] = day_res[0]
  460.         Vmpp_system[day_index] = day_res[1]
  461.         Pmpp_BC[day_index] = day_res[2]
  462.         T_cell[day_index] = day_res[3]
  463.         Net_Heat[day_index] = day_res[4]
  464.  
  465.     Pmpp_system = Pmpp_system*real_sim_nbr_bc_ratio
  466.     Vmpp_system = Vmpp_system*real_sim_nbr_bc_ratio
  467.  
  468.     wpeak_watt = comp_system_wpeak(cell, Vdiode_BC,
  469.     climatic_data.basic_component, nbr_cells_in_basic_comp,
  470.     nbr_basic_components, real_sim_nbr_bc_ratio)
  471.  
  472.     if debug.debug_mode:
  473.         debug.write_debug_mat_file(["nbr_days_to_simulate", "nbr_basic_components",
  474.         "nbr_timestamps_per_day", "Iph_STC", "Vdiode_BC", "Heat_STC", "Heat_Irr_mat",
  475.         "Iph_mat", "sky_temp_offset", "ground_temp_offset", "time_step",
  476.         "day_indices", "R_cabling_tot", "Rs", "R_in", "R_out", "wpeak_watt"],
  477.         [nbr_days_to_simulate, nbr_basic_components, nbr_timestamps_per_day,
  478.         Iph_STC, Vdiode_BC, Heat_STC, Heat_Irr_mat, Iph_mat, sky_temp_offset,
  479.         ground_temp_offset, time_step, day_indices, R_cabling_tot, Rs, R_in,
  480.         R_out, wpeak_watt])
  481.  
  482.     Pmpp_system = Pmpp_system*2 if isinstance(cell, HalfCellTsec) else Pmpp_system
  483.  
  484.     return (Pmpp_system, Vmpp_system, Pmpp_BC, T_cell, wpeak_watt, Net_Heat,
  485.     Heat_Irr_mat)
  486.  
  487. #@numba.njit #slows down a bit
  488. def IV_series_connection(voltage, current):
  489.     """
  490.    Series connection of I-V curves.
  491.    N.B. When it comes to interpolation, numpy.interp is faster but cannot
  492.    extrapolate, leading to big error
  493.    """
  494.     nbr_of_basic_components = current.shape[0]
  495.     if nbr_of_basic_components == 1:
  496.         return (voltage, current.flatten())
  497.     else:
  498.         #Sort current by increasing order (row by row) and change positions of
  499.         #values in voltage accordingly
  500.         sort_indices = np.argsort(current, axis=1)
  501.         sort_current = np.take_along_axis(current, sort_indices, axis=1)
  502.         voltage_sort_by_current = np.take_along_axis(voltage, sort_indices,
  503.         axis=1)
  504.         flat_current = sort_current.flatten()
  505.         curr = np.unique(np.sort(flat_current))
  506.         volt = np.zeros((nbr_of_basic_components, curr.size))
  507.         #Initialize internal vector of interpolate here, because numba fails to
  508.         #np.zeros and because it allows to decreases costly call to malloc
  509.         #inside interpolate_row_by_row called many times.
  510.         bound_right_indices = np.zeros(curr.size).astype(int)
  511.  
  512.         # Old interpolation code using scipy (much slower)
  513.         # for i in range(voltage_sort_by_current.shape[0]):
  514.         #     itp_volt = interp1d(sort_current[i,:], voltage_sort_by_current[i,:],
  515.         #     kind='linear', fill_value="extrapolate", copy=False)
  516.         #     volt[i,:] = itp_volt(curr)
  517.  
  518.         interpolate_row_by_row(sort_current, voltage_sort_by_current, curr,
  519.         bound_right_indices, volt)
  520.         return (volt, curr)
  521.  
  522. # Parallel connection of I-V curves:
  523. def IV_parallel_connection(Voltage,Current):
  524.     if len(Voltage.shape)==1:
  525.         return(Voltage, sum(Current))
  526.     else:
  527.         volt = np.unique(np.sort(Voltage.flatten()))[::-1] # voltage vector for interpolation
  528.         curr = np.zeros([Voltage.shape[0],len(volt)])
  529.         for i in range(Voltage.shape[0]):
  530.             itp_curr = interp1d(Voltage[i][:],Current[i][:], kind='linear',
  531.             fill_value="extrapolate", copy=False) # create a scipy.interpolate.interp1d instance
  532.             curr[i,:] = itp_curr(volt) # filling current matrix by interpolation
  533.             #curr[i][:] = np.interp(volt,Voltage[i][:],Current[i][:])
  534.         return(volt, sum(curr))
  535.  
  536. # Find operating voltage given the operating current
  537. def find_operating_voltage(common_current, voltage_sort_by_current,
  538. sort_current):
  539.  
  540.     itp_volt = interp1d(Current, Voltage, kind='linear',
  541.     fill_value="extrapolate", copy=False) # create a scipy.interpolate.interp1d instance
  542.     return itp_volt(common_current)
  543.  
  544.     # interpolate_row_by_row(sort_current, voltage_sort_by_current,
  545.     # common_current, itp_volt)
  546.     # itp_volt = interp1d(Current, Voltage, kind='linear',
  547.     # fill_value="extrapolate", copy=False) # create a scipy.interpolate.interp1d instance
  548.     # return itp_volt(common_current)
  549.  
  550.     #return (np.interp(common_current,Current,Voltage))
  551.  
  552. # Find operating current given the operating voltage
  553. def find_operating_current(common_voltage,Voltage,Current):
  554.     itp_curr = interp1d(Voltage,Current, kind='linear',fill_value="extrapolate",
  555.     copy=False) # create a scipy.interpolate.interp1d instance
  556.     return (itp_curr(common_voltage))
  557.     #return (np.interp(common_voltage,Voltage,Current))
  558.  
  559. def diode_current(cell_elec, cell_temp, nbr_cells_in_basic_comp, Vdiode_BC):
  560.  
  561.     Rs = cell_elec.R_series
  562.     Rsh = cell_elec.R_shunt
  563.     C = cell_elec.saturation_cst
  564.     n = cell_elec.ideality_factor
  565.     k = Constants.ELECTRON_CHARGE_DIV_BY_BOLTZMANN_CONST
  566.     a = cell_elec.a
  567.     b = cell_elec.b
  568.  
  569.     return C*cell_temp**3*\
  570.     np.exp(-k*(1.1557-(a*cell_temp**2)/(cell_temp+b))/cell_temp)* \
  571.     (np.exp(k*Vdiode_BC/(n*nbr_cells_in_basic_comp*cell_temp))-1)
  572.  
  573. # I-V curve calculation
  574. def BC_IV(Iph, cell_temp, cell_elec, nbr_cells_in_basic_comp, Vdiode_BC):
  575.     Idiode = diode_current(cell_elec, cell_temp, nbr_cells_in_basic_comp, Vdiode_BC)
  576.     return (Iph-Idiode-Vdiode_BC/(cell_elec.R_shunt*nbr_cells_in_basic_comp)) # BC current
  577.  
  578. # Adding series resistance to any I-V curve
  579. def IV_real(Rin,Rout,Voltage,Current):
  580.     return (Voltage-(Rin+Rout)*Current) # Voltage of the real I-V curve
  581.  
  582. ## ###################################### AUXILIARY FUNCTIONS ##############################################################################################################################################################################################
  583. # Check if a variable is not empty
  584. def is_not_empty(any_structure):
  585.     return any_structure
  586.  
  587. # Check if a variable is empty
  588. def is_empty(any_structure):
  589.     return not is_not_empty(any_structure)
  590.  
  591. def comp_nbr_cells_in_basic_comp(basic_component_type, module):
  592.     """
  593.    A BASIC COMPONENT IS THE COMPONENT FOR WHICH WE GENERATE THE INPUT. IF INPUT
  594.    IS DEFINED PER CELL, CELL IS THE BASIC COMPONENT. IF INPUT IS DEFINED BY
  595.    MODULE, MODULE IS THE BASIC COMPONENT. A BASIC COMPONENT IN THIS VERSION OF
  596.    THE FRAMEWORK IS MADE OF num_cells SERIES-CONNECTED SOLAR CELLS
  597.    """
  598.     if basic_component_type == "cell":
  599.         return 1
  600.     elif basic_component_type == "module":
  601.         return module.number_of_cells
  602.     else:
  603.         raise NotImplementedError()
  604.  
  605. def comp_system_wpeak(cell, Vdiode_BC, basic_component, nbr_cells_in_basic_comp,
  606. nbr_basic_comp, real_sim_nbr_bc_ratio):
  607.  
  608.     cell_temp = 298.15
  609.     cell_elec = cell.electrical_prop
  610.     Rsh = cell_elec.R_shunt
  611.     Iph_STC = cell_elec.I_photo_generated
  612.     Rs = cell_elec.R_series
  613.  
  614.     Idiode = diode_current(cell_elec, cell_temp, nbr_cells_in_basic_comp, Vdiode_BC)
  615.     I_basic_comp = (Iph_STC-Idiode-Vdiode_BC/(Rsh*nbr_cells_in_basic_comp)) # BC current
  616.  
  617.     V_basic_comp = Vdiode_BC-Rs*nbr_cells_in_basic_comp*I_basic_comp
  618.     wpeak_watt_basic_comp = np.max(V_basic_comp*I_basic_comp)
  619.  
  620.     #if basic_component == "module":
  621.     wpeak_watt = wpeak_watt_basic_comp*nbr_basic_comp
  622.     if isinstance(cell, HalfCellTsec):
  623.         wpeak_watt = wpeak_watt*2
  624.  
  625.     return wpeak_watt*real_sim_nbr_bc_ratio
  626.  
  627. def time_interval_with_irr_gt_min(sim_params, irradiances):
  628.     """
  629.    For each day and for each basic component find the indices of the timestamps
  630.    that define the temporal interval during which the irradiance is superior to
  631.    a minimum irradiance.
  632.    """
  633.  
  634.     min_irradiance = sim_params.min_irradiance
  635.     first_day_to_simulate = sim_params.first_day_to_simulate
  636.     nbr_days_to_simulate = irradiances.shape[0]
  637.     nbr_basic_components = irradiances.shape[1]
  638.  
  639.     index_min = np.zeros(nbr_days_to_simulate)
  640.     index_max = np.zeros(nbr_days_to_simulate)
  641.     index_min_temp = np.zeros((nbr_days_to_simulate, nbr_basic_components))
  642.     index_max_temp = np.zeros((nbr_days_to_simulate, nbr_basic_components))
  643.  
  644.     for day_index in range(first_day_to_simulate-1,
  645.     first_day_to_simulate + nbr_days_to_simulate-1):
  646.         for BC_index in range(0, nbr_basic_components):
  647.  
  648.             buffer_condition = np.where(
  649.             irradiances[day_index][BC_index][:] > min_irradiance)
  650.  
  651.             if buffer_condition[0].size > 0:
  652.                 index_min_temp[day_index][BC_index] = max([3, buffer_condition[0][0]])
  653.                 index_max_temp[day_index][BC_index] = buffer_condition[0][-1]
  654.             else:
  655.                 index_min_temp[day_index][BC_index] = 10
  656.                 index_max_temp[day_index][BC_index] = 10
  657.  
  658.         index_min[day_index] = min(index_min_temp[day_index][:])
  659.         index_max[day_index] = max(index_max_temp[day_index][:])
  660.  
  661.     return (index_min, index_max)
  662.  
  663. def comp_real_sim_nbr_bc_ratio(string_of_modules, climatic_data):
  664.     """
  665.    Compute ratio between simulated number of basic components and
  666.    the number of basic components in the real solar system.
  667.    """
  668.  
  669.     basic_component = climatic_data.basic_component
  670.     nbr_basic_comp = climatic_data.nbr_of_components
  671.  
  672.     err_msg = "energy_yield.py, real_sim_nbr_bc_ratio: Number of \
  673.    basic components "+str(nbr_basic_comp)+" does not match \
  674.    string_of_modules.number_of_modules "+\
  675.     str(string_of_modules.number_of_modules)
  676.  
  677.     if basic_component == "module":
  678.         if string_of_modules.number_of_modules == nbr_basic_comp:
  679.             return 1
  680.         elif nbr_basic_comp == 1:
  681.             return string_of_modules.number_of_modules
  682.         else:
  683.             raise Exception(err_msg)
  684.     elif basic_component == "cell":
  685.         total_syst_cell_nbr = string_of_modules.number_of_modules*\
  686.         string_of_modules.module.number_of_cells
  687.         if nbr_basic_comp == total_syst_cell_nbr:
  688.             return 1
  689.         elif nbr_basic_comp == string_of_modules.module.number_of_cells:
  690.             return string_of_modules.number_of_modules
  691.         elif nbr_basic_comp == 1:
  692.             return total_syst_cell_nbr
  693.         else:
  694.             raise Exception(err_msg)
  695.  
  696. def comp_nbr_simulated_modules(climatic_data, module):
  697.     """
  698.    Compute the number of simulated modules, which may not be equal to the
  699.    number of modules of the real system (string_of_modules.number_of_modules).
  700.    """
  701.     if climatic_data.basic_component == "module":
  702.         return climatic_data.nbr_of_components
  703.     elif climatic_data.basic_component == "cell":
  704.         return climatic_data.nbr_of_components//module.number_of_cells
  705.     else:
  706.         raise NotImplementedError
  707.  
  708. def validate_inputs(climatic_data, string_of_modules):
  709.     """
  710.    Several supported cases:
  711.    1) We have one set of climatic data ("for 1 module"), we make the
  712.    thermo-electrical simulation for 1 module:
  713.    a) We want output power for 1 module (climatic_data.nbr_of_components
  714.    == string_of_modules.number_of_modules).
  715.    b) We want output power for several modules (climatic_data.nbr_of_components
  716.    > string_of_modules.number_of_modules).
  717.  
  718.    2) We have one set of climatic data for N modules, we make the
  719.    thermo-electrical simulation for N modules. climatic_data.nbr_of_components
  720.    == string_of_modules.number_of_modules
  721.  
  722.    3) We have one set of climatic data per cell, for N cells of 1 module. That
  723.    is N = module.number_of_cells.
  724.    We make the thermo-electrical simulation for N cells.
  725.    a) We want output power for 1 module. (climatic_data.nbr_of_components == N,
  726.    == string_of_modules.module.number_of_cells,
  727.    string_of_modules.number_of_modules == 1)
  728.    b) We want output power for M modules (climatic_data.nbr_of_components == N,
  729.    == string_of_modules.module.number_of_cells,
  730.    string_of_modules.number_of_modules == M).
  731.  
  732.    4) We have one set of climatic data per cell, for N*M cells, where
  733.    is N = module.number_of_cells and M = string_of_modules.number_of_modules.
  734.    Then climatic_data.nbr_of_components == N*M.
  735.    """
  736.  
  737.     assert(climatic_data.basic_component == "module" or \
  738.     climatic_data.basic_component == "cell")
  739.  
  740.     if climatic_data.basic_component == "module":
  741.         assert(climatic_data.nbr_of_components == 1 or \
  742.         climatic_data.nbr_of_components == string_of_modules.number_of_modules)
  743.     else:
  744.         assert(climatic_data.nbr_of_components == 1 or \
  745.         climatic_data.nbr_of_components == string_of_modules.number_of_modules*\
  746.         string_of_modules.module.number_of_cells or \
  747.         climatic_data.nbr_of_components == \
  748.         string_of_modules.module.number_of_cells)
  749.  
  750.  
  751. def compute_energy_yield(climatic_data, string_of_modules, output_data_path=None,
  752. sim_params=None, debug=False):
  753.  
  754.     validate_inputs(climatic_data, string_of_modules)
  755.  
  756.     if sim_params is None:
  757.         sim_params = EnergyYieldSimParams()
  758.  
  759.     module = string_of_modules.module
  760.     nbr_cells_in_basic_comp = comp_nbr_cells_in_basic_comp(
  761.     climatic_data.basic_component, module)
  762.     debug = sim_params.debug
  763.     timestamp_index_min, timestamp_index_max = \
  764.     time_interval_with_irr_gt_min(sim_params, climatic_data.global_irradiance)
  765.  
  766.     real_sim_nbr_bc_ratio = comp_real_sim_nbr_bc_ratio(string_of_modules,
  767.     climatic_data)
  768.     nbr_sim_modules = comp_nbr_simulated_modules(climatic_data, module)
  769.  
  770.     if debug.debug_mode:
  771.         debug.write_debug_mat_file(["nbr_cells_in_basic_comp", "timestamp_index_min",
  772.         "timestamp_index_max", "real_sim_nbr_bc_ratio", "nbr_sim_modules"],
  773.         [nbr_cells_in_basic_comp, timestamp_index_min,
  774.         timestamp_index_max, real_sim_nbr_bc_ratio, nbr_sim_modules])
  775.  
  776.     time_before_sim = time.time()
  777.     result = dispatch_work_among_proc(climatic_data, sim_params,
  778.     timestamp_index_min, timestamp_index_max, nbr_cells_in_basic_comp,
  779.     string_of_modules, real_sim_nbr_bc_ratio, nbr_sim_modules, debug)
  780.     simulation_time = time.time() - time_before_sim
  781.  
  782.     Pmpp_system, Vmpp_system, Pmpp_BC, temp_BC, wpeak_watt, net_heat, \
  783.     heat_irr = result
  784.  
  785.     return EnergyYieldRes(Pmpp_system, Vmpp_system, Pmpp_BC, temp_BC,
  786.     net_heat, heat_irr, simulation_time, wpeak_watt, climatic_data.time_vector)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement