def calculate_absorption_profile(structure, wavelength, z_limit=None, steps_size=2, dist=None, no_back_reflexion=True): """ It calculates the absorbed energy density within the material. From the documentation: 'In principle this has units of [power]/[volume], but we can express it as a multiple of incoming light power density on the material, which has units [power]/[area], so that absorbed energy density has units of 1/[length].' Integrating this absorption profile in the whole stack gives the same result that the absorption obtained with calculate_rat as long as the spacial mesh (controlled by steps_thinest_layer) is fine enough. If the structure is very thick and the mesh not thin enough, the calculation might diverege at short wavelengths. For now, it only works for normal incident, coherent light. :param structure: A solcore structure with layers and materials. :param wavelength: Wavelengths in which calculate the data (in nm). An array-like object. :param z_limit: Maximum value in the z direction :return: A dictionary containing the positions (in nm) and a 2D array with the absorption in the structure as a function of the position and the wavelength. """ num_wl = len(wavelength) if 'OptiStack' in str(type(structure)): stack = structure stack.no_back_reflexion = no_back_reflexion else: stack = OptiStack(structure, no_back_reflexion=no_back_reflexion) if dist is None: if z_limit is None: z_limit = np.sum(np.array(stack.widths)) dist = np.arange(0, z_limit, steps_size) output = {'position': dist, 'absorption': np.zeros((num_wl, len(dist)))} # print(stack.get_indices(wavelength).shape) out1 = tmm.coh_tmm('s', stack.get_indices(wavelength), stack.get_widths(), 0, wavelength) layer, d_in_layer = tmm.find_in_structure_with_inf(stack.get_widths(), dist) data = tmm.position_resolved(layer, d_in_layer, out1) output['absorption'] = data['absor'] return output
def calculate_rat(structure, wavelength, angle=0, pol='u', coherent=True, coherency_list=None, no_back_reflection=True, **kwargs): """ Calculates the reflected, absorbed and transmitted intensity of the structure for the wavelengths and angles defined. :param structure: A solcore Structure object with layers and materials or a OptiStack object. :param wavelength: Wavelengths (in nm) in which calculate the data. An array. :param angle: Angle (in degrees) of the incident light. Default: 0 (normal incidence). :param pol: Polarisation of the light: 's', 'p' or 'u'. Default: 'u' (unpolarised). :param coherent: If the light is coherent or not. If not, a coherency list must be added. :param coherency_list: A list indicating in which layers light should be treated as coeherent ('c') and in which incoherent ('i'). It needs as many elements as layers in the structure. :param no_back_reflection: If reflexion from the back must be supressed. Default=True. :return: A dictionary with the R, A and T at the specified wavelengths and angle. """ num_wl = len(wavelength) if 'no_back_reflexion' in kwargs: warn( 'The no_back_reflexion argument is deprecated. Use no_back_reflection instead.', FutureWarning) no_back_reflection = kwargs['no_back_reflexion'] if 'OptiStack' in str(type(structure)): stack = structure stack.no_back_reflection = no_back_reflection else: stack = OptiStack(structure, no_back_reflection=no_back_reflection) if not coherent: if coherency_list is not None: assert len(coherency_list) == stack.num_layers, \ 'Error: The coherency list must have as many elements (now {}) as the ' \ 'number of layers (now {}).'.format(len(coherency_list), stack.num_layers) if stack.no_back_reflection: coherency_list = ['i'] + coherency_list + ['i', 'i'] else: coherency_list = ['i'] + coherency_list + ['i'] else: raise Exception( 'Error: For incoherent or partly incoherent calculations you must supply the ' 'coherency_list parameter with as many elements as the number of layers in the ' 'structure') output = { 'R': np.zeros(num_wl), 'A': np.zeros(num_wl), 'T': np.zeros(num_wl), 'all_p': [], 'all_s': [], 'out': [], 'out_s': [], 'out_p': [], 'A_per_layer_s': [], 'A_per_layer_p': [] } if pol in 'sp': if coherent: out = tmm.coh_tmm(pol, stack.get_indices(wavelength), stack.get_widths(), angle * degree, wavelength) A_per_layer = tmm.absorp_in_each_layer(out) output['R'] = out['R'] output['A'] = 1 - out['R'] - out['T'] output['T'] = out['T'] output['A_per_layer'] = A_per_layer output['out'] = out else: out = tmm.inc_tmm(pol, stack.get_indices(wavelength), stack.get_widths(), coherency_list, angle * degree, wavelength) A_per_layer = np.array(tmm.inc_absorp_in_each_layer(out)) output['R'] = out['R'] output['A'] = 1 - out['R'] - out['T'] output['T'] = out['T'] output['A_per_layer'] = A_per_layer output['out'] = out else: if coherent: out_p = tmm.coh_tmm('p', stack.get_indices(wavelength), stack.get_widths(), angle * degree, wavelength) out_s = tmm.coh_tmm('s', stack.get_indices(wavelength), stack.get_widths(), angle * degree, wavelength) A_per_layer_p = tmm.absorp_in_each_layer(out_p) A_per_layer_s = tmm.absorp_in_each_layer(out_s) output['R'] = 0.5 * (out_p['R'] + out_s['R']) output['T'] = 0.5 * (out_p['T'] + out_s['T']) output['A'] = 1 - output['R'] - output['T'] output['A_per_layer'] = 0.5 * (A_per_layer_p + A_per_layer_s) output['out_s'] = out_s output['out_p'] = out_p output['A_per_layer_s'] = A_per_layer_s output['A_per_layer_p'] = A_per_layer_p else: out_p = tmm.inc_tmm('p', stack.get_indices(wavelength), stack.get_widths(), coherency_list, angle * degree, wavelength) out_s = tmm.inc_tmm('s', stack.get_indices(wavelength), stack.get_widths(), coherency_list, angle * degree, wavelength) A_per_layer_p = np.array(tmm.inc_absorp_in_each_layer(out_p)) A_per_layer_s = np.array(tmm.inc_absorp_in_each_layer(out_s)) output['R'] = 0.5 * (out_p['R'] + out_s['R']) output['T'] = 0.5 * (out_p['T'] + out_s['T']) output['A'] = 1 - output['R'] - output['T'] output['all_p'] = out_p['power_entering_list'] output['all_s'] = out_s['power_entering_list'] output['A_per_layer'] = 0.5 * (A_per_layer_p + A_per_layer_s) output['out_s'] = out_s output['out_p'] = out_p output['A_per_layer_s'] = A_per_layer_s output['A_per_layer_p'] = A_per_layer_p return output
def calculate_absorption_profile(structure, wavelength, z_limit=None, steps_size=2, dist=None, no_back_reflexion=True, angle=0, pol='u', coherent=True, coherency_list=None): """ It calculates the absorbed energy density within the material. From the documentation: 'In principle this has units of [power]/[volume], but we can express it as a multiple of incoming light power density on the material, which has units [power]/[area], so that absorbed energy density has units of 1/[length].' Integrating this absorption profile in the whole stack gives the same result that the absorption obtained with calculate_rat as long as the spacial mesh (controlled by steps_thinest_layer) is fine enough. If the structure is very thick and the mesh not thin enough, the calculation might diverge at short wavelengths. :param structure: A solcore structure with layers and materials. :param wavelength: Wavelengths in which calculate the data (in nm). An array :param z_limit: Maximum value in the z direction :param steps_size: if the dist is not specified, the step size in nm to use in the depth-dependent calculation :param dist: the positions (in nm) at which to calculate depth-dependent absorption :param no_back_reflexion: whether to suppress reflections from the back interface (True) or not (False) :param angle: incidence angle in degrees :param pol: polarization of incident light: 's', 'p' or 'u' (unpolarized) :param coherent: True if all the layers are to be treated coherently, False otherwise :param coherency_list: if coherent is False, a list of 'c' (coherent) or 'i' (incoherent) for each layer :return: A dictionary containing the positions (in nm) and a 2D array with the absorption in the structure as a function of the position and the wavelength. """ num_wl = len(wavelength) if 'OptiStack' in str(type(structure)): stack = structure stack.no_back_reflexion = no_back_reflexion else: stack = OptiStack(structure, no_back_reflexion=no_back_reflexion) if dist is None: if z_limit is None: z_limit = np.sum(np.array(stack.widths)) dist = np.arange(0, z_limit, steps_size) if not coherent: if coherency_list is not None: if stack.no_back_reflexion: coherency_list = ['i'] + coherency_list + ['i', 'i'] else: coherency_list = ['i'] + coherency_list + ['i'] else: raise Exception( 'Error: For incoherent or partly incoherent calculations you must supply the ' 'coherency_list parameter with as many elements as the number of layers in the ' 'structure') output = {'position': dist, 'absorption': np.zeros((num_wl, len(dist)))} if pol in 'sp': # print(stack.get_indices(wavelength).shape) if coherent: out = tmm.coh_tmm(pol, stack.get_indices(wavelength), stack.get_widths(), angle * degree, wavelength) layer, d_in_layer = tmm.find_in_structure_with_inf( stack.get_widths(), dist) data = tmm.position_resolved(layer, d_in_layer, out) output['absorption'] = data['absor'] else: out = tmm.inc_tmm(pol, stack.get_indices(wavelength), stack.get_widths(), coherency_list, angle * degree, wavelength) layer, d_in_layer = tmm.find_in_structure_with_inf( stack.get_widths(), dist) data = tmm.inc_position_resolved( layer, d_in_layer, out, coherency_list, 4 * np.pi * np.imag(stack.get_indices(wavelength)) / wavelength) output['absorption'] = data else: if coherent: out1 = tmm.coh_tmm('s', stack.get_indices(wavelength), stack.get_widths(), angle * degree, wavelength) out2 = tmm.coh_tmm('p', stack.get_indices(wavelength), stack.get_widths(), angle * degree, wavelength) layer, d_in_layer = tmm.find_in_structure_with_inf( stack.get_widths(), dist) data_s = tmm.position_resolved(layer, d_in_layer, out1) data_p = tmm.position_resolved(layer, d_in_layer, out2) output['absorption'] = 0.5 * (data_s['absor'] + data_p['absor']) else: out1 = tmm.inc_tmm('s', stack.get_indices(wavelength), stack.get_widths(), coherency_list, angle * degree, wavelength) out2 = tmm.inc_tmm('p', stack.get_indices(wavelength), stack.get_widths(), coherency_list, angle * degree, wavelength) layer, d_in_layer = tmm.find_in_structure_with_inf( stack.get_widths(), dist) data_s = tmm.inc_position_resolved( layer, d_in_layer, out1, coherency_list, 4 * np.pi * np.imag(stack.get_indices(wavelength)) / wavelength) data_p = tmm.inc_position_resolved( layer, d_in_layer, out2, coherency_list, 4 * np.pi * np.imag(stack.get_indices(wavelength)) / wavelength) output['absorption'] = 0.5 * (data_s + data_p) return output
def calculate(self, wavelength, angle=0, pol='u', profile=False, layers=None, nm_spacing = 1): """ Calculates the reflected, absorbed and transmitted intensity of the structure for the wavelengths and angles defined. :param stack: an OptiStack object. :param wavelength: Wavelengths (in nm) in which calculate the data. An array. :param angle: Angle (in radians) of the incident light. Default: 0 (normal incidence). :param pol: Polarisation of the light: 's', 'p' or 'u'. Default: 'u' (unpolarised). :param coherent: If the light is coherent or not. If not, a coherency list must be added. :param coherency_list: A list indicating in which layers light should be treated as coeherent ('c') and in which incoherent ('i'). It needs as many elements as layers in the structure. :param profile: whether or not to calculate the absorption profile :param layers: indices of the layers in which to calculate the absorption profile. Layer 0 is the incidence medium. :return: A dictionary with the R, A and T at the specified wavelengths and angle. """ stack = self.stack coherency_list = self.coherency_list coherent = self.coherent num_wl = len(wavelength) output = {'R': np.zeros(num_wl), 'A': np.zeros(num_wl), 'T': np.zeros(num_wl), 'all_p': [], 'all_s': []} if pol in 'sp': if coherent: out = tmm.coh_tmm(pol, stack.get_indices(wavelength), stack.get_widths(), angle, wavelength) A_per_layer = tmm.absorp_in_each_layer(out) output['R'] = out['R'] output['A'] = 1 - out['R'] - out['T'] output['T'] = out['T'] output['A_per_layer'] = A_per_layer[1:-1] else: out = tmm.inc_tmm(pol, stack.get_indices(wavelength), stack.get_widths(), coherency_list, angle, wavelength) A_per_layer = np.array(tmm.inc_absorp_in_each_layer(out)) output['R'] = out['R'] output['A'] = 1 - out['R'] - out['T'] output['T'] = out['T'] output['A_per_layer'] = A_per_layer[1:-1] else: if coherent: out_p = tmm.coh_tmm('p', stack.get_indices(wavelength), stack.get_widths(), angle, wavelength) out_s = tmm.coh_tmm('s', stack.get_indices(wavelength), stack.get_widths(), angle, wavelength) A_per_layer_p = tmm.absorp_in_each_layer(out_p) A_per_layer_s = tmm.absorp_in_each_layer(out_s) output['R'] = 0.5 * (out_p['R'] + out_s['R']) output['T'] = 0.5 * (out_p['T'] + out_s['T']) output['A'] = 1 - output['R'] - output['T'] output['A_per_layer'] = 0.5*(A_per_layer_p[1:-1] + A_per_layer_s[1:-1]) else: out_p = tmm.inc_tmm('p', stack.get_indices(wavelength), stack.get_widths(), coherency_list, angle, wavelength) out_s = tmm.inc_tmm('s', stack.get_indices(wavelength), stack.get_widths(), coherency_list, angle, wavelength) A_per_layer_p = np.array(tmm.inc_absorp_in_each_layer(out_p)) A_per_layer_s = np.array(tmm.inc_absorp_in_each_layer(out_s)) output['R'] = 0.5 * (out_p['R'] + out_s['R']) output['T'] = 0.5 * (out_p['T'] + out_s['T']) output['A'] = 1 - output['R'] - output['T'] output['all_p'] = out_p['power_entering_list'] output['all_s'] = out_s['power_entering_list'] output['A_per_layer'] = 0.5*(A_per_layer_p[1:-1] + A_per_layer_s[1:-1]) # if requested, calculate absorption profile as well # layer indices: 0 is incidence, n is transmission medium if profile: z_limit = np.sum(np.array(stack.widths)) full_dist = np.arange(0, z_limit, nm_spacing) layer_start = np.insert(np.cumsum(np.insert(stack.widths, 0, 0)), 0, 0) layer_end = np.cumsum(np.insert(stack.widths, 0, 0)) dist = [] for l in layers: dist = np.hstack((dist, full_dist[np.all((full_dist >= layer_start[l], full_dist < layer_end[l]), 0)])) if pol in 'sp': if coherent: fn = tmm.absorp_analytic_fn().fill_in(out, layers) layer, d_in_layer = tmm.find_in_structure_with_inf(stack.get_widths(), dist) data = tmm.position_resolved(layer, d_in_layer, out) output['profile'] = data['absor'] else: fraction_reaching = 1 - np.cumsum(A_per_layer, axis=0) fn = tmm.absorp_analytic_fn() fn.a1, fn.a3, fn.A1, fn.A2, fn.A3 = np.empty((0, num_wl)), np.empty((0, num_wl)), np.empty((0, num_wl)), \ np.empty((0, num_wl)), np.empty((0, num_wl)) layer, d_in_layer = tmm.find_in_structure_with_inf(stack.get_widths(), dist) data = tmm.inc_position_resolved(layer, d_in_layer, out, coherency_list, 4 * np.pi * np.imag(stack.get_indices(wavelength)) / wavelength) output['profile'] = data for i1, l in enumerate(layers): if coherency_list[l] == 'c': fn_l = tmm.inc_find_absorp_analytic_fn(l, out) fn.a1 = np.vstack((fn.a1, fn_l.a1)) fn.a3 = np.vstack((fn.a3, fn_l.a3)) fn.A1 = np.vstack((fn.A1, fn_l.A1)) fn.A2 = np.vstack((fn.A2, fn_l.A2)) fn.A3 = np.vstack((fn.A3, fn_l.A3)) else: # DO NOT KNOW IF UNITS ARE CORRECT alpha = np.imag(stack.get_indices(wavelength)[l])*4*np.pi/wavelength fn.a1 = np.vstack((fn.a1, alpha)) fn.A2 = np.vstack((fn.A2, alpha*fraction_reaching[l-1])) fn.a3 = np.vstack((fn.a3, np.zeros((1, num_wl)))) fn.A1 = np.vstack((fn.A1, np.zeros((1, num_wl)))) fn.A3 = np.vstack((fn.A3, np.zeros((1, num_wl)))) else: if coherent: fn_s = tmm.absorp_analytic_fn().fill_in(out_s, layers) fn_p = tmm.absorp_analytic_fn().fill_in(out_p, layers) fn = fn_s.add(fn_p).scale(0.5) layer, d_in_layer = tmm.find_in_structure_with_inf(stack.get_widths(), dist) data_s = tmm.position_resolved(layer, d_in_layer, out_s) data_p = tmm.position_resolved(layer, d_in_layer, out_p) output['profile'] = 0.5 * (data_s['absor'] + data_p['absor']) else: fraction_reaching_s = 1 - np.cumsum(A_per_layer_s, axis=0) fraction_reaching_p = 1 - np.cumsum(A_per_layer_s, axis=0) fraction_reaching = 0.5*(fraction_reaching_s + fraction_reaching_p) fn = tmm.absorp_analytic_fn() fn.a1, fn.a3, fn.A1, fn.A2, fn.A3 = np.empty((0, num_wl)), np.empty((0, num_wl)), np.empty((0, num_wl)), \ np.empty((0, num_wl)), np.empty((0, num_wl)) layer, d_in_layer = tmm.find_in_structure_with_inf(stack.get_widths(), dist) data_s = tmm.inc_position_resolved(layer, d_in_layer, out_s, coherency_list, 4 * np.pi * np.imag(stack.get_indices(wavelength)) / wavelength) data_p = tmm.inc_position_resolved(layer, d_in_layer, out_p, coherency_list, 4 * np.pi * np.imag(stack.get_indices(wavelength)) / wavelength) output['profile'] = 0.5 * (data_s + data_p) for i1, l in enumerate(layers): if coherency_list[l] == 'c': fn_s = tmm.inc_find_absorp_analytic_fn(l, out_s) fn_p = tmm.inc_find_absorp_analytic_fn(l, out_s) fn_l = fn_s.add(fn_p).scale(0.5) fn.a1 = np.vstack((fn.a1, fn_l.a1)) fn.a3 = np.vstack((fn.a3, fn_l.a3)) fn.A1 = np.vstack((fn.A1, fn_l.A1)) fn.A2 = np.vstack((fn.A2, fn_l.A2)) fn.A3 = np.vstack((fn.A3, fn_l.A3)) else: # DO NOT KNOW IF UNITS ARE CORRECT alpha = np.imag(stack.get_indices(wavelength)[l]) * 4 * np.pi / wavelength fn.a1 = np.vstack((fn.a1, alpha)) fn.A2 = np.vstack((fn.A2, alpha * fraction_reaching[l - 1])) fn.a3 = np.vstack((fn.a3, np.zeros((1, num_wl)))) fn.A1 = np.vstack((fn.A1, np.zeros((1, num_wl)))) fn.A3 = np.vstack((fn.A3, np.zeros((1, num_wl)))) output['profile_coeff'] = np.stack((fn.A1, fn.A2, np.real(fn.A3), np.imag(fn.A3), fn.a1, fn.a3)) # shape is (5, n_layers, num_wl) return output