def _get_custom_spectrum(self, options): """ Convert an custom spectrum into a solcore light source object. :param options: A dictionary that contains the following information: - 'x_data' and 'y_data' of the custom spectrum. - 'input_units', the units of the spectrum, such as 'photon_flux_per_nm' or 'power_density_per_eV' :return: A function that takes as input the wavelengths and return the custom spectrum at those wavelengths. """ try: x_data = options['x_data'] y_data = options['y_data'] units = options['input_units'] # We check the units type by type. # Regardless of the input, we want power_density_per_nm if units == 'power_density_per_nm': wl = x_data spectrum = y_data elif units == 'photon_flux_per_nm': wl = x_data spectrum = y_data * (c * h * 1e+9 / wl) elif units == 'power_density_per_eV': wl, spectrum = spectral_conversion_nm_ev(x_data, y_data) elif units == 'photon_flux_per_eV': wl, spectrum = spectral_conversion_nm_ev(x_data, y_data) spectrum = spectrum * (c * h * 1e+9 / wl) elif units == 'power_density_per_J': wl, spectrum = spectral_conversion_nm_ev( x_data / q, y_data * q) elif units == 'photon_flux_per_J': wl, spectrum = spectral_conversion_nm_ev( x_data / q, y_data * q) spectrum = spectrum * (c * h * 1e+9 / wl) elif units == 'power_density_per_hz': wl, spectrum = spectral_conversion_nm_hz(x_data, y_data) elif units == 'photon_flux_per_hz': wl, spectrum = spectral_conversion_nm_hz(x_data, y_data) spectrum = spectrum * (h * x_data) else: raise ValueError( 'Unknown units: {0}.\nValid units are: {1}.'.format( units, self.output_units)) self.x_internal = wl self.power_density = np.trapz(y=spectrum, x=wl) * self.options['concentration'] output = interp1d(x=wl, y=spectrum, bounds_error=False, fill_value=0, assume_sorted=True) return output except KeyError as err: print(err)
def test_power_density_per_ev(wavelength, gauss_spectrum): from solcore.light_source.light_source import power_density_per_ev from solcore import spectral_conversion_nm_ev sp, sp_fun = gauss_spectrum ev, expected = spectral_conversion_nm_ev(wavelength, sp) actual = power_density_per_ev(sp_fun, ev) assert actual == approx(expected)
def _get_power_density_per_eV(self, energy): """ Function that returns the spectrum in power density per eV. :param energy: Array with the energies at which to calculate the spectrum (in eV) :return: The spectrum in the chosen units. """ wavelength = eVnm(energy)[::-1] output = self._spectrum(wavelength) energy_eV, output = spectral_conversion_nm_ev(wavelength, output) return output
def test_photon_flux_per_joule(wavelength, gauss_spectrum): from solcore.light_source.light_source import photon_flux_per_joule from solcore import spectral_conversion_nm_ev from solcore.constants import q sp, sp_fun = gauss_spectrum ev, expected = spectral_conversion_nm_ev(wavelength, sp) actual = photon_flux_per_joule(sp_fun, ev * q) assert actual * q**2 * ev == approx(expected)
def _get_photon_flux_per_J(self, energy): """ Function that returns the spectrum in photon flux per Joule. :param energy: Array with the energies at which to calculate the spectrum (in J) :return: The spectrum in the chosen units. """ wavelength = nmJ(energy)[::-1] output = self._spectrum(wavelength) energy_eV, output = spectral_conversion_nm_ev(wavelength, output) output = output / (q * energy) return output
def photon_flux_per_joule(spectrum: Callable[[np.ndarray], np.ndarray], x: np.ndarray): """ Function that returns the spectrum in photon flux per Joule. The input spectrum is assumed to be in power density per nanometer. :param spectrum: The spectrum to interpolate. :param x: Array with the energies (in J) :return: The spectrum in the chosen units. """ wavelength = nmJ(x)[::-1] output = spectrum(wavelength) _, output = spectral_conversion_nm_ev(wavelength, output) return output / (q * x)
def power_density_per_ev(spectrum: Callable[[np.ndarray], np.ndarray], x: np.ndarray): """ Function that returns the spectrum in power density per eV. The input spectrum is assumed to be in power density per nanometer. :param spectrum: The spectrum to interpolate. :param x: Array with the energies (in eV) :return: The spectrum in the chosen units. """ wavelength = eVnm(x)[::-1] output = spectrum(wavelength) _, output = spectral_conversion_nm_ev(wavelength, output) return output
def calculate_spectrum_spectral2(stateObject=None, suppress_nan=True, power_density_in_nm=False): """ Calculates a solar spectrum using the SPECTRAL2 irradiance model developed by the NRL: "http://rredc.nrel.gov/solar/models/spectral/SPCTRAL2/" :param stateObject: :param suppress_nan: :return: """ science_reference("spectral2 irradiance model", "http://rredc.nrel.gov/solar/models/spectral/SPCTRAL2/") if stateObject == None: stateObject = get_default_spectral2_object() latitude = stateObject["latitude"] longitude = stateObject["longitude"] dateAndTime = stateObject["dateAndTime"] aod_model = stateObject["aod_model"] pressure = stateObject["pressure"] humidity = stateObject["humidity"] ozone = stateObject["ozone"] turbidity = stateObject["turbidity"] precipwater = stateObject["precipwater"] assert aod_model in "rural urban maritime tropospheric".split( ), "aod_model must be rural, urban, maritime, or tropospheric" time_since_new_years = dateAndTime - datetime(dateAndTime.year, 1, 1, 0, 0) # 1/1/year, 0:00 am. time_since_midnight = dateAndTime - datetime( dateAndTime.year, dateAndTime.month, dateAndTime.day, 0, 0) # hours_since_midnight = time_since_midnight.seconds / convert(time_since_midnight.seconds, "s", "h") hours_since_midnight = time_since_midnight.seconds / 3600 day_number = time_since_new_years.days # converting back go degrees for longitude rounding longitude_degrees = longitude / numpy.pi * 180 # convert(longitude,"radians",u"degrees") day_angle = (2.0 * pi * (day_number - 1.0)) / 365.0 # this is in radians hour_angle_degrees = 15.0 * ( hours_since_midnight + equation_of_time(day_angle) / 60.0 + ((int(longitude_degrees / 15.0) * 15.0 - longitude_degrees) * 4.0) / 60.0 + 12.0) - 360.0 # this is in degrees. hour_angle = hour_angle_degrees / 180 * numpy.pi # convert(hour_angle_degrees, "degrees", "radians") declination = (0.006918 - 0.399912 * cos(day_angle) + 0.070257 * sin(day_angle) - 0.006758 * cos(2 * day_angle) + 0.000907 * sin(2 * day_angle) - 0.002697 * cos(3 * day_angle) + 0.00148 * sin(3 * day_angle)) earth_sun_distance_factor = (1.00011 + 0.034221 * cos(day_angle) + 0.001280 * sin(day_angle) + 0.000719 * cos(2 * day_angle) + 0.000077 * sin(2 * day_angle)) solar_zenith_angle = arccos( cos(declination) * cos(latitude) * cos(hour_angle) + sin(declination) * sin(latitude)) solar_zenith_angle_degrees = solar_zenith_angle / pi * 180 # convert(solar_zenith_angle,"radians",u'degrees') # original code checked to stop sun dropping below horizon # //Stop sun dropping below horizon # if(solar_zenith_angle > 91) # solar_zenith_angle = 91; (was in degrees) # this used to be 1/ could this cause issues in java? relative_am = 1.0 / (cos(solar_zenith_angle) + (0.15 * pow( (93.885 - solar_zenith_angle_degrees), -1.253)) ) ##AARG raised to the power of degrees pressure_corrected_am = relative_am * (pressure / 101325.33538686013 ) # si("1 atm") effective_ozone_am = (1.0 + (22.0 / 6370.0)) / (pow( (pow(cos(solar_zenith_angle), 2.0) + (2.0 * (22.0 / 6370.0))), 0.5)) if aod_model == "rural": c_coefficient = [0.581, 16.823, 17.539] d_coefficient = [0.8547, 78.696, 0, 64.458] elif aod_model == "rural": c_coefficient = [0.2595, 33.843, 39.524] d_coefficient = [1.0, 84.254, -9.1, 65.458] elif aod_model == "maritime": c_coefficient = [0.1134, 0.8941, 1.0796] d_coefficient = [0.04435, 1.6048, 0, 1.5298] elif aod_model == "tropospheric": c_coefficient = [0.6786, 13.899, 13.313] d_coefficient = [1.8379, 14.912, 0, 5.96] elif type(aod_model) == list: c_coefficient, d_coefficient = aod_model # 0.9 degrees * something_in_percent rather than 90 degrees?? Honestly. x_humidity = cos(humidity * pi / 2.) # si(0.9,"degrees","radians") alpha1 = (c_coefficient[0] + c_coefficient[1] * x_humidity) / ( 1 + c_coefficient[2] * x_humidity) alpha2 = (d_coefficient[0] + d_coefficient[1] * x_humidity + d_coefficient[2] * x_humidity**2) / ( 1 + (d_coefficient[3] * x_humidity)) wavelength_um = am_zero_wavelength * 1e6 # convert(am_zero_wavelength, "m", 'um') rayleigh_coeff = exp(-pressure_corrected_am / (wavelength_um**4 * (115.6406 - 1.335 / wavelength_um**2))) aerosol_coeff = where( wavelength_um <= 0.5, exp(-pressure_corrected_am * turbidity * 2.0**(alpha2 - alpha1) * wavelength_um**-alpha1), exp(-pressure_corrected_am * turbidity * wavelength_um**-alpha2)) vapour_coeff = exp( -(0.2385 * precipwater * waterspectra * relative_am) / (1. + 20.07 * precipwater * waterspectra * relative_am)**0.45) ozone_coeff = exp(-ozonespectra * ozone * effective_ozone_am) mixed_coeff = exp( (-1.41 * uniformgasspectra * pressure_corrected_am) / (1. + 118.93 * uniformgasspectra * pressure_corrected_am)**0.45) # Apply attenuation/scatting coefficients to the per m specturm (SI wavelength spectrum) wavelength_m = am_zero_wavelength irradiance_per_m = am_zero_irradiance * earth_sun_distance_factor * rayleigh_coeff * aerosol_coeff * vapour_coeff * ozone_coeff * mixed_coeff if suppress_nan: irradiance_per_m = numpy.nan_to_num(irradiance_per_m) storage = dict() # Convert to per nm spectrum wavelength_nm = wavelength_m * 1e9 # asUnit(wavelength_m,"nm") irradiance_per_nm = 1e-9 * irradiance_per_m # asUnit(irradiance_per_m,"nm-1") if power_density_in_nm: storage = wavelength_nm, irradiance_per_nm else: # Convert to per eV spectrum energy_ev, irradiance_per_ev = spectral_conversion_nm_ev( wavelength_nm, irradiance_per_nm) # Covert to energy per Joule spectrum energy_j = energy_ev * 1.6e-19 # siUnits(energy_ev, 'J') irradiance_per_j = irradiance_per_ev / 1.6e-19 # siUnits(irradiance_per_ev, 'J-1') # integrate to find power density power_density = trapz(x=wavelength_m, y=irradiance_per_m) storage['incident power density'] = power_density storage['incident spectrum wavelength si'] = array( [wavelength_m, irradiance_per_m]) storage['incident spectrum wavelength nm'] = array( [wavelength_nm, irradiance_per_nm]) storage['incident spectrum energy si'] = array( [energy_j, irradiance_per_j]) storage['incident spectrum energy eV'] = array( [energy_ev, irradiance_per_ev]) return storage