def __init__( self, coefficient, x=None, quantum_yield=1.0, phase_function=None, hist=False, name="Scatterer", ): """ Parameters ---------- coefficient: float, list, tuple or numpy.ndarray Specifies the scattering coefficient per unit length. Constant values can be supplied or a spectrum per nanometer per unit length. x: list, tuple of numpy.ndarray (optional) Wavelength values in nanometers. Required when specifying a the `coefficient` with an list or tuple. quantum_yield: float (optional) Default value is 1.0. To include non-radiative scattering use values between less than 1.0. phase_function callable (optional) Determines the direction of scattering. If None is supplied scattering is isotropic. hist: Bool Specifies how the coefficient spectrum is sampled. If `True` the values are treated as a histogram. If `False` the values are linearly interpolated. name: str A user-defined identifier string """ super(Scatterer, self).__init__(name=name) # Make absorption/scattering spectrum distribution self._coefficient = coefficient if coefficient is None: raise ValueError("Coefficient must be specified.") elif isinstance(coefficient, (float, np.float)): self._abs_dist = Distribution(x=None, y=coefficient, hist=hist) elif isinstance(coefficient, np.ndarray): self._abs_dist = Distribution(x=coefficient[:, 0], y=coefficient[:, 1], hist=hist) elif isinstance(coefficient, (list, tuple)): if x is None: raise ValueError("Requires `x`.") self._abs_dist = Distribution.from_functions(x, coefficient, hist=hist) self.quantum_yield = quantum_yield self.phase_function = (phase_function if phase_function is not None else isotropic)
def test2(): x = np.arange(400, 801, dtype=np.float) size = (l, w, d) = (4.8, 1.8, 0.250) # cm-1 lsc = LSC(size, wavelength_range=x) lsc.add_luminophore( "Fluro Red", np.column_stack((x, fluro_red.absorption(x) * 11.387815)), # cm-1 np.column_stack((x, fluro_red.emission(x))), quantum_yield=0.95, ) lsc.add_absorber("PMMA", 0.02) # cm-1 def lamp_spectrum(x): """ Fit to an experimentally measured lamp spectrum with long wavelength filter. """ def g(x, a, p, w): return a * np.exp(-(((p - x) / w)**2)) a1 = 0.53025700136646192 p1 = 512.91400020614333 w1 = 93.491838802960473 a2 = 0.63578999789955015 p2 = 577.63100003089369 w2 = 66.031706473985736 return g(x, a1, p1, w1) + g(x, a2, p2, w2) lamp_dist = Distribution(x, lamp_spectrum(x)) wavelength_callable = lambda: lamp_dist.sample(np.random.uniform()) position_callable = lambda: rectangular_mask(l / 2, w / 2) lsc.add_light( "Oriel Lamp + Filter", (0.0, 0.0, 0.5 * d + 0.01), # put close to top surface rotation=(np.radians(180), (1, 0, 0)), # normal and into the top surface wavelength=wavelength_callable, # wavelength delegate callable position=position_callable, # uniform surface illumination ) lsc.add_solar_cell({"left", "right", "near", "far"}) lsc.add_air_gap_mirror(lambertian=False) lsc.show() throw = 300 lsc.simulate(throw, emit_method="redshift") lsc.report()
def __init__(self, emission_spectrum, quantum_yield, *args, **kwargs): super(Emissive, self).__init__(*args, **kwargs) check_spectrum_like(emission_spectrum) self._emission_dist = Distribution( x=emission_spectrum[:, 0], y=emission_spectrum[:, 1] ) self._quantum_yield = quantum_yield
def test_step_sample(self): """ Sampling a step function should only return two value. """ nmedge = 600.0 nmmin, nmmax = (400.0, 800.0) spacing = 1.0 x = np.arange(nmmin, nmmax + spacing, spacing) abs_spec = np.column_stack((x, bandgap(x, nmedge, 1.0))) # ends a mid range. dist = Distribution(abs_spec[:, 0], abs_spec[:, 1], hist=True) xmin = dist.sample(0) assert np.isclose(xmin, nmmin) xmax = dist.sample(1) # The probabiliity of getting a value > nmedge is zero assert np.isclose(xmax, nmedge) pmin = dist.lookup(nmmin) pmax = dist.lookup(nmmax) assert pmin >= 0.0 and pmin <= dist.lookup(nmmin+spacing) assert pmax == 1.0 values = dist.sample(np.linspace(dist.lookup(599-spacing), dist.lookup(600+spacing), 10000)) assert len(set(values)) == 3
def test_sample_range(self): x = np.linspace(400, 1010, 2000) abs_spec = np.column_stack((x, bandgap(x, 600, 1000))) ems_spec = thermodynamic_emission(abs_spec, T=300, mu=0.1) dist = Distribution(ems_spec[:, 0], ems_spec[:, 1]) xmin = dist.sample(0) assert np.isclose(xmin, x.min()) xmax = dist.sample(1) assert np.isclose(xmax, x.max()) y_at_xmin = dist.lookup(xmin) assert np.isclose(y_at_xmin, 0.0) y_at_xmax = dist.lookup(xmax) assert np.isclose(y_at_xmin, 0.0)
def __init__( self, coefficient, emission=None, x=None, hist=False, quantum_yield=1.0, phase_function=None, name="Luminophore", ): """ coefficient: float, list, tuple or numpy.ndarray Specifies the absorption coefficient per unit length. Constant values can be supplied or a spectrum per nanometer per unit length. If using a list of tuple you should also specify the wavelengths using the `x` keyword. If using a numpy array use `column_stack` to supply a single array with a wavelength and coefficient values. emission: float, list, tuple or numpy.ndarray (optional) Specifies the emission line-shape per nanometer. If `None` will use a Gaussian centred at 600nm. If using a list of tuple you should also specify the wavelengths using the `x` keyword. If using a numpy array use `column_stack` to supply a single array with a wavelength and coefficient values. x: list, tuple of numpy.ndarray (optional) Wavelength values in nanometers. Required when specifying a the `coefficient` with an list or tuple. quantum_yield: float (optional) The probability of re-emitting a ray. phase_function callable (optional) Specifies the direction of emitted rays. hist: Bool Specifies how the absorption and emission spectra are sampled. If `True` the values are treated as a histogram. If `False` the values are linearly interpolated. name: str A user-defined identifier string """ super(Luminophore, self).__init__( coefficient, x=x, quantum_yield=quantum_yield, phase_function=phase_function, hist=hist, name=name, ) # Make emission spectrum distribution self._emission = emission if emission is None: self._ems_dist = Distribution.from_functions( x, [lambda x: gaussian(x, 1.0, 600.0, 40.0)], hist=hist) elif isinstance(emission, np.ndarray): self._ems_dist = Distribution(x=emission[:, 0], y=emission[:, 1], hist=hist) elif isinstance(emission, (tuple, list)): if x is None: raise ValueError("Requires `x`.") self._ems_dist = Distribution.from_functions(x, emission, hist=hist) else: raise ValueError("Luminophore `emission` arg has wrong type.")
def test1(): x = np.arange(400, 801, dtype=np.float) size = (l, w, d) = (4.8, 1.8, 0.250) # cm-1 lsc = LSC(size, wavelength_range=x) lsc.add_luminophore( "Fluro Red", np.column_stack((x, fluro_red.absorption(x) * 11.387815)), # cm-1 np.column_stack((x, fluro_red.emission(x))), quantum_yield=0.95, ) lsc.add_absorber("PMMA", 0.02) # cm-1 def lamp_spectrum(x): """ Fit to an experimentally measured lamp spectrum with long wavelength filter. """ def g(x, a, p, w): return a * np.exp(-(((p - x) / w)**2)) a1 = 0.53025700136646192 p1 = 512.91400020614333 w1 = 93.491838802960473 a2 = 0.63578999789955015 p2 = 577.63100003089369 w2 = 66.031706473985736 return g(x, a1, p1, w1) + g(x, a2, p2, w2) lamp_dist = Distribution(x, lamp_spectrum(x)) wavelength_callable = lambda: lamp_dist.sample(np.random.uniform()) position_callable = lambda: rectangular_mask(l / 2, w / 2) lsc.add_light( "Oriel Lamp + Filter", (0.0, 0.0, 0.5 * d + 0.01), # put close to top surface rotation=(np.radians(180), (1, 0, 0)), # normal and into the top surface wavelength=wavelength_callable, # wavelength delegate callable position=position_callable, # uniform surface illumination ) lsc.show() throw = 2500 lsc.simulate(throw, emit_method="redshift") edge = lsc.spectrum(facets={"left", "right", "near", "far"}, source="all") escape = lsc.spectrum(facets={"top", "bottom"}, source="all") lost = lsc.spectrum(source="all", events={"absorb"}) import matplotlib.pyplot as plt plt.hist(edge, bins=np.linspace(400, 800, 10), label="edge") plt.hist(escape, bins=np.linspace(400, 800, 10), label="escape") plt.hist(lost, bins=np.linspace(400, 800, 10), label="lost") plt.show() # number of lamp rays hitting the top surface incident = lsc.spectrum(source={"Oriel Lamp + Filter"}, kind="first", facets={"top"}) hitting = len(incident) counts = {"edge": len(edge), "escape": len(escape), "lost": len(lost)} fractions = { "edge": counts["edge"] / hitting, "escape": counts["escape"] / hitting, "lost": counts["lost"] / hitting, } print(counts, "sum", np.sum(list(counts.values()))) print(fractions, "sum", np.sum(list(fractions.values())))
def __init__(self, coefficient, x=None, quantum_yield=1.0, phase_function=None, hist=False, name="Scatterer"): """ coefficient: float, list, tuple or numpy.ndarray Specifies the scattering coefficient per unit length. Constant values can be supplied or a spectrum per nanometer per unit length. If using a list of tuple you should also specify the wavelengths using the `x` keyword:: x = numpy.linspace(600, 800) # nanometer values coefficient = list(numpy.ones(x.shape) * 1.5) # per nm per unit length Scatterer( coefficient, x=x ) If using a numpy array use `column_stack` to supply a single array with a wavelength and coefficient values:: Scatterer( coefficient=numpy.column_stack((x, y)) ) x: list, tuple of numpy.ndarray (optional) Wavelength values in nanometers. Required when specifying a the `coefficient` with an list or tuple. quantum_yield: float (optional) Default value is 1.0. To include non-radiative scattering use values between less than 1.0. phase_function callable (optional) Determines the direction of scattering. If None is supplied scattering is isotropic. hist: Bool Specifies how the coefficient spectrum is sampled. If `True` the values are treated as a histogram. If `False` the values are linearly interpolated. name: str A user-defined identifier string """ super(Scatterer, self).__init__(name=name) # Make absorption/scattering spectrum distribution self._coefficient = coefficient if coefficient is None: raise ValueError("Coefficient must be specified.") elif isinstance(coefficient, (float, np.float)): self._abs_dist = Distribution(x=None, y=coefficient, hist=hist) elif isinstance(coefficient, np.ndarray): self._abs_dist = Distribution(x=coefficient[:, 0], y=coefficient[:, 1], hist=hist) elif isinstance(coefficient, (list, tuple)): if x is None: raise ValueError("Requires `x`.") self._abs_dist = Distribution.from_functions(x, coefficient, hist=hist) self.quantum_yield = quantum_yield self.phase_function = phase_function if phase_function is not None else isotropic