def test_goniometer_detector(): # test z_pos = np.array([[0, 0, 0, 0], [1, 1, 1, 1], [-1, -1, 2, 2], [-2, -2, 20, -0.0000001]]) ntrajectories = z_pos.shape[1] nevents = z_pos.shape[0] x_pos = np.zeros((nevents, ntrajectories)) y_pos = np.zeros((nevents, ntrajectories)) ky = np.zeros((nevents, ntrajectories)) kx = np.array([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 1 / np.sqrt(2)]]) kz = np.array([[1, 1, 1, 1], [-1, -1, 1, 1], [-1, -1, 1, -1 / np.sqrt(2)]]) weights = np.ones((nevents, ntrajectories)) trajectories = mc.Trajectory([x_pos, y_pos, z_pos], [kx, ky, kz], weights) thickness = 10 n_medium = 1 n_sample = 1 R, T = det.calc_refl_trans(trajectories, thickness, n_medium, n_sample, 'film', detector=True, det_theta=sc.Quantity('45 degrees'), det_len=sc.Quantity('1 um'), det_dist=sc.Quantity('10 cm'), plot_detector=True) assert_almost_equal(R, 0.25)
def test_throw_valueerror_for_polydisperse_unspecified_parameters(): # test that a valueerror is raised when the system is polydisperse and radius2 # concentration or pdi are not specified with pytest.raises(ValueError): seed = 1 nevents = 10 ntrajectories = 5 radius_cs = sc.Quantity(np.array([100, 150]), 'nm') # specify the radii from innermost to outermost layer n_particle_cs = sc.Quantity(np.array([1.5,1.5]), '') # specify the index from innermost to outermost layer concentration = sc.Quantity(np.array([0.9,0.1]), '') pdi = sc.Quantity(np.array([1e-7, 1e-7]), '') # monodisperse limit # calculate the volume fractions of each layer vf_array = np.empty(len(radius_cs)) r_array = np.array([0] + radius_cs.magnitude.tolist()) for r in np.arange(len(r_array)-1): vf_array[r] = (r_array[r+1]**3-r_array[r]**3) / (r_array[-1:]**3) * volume_fraction n_sample_cs = ri.n_eff(n_particle_cs, n_matrix, vf_array) R_cs, T_cs = calc_montecarlo(nevents, ntrajectories, radius_cs, n_particle_cs, n_sample_cs, n_medium, volume_fraction, wavelen, seed, concentration=concentration, pdi=pdi, polydisperse=True) # unspecified radius2
def test_trajectories(): # Initialize runs nevents = 2 ntrajectories = 3 r0, k0, W0 = mc.initialize(nevents, ntrajectories, n_matrix, n_sample, 'sphere', seed=1, sample_diameter=sc.Quantity('1 um')) r0 = sc.Quantity(r0, 'um') k0 = sc.Quantity(k0, '') W0 = sc.Quantity(W0, '') # Create a Trajectory object trajectories = mc.Trajectory(r0, k0, W0)
def test_index_match(): ntrajectories = 2 nevents = 3 wavelen = sc.Quantity('600 nm') radius = sc.Quantity('0.140 um') microsphere_radius = sc.Quantity('10 um') volume_fraction = sc.Quantity(0.55,'') n_particle = sc.Quantity(1.6,'') n_matrix = sc.Quantity(1.6,'') n_sample = n_matrix n_medium = sc.Quantity(1,'') p, mu_scat, mu_abs = mc.calc_scat(radius, n_particle, n_sample, volume_fraction, wavelen) # initialize all at center top edge of the sphere going down r0_sphere = np.zeros((3,nevents+1,ntrajectories)) k0_sphere = np.zeros((3,nevents,ntrajectories)) k0_sphere[2,0,:] = 1 W0_sphere = np.ones((nevents, ntrajectories)) # make into quantities with units r0_sphere = sc.Quantity(r0_sphere, 'um') k0_sphere = sc.Quantity(k0_sphere, '') W0_sphere = sc.Quantity(W0_sphere, '') # Generate a matrix of all the randomly sampled angles first sintheta, costheta, sinphi, cosphi, _, _ = mc.sample_angles(nevents, ntrajectories, p) # Create step size distribution step = mc.sample_step(nevents, ntrajectories, mu_scat) # make trajectories object trajectories_sphere = mc.Trajectory(r0_sphere, k0_sphere, W0_sphere) trajectories_sphere.absorb(mu_abs, step) trajectories_sphere.scatter(sintheta, costheta, sinphi, cosphi) trajectories_sphere.move(step) # calculate reflectance refl_sphere, trans = det.calc_refl_trans(trajectories_sphere, microsphere_radius, n_medium, n_sample, 'sphere', p=p, mu_abs=mu_abs, mu_scat=mu_scat, run_fresnel_traj = True, max_stuck = 0.0001) # calculated by hand from fresnel infinite sum refl_fresnel_int = 0.053 # calculated by hand refl_exact = refl_fresnel_int + (1-refl_fresnel_int)**2*refl_fresnel_int/(1-refl_fresnel_int**2) assert_almost_equal(refl_sphere, refl_exact, decimal=3)
def calc_montecarlo(nevents, ntrajectories, radius, n_particle, n_sample, n_medium, volume_fraction, wavelen, seed, radius2=None, concentration=None, pdi=None, polydisperse=False, fine_roughness=0., coarse_roughness=0.): # Function to run montecarlo for the tests p, mu_scat, mu_abs = mc.calc_scat(radius, n_particle, n_sample, volume_fraction, wavelen, radius2=radius2, concentration=concentration, pdi=pdi, polydisperse=polydisperse, fine_roughness=fine_roughness) if coarse_roughness > 0.: r0, k0, W0, kz0_rotated, kz0_reflected = mc.initialize(nevents, ntrajectories, n_medium, n_sample, 'film', seed=seed, coarse_roughness=coarse_roughness) else: r0, k0, W0 = mc.initialize(nevents, ntrajectories, n_medium, n_sample, 'film', seed=seed) kz0_rotated = None kz0_reflected = None r0 = sc.Quantity(r0, 'um') k0 = sc.Quantity(k0, '') W0 = sc.Quantity(W0, '') sintheta, costheta, sinphi, cosphi, _, _= mc.sample_angles(nevents, ntrajectories,p) step = mc.sample_step(nevents, ntrajectories, mu_scat, fine_roughness=fine_roughness) trajectories = mc.Trajectory(r0, k0, W0) trajectories.absorb(mu_abs, step) trajectories.scatter(sintheta, costheta, sinphi, cosphi) trajectories.move(step) cutoff = sc.Quantity('50 um') # calculate R, T R, T = det.calc_refl_trans(trajectories, cutoff, n_medium, n_sample, 'film', kz0_rot=kz0_rotated, kz0_refl=kz0_reflected, fine_roughness=fine_roughness, n_matrix=n_matrix) return R, T
def calc_path_length(step, exit_indices): ''' Returns reflectance and transmittance as a function of event number Parameters ---------- step: 2d array (shape: nevents, ntrajectories) Sampled step sizes for all events and trajectories in Monte Carlo model exit_indices: 1d array (length: ntrajectories) event number at exit for each trajectory. Input refl_indices if you want to only consider reflectance and trans_indices if you want to only consider transmittance. Input refl_indices + trans_indices if you want to consider both Returns ------- path_length_traj: 1d array (length: ntrajectories) path length travelled before exit for each trajectory. ''' ntraj = len(exit_indices) path_length_traj = sc.Quantity(np.zeros(ntraj), 'um') for i in range(0, ntraj): path_length_traj[i] = np.sum(step[:exit_indices[i], i]) return path_length_traj
def test_calc_refl_trans(): small_n = sc.Quantity(1,'') large_n = sc.Quantity(2,'') # test absoprtion and stuck without fresnel z_pos = np.array([[0,0,0,0],[1,1,1,1],[-1,11,2,11],[-2,12,4,12]]) x_pos = np.array([[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]) y_pos = np.array([[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]) ntrajectories = z_pos.shape[1] kx = np.zeros((3,4)) ky = np.zeros((3,4)) kz = np.array([[1,1,1,1],[-1,1,1,1],[-1,1,1,1]]) weights = np.array([[.8, .8, .9, .8],[.7, .3, .7, 0],[.1, .1, .5, 0]]) trajectories = mc.Trajectory([x_pos, y_pos, z_pos],[kx, ky, kz], weights) p, mu_scat, mu_abs = mc.calc_scat(radius, n_particle, small_n, volume_fraction, wavelen) refl, trans = det.calc_refl_trans(trajectories, assembly_radius, small_n, small_n, 'sphere') expected_trans_array = np.array([0., .3, 0.25, 0])/ntrajectories #calculated manually expected_refl_array = np.array([.7, 0., .25, 0.])/ntrajectories #calculated manually assert_almost_equal(refl, np.sum(expected_refl_array)) assert_almost_equal(trans, np.sum(expected_trans_array)) # test fresnel as well refl, trans = det.calc_refl_trans(trajectories, assembly_radius, small_n, large_n, 'sphere') expected_trans_array = np.array([0.0345679, .25185185, 0.22222222, 0.])/ntrajectories #calculated manually expected_refl_array = np.array([.69876543, 0.12592593, 0.33333333, 0.11111111])/ntrajectories #calculated manually assert_almost_equal(refl, np.sum(expected_refl_array)) assert_almost_equal(trans, np.sum(expected_trans_array)) # test steps in z longer than sample thickness z_pos = np.array([[0,0,0,0],[1,1,14,12],[-1,11,2,11],[-2,12,4,12]]) trajectories = mc.Trajectory([x_pos, y_pos, z_pos],[kx, ky, kz], weights) refl, trans= det.calc_refl_trans(trajectories, assembly_radius, small_n, small_n, 'sphere') expected_trans_array = np.array([0., .3, .9, .8])/ntrajectories #calculated manually expected_refl_array = np.array([.7, 0., 0., 0.])/ntrajectories #calculated manually assert_almost_equal(refl, np.sum(expected_refl_array)) assert_almost_equal(trans, np.sum(expected_trans_array)) # test tir z_pos = np.array([[0,0,0,0],[1,1,1,1],[-1,11,2,11],[-2,12,4,12]]) weights = np.ones((3,4)) trajectories = mc.Trajectory([x_pos, y_pos, z_pos],[kx, ky, kz], weights) refl, trans = det.calc_refl_trans(trajectories, assembly_radius, small_n, small_n, 'sphere', p=p, mu_abs=mu_abs, mu_scat=mu_scat, run_fresnel_traj=True) # since the tir=True reruns the stuck trajectory, we don't know whether it will end up reflected or transmitted # all we can know is that the end refl + trans > 0.99 assert_almost_equal(refl + trans, 1.)
def test_polarization_absorption(): n_particle = sc.Quantity(1.5 + 0.01j, '') n_matrix = sc.Quantity(1.0 + 0.01j, '') n_medium = sc.Quantity(1.0 + 0.01j, '') n_sample = ri.n_eff(n_particle, n_matrix, volume_fraction) # run mc trajectories with polarization p, mu_scat, mu_abs = mc.calc_scat(radius, n_particle, n_sample, volume_fraction, wavelen, polarization=True) #print(p) r0, k0, W0, p0 = mc.initialize(nevents, ntrajectories, n_medium, n_sample, 'film', polarization=True) r0 = sc.Quantity(r0, 'um') k0 = sc.Quantity(k0, '') W0 = sc.Quantity(W0, '') p0 = sc.Quantity(p0, '') sintheta, costheta, sinphi, cosphi, theta, phi = mc.sample_angles( nevents, ntrajectories, p) trajectories = mc.Trajectory(r0, k0, W0, p0) trajectories.scatter(sintheta, costheta, sinphi, cosphi) trajectories.polarize(theta, phi, sintheta, costheta, sinphi, cosphi, n_particle, n_sample, radius, wavelen, volume_fraction) #################### check polarization magnitude is always 1 pol_mag = np.sqrt(trajectories.polarization[0, :, :] * np.conj(trajectories.polarization[0, :, :]) + trajectories.polarization[1, :, :] * np.conj(trajectories.polarization[1, :, :]) + trajectories.polarization[2, :, :] * np.conj(trajectories.polarization[2, :, :])) pol_mag_sum = np.sum(np.abs(pol_mag.magnitude)) assert_equal(pol_mag_sum, nevents * ntrajectories) ############ check that polarization vector is perpendicular to direction # dot product is a dot conj(b), but b is real, so can just do a dot b dot = ( trajectories.polarization[0, :, :] * trajectories.direction[0, :, :] + trajectories.polarization[1, :, :] * trajectories.direction[1, :, :] + trajectories.polarization[2, :, :] * trajectories.direction[2, :, :]) dot_sum = np.sum(np.abs(dot.magnitude)) assert_almost_equal(dot_sum, 0.0, decimal=12)
def test_find_max_like(): wavelength = [450, 500, 550, 600] wavelength_ind = find_close_indices(wavelength_sigma, sc.Quantity(wavelength, 'nm')) sigma_test = sigma[np.array(wavelength_ind)] sample = Sample(wavelength, particle_index=1.59, matrix_index=1) theta = (0.55, 120, 120, 0.02, 0, 0.02, 0 ) #these are the default starting values for lmfit calculation spect = calc_model_spect(sample, theta, (sigma_test, sigma_test), ntrajectories, nevents, seed=2) theta_range = { 'min_phi': 0.34, 'max_phi': 0.74, 'min_radius': 70, 'max_radius': 160, 'min_thickness': 1, 'max_thickness': 1000, 'min_l0_r': 0, 'max_l0_r': 1, 'min_l1_r': -1, 'max_l1_r': 1, 'min_l0_t': 0, 'max_l0_t': 1, 'min_l1_t': -1, 'max_l1_t': 1 } theta_guess = { 'phi': 0.55, 'radius': 120, 'thickness': 120, 'l0_r': 0.02, 'l1_r': 0, 'l0_t': 0.02, 'l1_t': 0 } max_like_vals = find_max_like(spect, sample, theta_guess=theta_guess, theta_range=theta_range, sigma=(sigma_test, sigma_test), ntrajectories=ntrajectories, nevents=nevents, seed=2) assert_almost_equal(max_like_vals, theta)
def test_trajectories(): # Initialize runs nevents = 2 ntrajectories = 3 r0, k0, W0 = mc.initialize(nevents, ntrajectories, n_medium, n_sample, 'film', seed=1) r0 = sc.Quantity(r0, 'um') k0 = sc.Quantity(k0, '') W0 = sc.Quantity(W0, '') # Create a Trajectory object trajectories = mc.Trajectory(r0, k0, W0) # Test the absorb function mu_abs = 1 / sc.Quantity(10, 'um') step = sc.Quantity(np.array([[1, 1, 1], [1, 1, 1]]), 'um') trajectories.absorb(mu_abs, step) assert_almost_equal( trajectories.weight, np.array([[0.90483742, 0.90483742, 0.90483742], [0.81873075, 0.81873075, 0.81873075]])) # Make up some test theta and phi sintheta = np.array([[0., 0., 0.], [0., 0., 0.]]) costheta = np.array([[-1., -1., -1.], [1., 1., 1.]]) sinphi = np.array([[0., 0., 0.], [0., 0., 0.]]) cosphi = np.array([[0., 0., 0.], [0., 0., 0.]]) trajectories.scatter(sintheta, costheta, sinphi, cosphi) # Expected propagation directions kx = sc.Quantity(np.array([[0., 0., 0.], [0., 0., 0.]]), '') ky = sc.Quantity(np.array([[0., 0., 0.], [0., 0., 0.]]), '') kz = sc.Quantity(np.array([[1., 1., 1.], [-1., -1., -1.]]), '') # Test the scatter function assert_almost_equal(trajectories.direction[0], kx.magnitude) assert_almost_equal(trajectories.direction[1], ky.magnitude) assert_almost_equal(trajectories.direction[2], kz.magnitude) # Test the move function trajectories.move(step) assert_equal(trajectories.position[2], np.array([[0, 0, 0], [1, 1, 1], [0, 0, 0]]))
def test_calc_model_spect(): wavelength = [500] sample = Sample(wavelength, 1.5, 1) theta = (0.5, 100, 200, 0, 0, 0, 0) wavelength_ind = find_close_indices(wavelength_sigma, sc.Quantity(wavelength, 'nm')) sigma_test = sigma[np.array(wavelength_ind)] assert_frame_equal( calc_model_spect(sample, theta, (sigma_test, sigma_test), ntrajectories, nevents, 2), Spectrum(500, reflectance=0.828595524325, sigma_r=0.0193369922424, transmittance=0.171404475675, sigma_t=0.0193369922424))
def test_log_posterior(): wavelength = [500] spectrum = Spectrum(wavelength, reflectance=0.5, sigma_r=0.1) sample = Sample(wavelength, 1.5, 1) theta_range = { 'min_phi': 0.35, 'max_phi': 0.74, 'min_radius': 70, 'max_radius': 201, 'min_thickness': 1, 'max_thickness': 1000 } wavelength_ind = find_close_indices(wavelength_sigma, sc.Quantity(wavelength, 'nm')) sigma_test = sigma[np.array(wavelength_ind)] # When parameters are within prior range theta1 = (0.5, 200, 200, 0, 0) post1 = log_posterior(theta1, spectrum, sample, theta_range=theta_range, sigma=(sigma_test, sigma_test), ntrajectories=ntrajectories, nevents=nevents, seed=2) assert_approx_equal(post1, -6.0413752765269875) # When parameters are not within prior range theta2 = (0.3, 200, 200, 0, 0) post2 = log_posterior(theta2, spectrum, sample, theta_range=theta_range, sigma=(sigma_test, sigma_test), ntrajectories=ntrajectories, nevents=nevents, seed=2) assert_approx_equal(post2, -1e100)
def test_run_structcol(): # Test that structcol package is imported and reflectance calculation is correct import structcol as sc wavelength = np.array([400., 500.]) particle_radius = 150. thickness = 100. particle_index = np.array([1.40, 1.41]) matrix_index = np.array([1.0, 1.0]) volume_fraction = 0.5 incident_angle = 0.0 medium_index = np.array([1.0, 1.0]) front_index = np.array([1.0, 1.0]) back_index = np.array([1.0, 1.0]) wavelength_ind = find_close_indices(wavelength_sigma, sc.Quantity(wavelength, 'nm')) sigma_test = sigma[np.array(wavelength_ind)] refl, trans = calc_refl_trans(volume_fraction, particle_radius, thickness, Sample(wavelength, particle_index, matrix_index, medium_index, front_index, back_index, incident_angle), ntrajectories, nevents, seed=1) spectrum = Spectrum(wavelength, reflectance=refl, transmittance=trans, sigma_r=sigma_test, sigma_t=sigma_test) outarray = np.array([0.84576, 0.74796]) assert_almost_equal(spectrum.reflectance, outarray, decimal=5)
def calc_refl_trans(volume_fraction, Sample, ntrajectories=300, nevents=200, seed=None): """ Calculates a reflection spectrum using the structcol package. Parameters ---------- volume_fraction : float volume fraction of scatterer in the system Sample : Sample object contains information about the sample that produced data ntrajectories : int number of trajectories nevents : int number of scattering events seed : int or None If seed is int, the simulation results will be reproducible. If seed is None, the simulation results are random. Returns ---------- reflection : ndarray fraction of reflected trajectories transmission : ndarray fraction of transmitted trajectories """ # Read in system parameters from the Sample object particle_radius = sc.Quantity(Sample.particle_radius, 'nm') thickness = sc.Quantity(Sample.thickness, 'um') particle_index = sc.Quantity(Sample.particle_index, '') matrix_index = sc.Quantity(Sample.matrix_index, '') medium_index = sc.Quantity(Sample.medium_index, '') incident_angle = Sample.incident_angle wavelength = sc.Quantity(Sample.wavelength, 'nm') # Calculate the effective index of the sample sample_index = ri.n_eff(particle_index, matrix_index, volume_fraction) reflectance = [] transmittance = [] for i in np.arange(len(wavelength)): # Calculate the phase function and scattering and absorption lengths # from the single scattering model p, mu_scat, mu_abs = mc.calc_scat(particle_radius, particle_index[i], sample_index[i], volume_fraction, wavelength[i], phase_mie=False, mu_scat_mie=False) # Initialize the trajectories r0, k0, W0 = mc.initialize(nevents, ntrajectories, medium_index[i], sample_index[i], seed=seed, incidence_angle=incident_angle) r0 = sc.Quantity(r0, 'um') k0 = sc.Quantity(k0, '') W0 = sc.Quantity(W0, '') # Generate a matrix of all the randomly sampled angles first sintheta, costheta, sinphi, cosphi, _, _ = mc.sample_angles( nevents, ntrajectories, p) # Create step size distribution step = mc.sample_step(nevents, ntrajectories, mu_abs, mu_scat) # Create trajectories object trajectories = mc.Trajectory(r0, k0, W0) # Run photons trajectories.absorb(mu_abs, step) trajectories.scatter(sintheta, costheta, sinphi, cosphi) trajectories.move(step) # Calculate the reflection fraction R_fraction, T_fraction = mc.calc_refl_trans(trajectories, sc.Quantity('0.0 um'), thickness, medium_index[i], sample_index[i], detection_angle=np.pi / 2) reflectance.append(R_fraction) transmittance.append(T_fraction) # Define an array for the visible wavelengths wavelength_sigma = sc.Quantity(np.arange(400, 1000, 61), 'nm') # The uncertainty for the reflection fraction is taken to be 1 standard # deviation from the mean, and was calculated using the results of 100 identical runs. sigma_measured = np.array([ 1.578339786806479475e-02, 1.814049675099610806e-02, 2.263508305348480368e-02, 2.280651893165159400e-02, 2.289441072296988580e-02, 2.475289930703982594e-02, 2.591244161256863951e-02, 2.432751507610093206e-02, 2.840212103614853448e-02, 2.464656608869982696e-02, 2.535837658100221007e-02, 2.352910256218017013e-02, 2.264365724262535837e-02, 2.574192164180175851e-02, 2.546192844771695551e-02, 2.767992948024671981e-02, 2.399941085043348632e-02, 2.767133759422578734e-02, 2.759793344858079908e-02, 2.581248267951743655e-02, 2.664000919072649631e-02, 2.914272756298553688e-02, 2.549173729396642454e-02, 2.722649681737301583e-02, 2.322297011391676741e-02, 2.409138186086920430e-02, 2.807311239866464025e-02, 3.018509924866123045e-02, 2.929772336148638717e-02, 2.866675231142475078e-02, 2.377896176281297722e-02, 2.532538972626817778e-02, 2.408458082494839558e-02, 2.823887112376391451e-02, 2.285680624758363796e-02, 2.834624619602043455e-02, 2.342167374818072967e-02, 2.896504983742856365e-02, 2.835463183413225105e-02, 2.981124596936481769e-02, 2.499991827718371987e-02, 2.697080309787770400e-02, 2.788310424558666095e-02, 2.819362357263776805e-02, 2.852537757990830647e-02, 2.651641629976883227e-02, 3.022850005391930842e-02, 2.772006618991802729e-02, 2.971671988748269405e-02, 3.219841220549832933e-02, 2.570752641741474998e-02, 2.352680291863861600e-02, 2.709648629442167056e-02, 2.524674214046034038e-02, 2.758045644043585765e-02, 2.607698592177773098e-02, 2.738258841523178236e-02, 2.868487596917410412e-02, 3.176931078488830218e-02, 2.729837088883461590e-02, 2.513728413028934808e-02 ]) # Find the uncertainties corresponding to each wavelength wavelength_ind = main.find_close_indices(wavelength_sigma, wavelength) sigma = sigma_measured[np.array(wavelength_ind)] return main.Spectrum(wavelength.magnitude, reflectance=np.array(reflectance), transmittance=np.array(transmittance), sigma_r=sigma, sigma_t=sigma)
def calc_refl_trans(volume_fraction, radius, thickness, Sample, ntrajectories, nevents, seed): """ Calculates a reflection spectrum using the structcol package. Parameters ---------- volume_fraction : float volume fraction of scatterer in the system radius : float (in nm) radius of scatterer thickness : float (in um) film thickness of sample Sample : Sample object contains information about the sample that produced data ntrajectories : int number of trajectories for the multiple scattering calculations nevents : int number of scattering events for the multiple scattering calculations seed : int or None If seed is int, the simulation results will be reproducible. If seed is None, the simulation results are random. Returns ---------- reflectance : ndarray fraction of reflected trajectories over the wavelength range transmittance : ndarray fraction of transmitted trajectories over the wavelength range """ # Read in system parameters from the Sample object particle_radius = sc.Quantity(radius, 'nm') thickness = sc.Quantity(thickness, 'um') particle_index = sc.Quantity(Sample.particle_index, '') matrix_index = sc.Quantity(Sample.matrix_index, '') medium_index = sc.Quantity(Sample.medium_index, '') front_index = sc.Quantity(Sample.front_index, '') back_index = sc.Quantity(Sample.back_index, '') incident_angle = Sample.incident_angle wavelength = sc.Quantity(Sample.wavelength, 'nm') reflectance = np.zeros(len(wavelength)) transmittance = np.zeros(len(wavelength)) for i in np.arange(len(wavelength)): # Calculate the effective index of the sample sample_index = ri.n_eff(particle_index[i], matrix_index[i], volume_fraction, maxwell_garnett=True) # Calculate the phase function and scattering and absorption lengths # from the single scattering model p, mu_scat, mu_abs = mc.calc_scat(particle_radius, particle_index[i], sample_index, volume_fraction, wavelength[i]) # Initialize the trajectories r0, k0, W0 = mc.initialize(nevents, ntrajectories, medium_index[i], sample_index, seed=seed, incidence_angle=incident_angle) r0 = sc.Quantity(r0, 'um') k0 = sc.Quantity(k0, '') W0 = sc.Quantity(W0, '') # Generate a matrix of all the randomly sampled angles first sintheta, costheta, sinphi, cosphi, _, _ = mc.sample_angles( nevents, ntrajectories, p) # Create step size distribution step = mc.sample_step(nevents, ntrajectories, mu_abs, mu_scat) # Create trajectories object trajectories = mc.Trajectory(r0, k0, W0) # Run photons trajectories.absorb(mu_abs, step) trajectories.scatter(sintheta, costheta, sinphi, cosphi) trajectories.move(step) # Calculate the reflection fraction reflectance[i], transmittance[i] = mc.calc_refl_trans( trajectories, sc.Quantity('0.0 um'), thickness, medium_index[i], sample_index, n_front=front_index[i], n_back=back_index[i], detection_angle=np.pi / 2) return (reflectance, transmittance)
def test_polarization(): ntrajectories = 50 nevents = 50 n_particle = sc.Quantity(1.5, '') n_matrix = sc.Quantity(1.0, '') n_medium = sc.Quantity(1.0, '') n_sample = ri.n_eff(n_particle, n_matrix, volume_fraction) # run mc trajectories with polarization p, mu_scat, mu_abs = mc.calc_scat(radius, n_particle, n_sample, volume_fraction, wavelen, polarization=True) r0, k0, W0, p0 = mc.initialize(nevents, ntrajectories, n_medium, n_sample, 'film', polarization=True) r0 = sc.Quantity(r0, 'um') k0 = sc.Quantity(k0, '') W0 = sc.Quantity(W0, '') p0 = sc.Quantity(p0, '') sintheta, costheta, sinphi, cosphi, theta, phi = mc.sample_angles( nevents, ntrajectories, p) trajectories = mc.Trajectory(r0, k0, W0, p0) trajectories.scatter(sintheta, costheta, sinphi, cosphi) trajectories.polarize(theta, phi, sintheta, costheta, sinphi, cosphi, n_particle, n_sample, radius, wavelen, volume_fraction) #################### check polarization magnitude is always 1 pol_mag = np.sqrt(trajectories.polarization[0, :, :] * np.conj(trajectories.polarization[0, :, :]) + trajectories.polarization[1, :, :] * np.conj(trajectories.polarization[1, :, :]) + trajectories.polarization[2, :, :] * np.conj(trajectories.polarization[2, :, :])) pol_mag_sum = np.sum(np.abs(pol_mag.magnitude)) assert_almost_equal(pol_mag_sum, nevents * ntrajectories, decimal=10) ########### check that trajectories are becoming depolarized after many ########### scattering events # calculate polarization components at last events pol_x = np.mean(trajectories.polarization[0, -20:-1, :] * np.conj(trajectories.polarization[0, -20:-1, :])) pol_y = np.mean(trajectories.polarization[1, -20:-1, :] * np.conj(trajectories.polarization[1, -20:-1, :])) pol_z = np.mean(trajectories.polarization[2, -20:-1, :] * np.conj(trajectories.polarization[2, -20:-1, :])) assert_almost_equal(pol_x.magnitude, 0.33, decimal=1) assert_almost_equal(pol_y.magnitude, 0.33, decimal=1) assert_almost_equal(pol_z.magnitude, 0.33, decimal=1) ############ check that polarization vector is perpendicular to direction # dot product is a dot conj(b), but b is real, so can just do a dot b dot = ( trajectories.polarization[0, :, :] * trajectories.direction[0, :, :] + trajectories.polarization[1, :, :] * trajectories.direction[1, :, :] + trajectories.polarization[2, :, :] * trajectories.direction[2, :, :]) dot_sum = np.sum(np.abs(dot.magnitude)) assert_almost_equal(dot_sum, 0.0, decimal=10)
''' This file tests functions from inference.py. ''' import structcol as sc from infer_structcol.inference import find_max_like, run_mcmc from infer_structcol.main import Sample, Spectrum, find_close_indices from infer_structcol.model import calc_model_spect from numpy.testing import assert_equal, assert_almost_equal import numpy as np ntrajectories = 600 nevents = 200 wavelength_sigma = sc.Quantity(np.linspace(400, 1000, 61), 'nm') sigma = np.array([ 1.860651421552072735e-02, 1.753980839818162357e-02, 1.839622738704549398e-02, 1.596763386664768955e-02, 1.894484659740078986e-02, 1.722962247665738716e-02, 1.555134197251030123e-02, 1.763293909648367200e-02, 2.027257609441594777e-02, 1.850550125238413501e-02, 1.933699224240205058e-02, 1.873148138270526453e-02, 1.908441182529240290e-02, 1.756355142274622708e-02, 1.590192651066632198e-02, 1.596104976169695697e-02, 2.024553310180053287e-02, 1.955488448380025140e-02, 1.882008022078682577e-02, 1.796507064336797313e-02, 2.004778422542081301e-02, 1.811040666898488388e-02, 1.805909831464867776e-02, 1.810327867013098932e-02, 1.516823124817042248e-02, 1.514314740128578328e-02, 1.696441336804245872e-02, 1.677168419886158890e-02, 1.132382672347467811e-02, 1.224676407793331805e-02, 1.117690246951372202e-02, 1.241312684961146107e-02, 1.326040920813134627e-02, 1.367716094293736952e-02,
def test_reflection_absorbing_particle_or_matrix(): # test that the reflections with a real n_particle and with a complex # n_particle with a 0 imaginary component are the same seed = 1 nevents = 60 ntrajectories = 30 # Reflection using non-absorbing particle R, T = calc_montecarlo(nevents, ntrajectories, radius, n_particle, n_sample, n_medium, volume_fraction, wavelen, seed) # Reflection using particle with an imaginary component of 0 n_particle_abs = sc.Quantity(1.5 + 0j, '') R_abs, T_abs = calc_montecarlo(nevents, ntrajectories, radius, n_particle_abs, n_sample, n_medium, volume_fraction, wavelen, seed) assert_equal(R, R_abs) assert_equal(T, T_abs) # Outputs before refactoring structcol R_before = 0.81382378303119451 R_abs_before = 0.81382378303119451 T_before = 0.1861762169688054 T_abs_before = 0.1861762169688054 assert_almost_equal(R_before, R, decimal=15) assert_almost_equal(R_abs_before, R_abs, decimal=15) assert_almost_equal(T_before, T, decimal=15) assert_almost_equal(T_abs_before, T_abs, decimal=15) # Same as previous test but with absorbing matrix # Reflection using matrix with an imaginary component of 0 n_matrix_abs = sc.Quantity(1. + 0j, '') n_sample_abs = ri.n_eff(n_particle, n_matrix_abs, volume_fraction) R_abs, T_abs = calc_montecarlo(nevents, ntrajectories, radius, n_particle, n_sample_abs, n_medium, volume_fraction, wavelen, seed) assert_equal(R, R_abs) assert_equal(T, T_abs) # Outputs before refactoring structcol R_before = 0.81382378303119451 R_abs_before = 0.81382378303119451 T_before = 0.1861762169688054 T_abs_before = 0.1861762169688054 assert_almost_equal(R_before, R, decimal=15) assert_almost_equal(R_abs_before, R_abs, decimal=15) assert_almost_equal(T_before, T, decimal=15) assert_almost_equal(T_abs_before, T_abs, decimal=15) # test that the reflection is essentially the same when the imaginary # index is 0 or very close to 0 n_matrix_abs = sc.Quantity(1. + 1e-10j, '') n_sample_abs = ri.n_eff(n_particle, n_matrix_abs, volume_fraction) R_abs, T_abs = calc_montecarlo(nevents, ntrajectories, radius, n_particle, n_sample_abs, n_medium, volume_fraction, wavelen, seed) assert_almost_equal(R, R_abs, decimal=6) assert_almost_equal(T, T_abs, decimal=6)
def test_reflection_polydispersity(): seed = 1 nevents = 60 ntrajectories = 30 radius2 = radius concentration = sc.Quantity(np.array([0.9, 0.1]), '') pdi = sc.Quantity(np.array([1e-7, 1e-7]), '') # monodisperse limit # Without absorption: test that the reflectance using very small # polydispersity is the same as the monodisperse case R_mono, T_mono = calc_montecarlo(nevents, ntrajectories, radius, n_particle, n_sample, n_medium, volume_fraction, wavelen, seed, polydisperse=False) R_poly, T_poly = calc_montecarlo(nevents, ntrajectories, radius, n_particle, n_sample, n_medium, volume_fraction, wavelen, seed, radius2=radius2, concentration=concentration, pdi=pdi, polydisperse=True) assert_almost_equal(R_mono, R_poly) assert_almost_equal(T_mono, T_poly) # Outputs before refactoring structcol R_mono_before = 0.81382378303119451 R_poly_before = 0.81382378303119451 T_mono_before = 0.1861762169688054 T_poly_before = 0.1861762169688054 assert_almost_equal(R_mono_before, R_mono, decimal=15) assert_almost_equal(R_poly_before, R_poly, decimal=15) assert_almost_equal(T_mono_before, T_mono, decimal=15) assert_almost_equal(T_poly_before, T_poly, decimal=15) # With absorption: test that the reflectance using with very small # polydispersity is the same as the monodisperse case n_particle_abs = sc.Quantity(1.5 + 0.0001j, '') n_matrix_abs = sc.Quantity(1. + 0.0001j, '') n_sample_abs = ri.n_eff(n_particle_abs, n_matrix_abs, volume_fraction) R_mono_abs, T_mono_abs = calc_montecarlo(nevents, ntrajectories, radius, n_particle_abs, n_sample_abs, n_medium, volume_fraction, wavelen, seed, polydisperse=False) R_poly_abs, T_poly_abs = calc_montecarlo(nevents, ntrajectories, radius, n_particle_abs, n_sample_abs, n_medium, volume_fraction, wavelen, seed, radius2=radius2, concentration=concentration, pdi=pdi, polydisperse=True) assert_almost_equal(R_mono_abs, R_poly_abs, decimal=6) assert_almost_equal(T_mono_abs, T_poly_abs, decimal=6) # Outputs before refactoring structcol R_mono_abs_before = 0.6480185516058053 #A:0.6575973175344868 #A/V:0.74182070115289855 R_poly_abs_before = 0.6476683654364985 #A:0.65723717422505701 #A/V:0.74153254583803685 T_mono_abs_before = 0.09473841417422774 #A:0.080731949531112429 #A/V:0.083823525277616467 T_poly_abs_before = 0.09456832138047852 #A:0.080574244683425236 #A/V:0.083720861809212316 assert_almost_equal(R_mono_abs_before, R_mono_abs, decimal=3) assert_almost_equal(R_poly_abs_before, R_poly_abs, decimal=3) assert_almost_equal(T_mono_abs_before, T_mono_abs, decimal=3) assert_almost_equal(T_poly_abs_before, T_poly_abs, decimal=3) # test that the reflectance is the same for a polydisperse monospecies # and a bispecies with equal types of particles concentration_mono = sc.Quantity(np.array([0., 1.]), '') concentration_bi = sc.Quantity(np.array([0.3, 0.7]), '') pdi2 = sc.Quantity(np.array([1e-1, 1e-1]), '') R_mono2, T_mono2 = calc_montecarlo(nevents, ntrajectories, radius, n_particle, n_sample, n_medium, volume_fraction, wavelen, seed, radius2=radius2, concentration=concentration_mono, pdi=pdi2, polydisperse=True) R_bi, T_bi = calc_montecarlo(nevents, ntrajectories, radius, n_particle, n_sample, n_medium, volume_fraction, wavelen, seed, radius2=radius2, concentration=concentration_bi, pdi=pdi2, polydisperse=True) assert_equal(R_mono2, R_bi) assert_equal(T_mono2, T_bi) # test that the reflectance is the same regardless of the order in which # the radii are specified radius2 = sc.Quantity('70 nm') concentration2 = sc.Quantity(np.array([0.5, 0.5]), '') R, T = calc_montecarlo(nevents, ntrajectories, radius, n_particle, n_sample, n_medium, volume_fraction, wavelen, seed, radius2=radius2, concentration=concentration2, pdi=pdi, polydisperse=True) R2, T2 = calc_montecarlo(nevents, ntrajectories, radius2, n_particle, n_sample, n_medium, volume_fraction, wavelen, seed, radius2=radius, concentration=concentration2, pdi=pdi, polydisperse=True) assert_almost_equal(R, R2) assert_almost_equal(T, T2) # test that the second size is ignored when its concentration is set to 0 radius1 = sc.Quantity('150 nm') radius2 = sc.Quantity('100 nm') concentration3 = sc.Quantity(np.array([1, 0]), '') pdi3 = sc.Quantity(np.array([0., 0.]), '') R3, T3 = calc_montecarlo(nevents, ntrajectories, radius1, n_particle, n_sample, n_medium, volume_fraction, wavelen, seed, radius2=radius2, concentration=concentration3, pdi=pdi3, polydisperse=True) assert_equal(R_mono, R3) assert_equal(T_mono, T3)
.. moduleauthor:: Anna B. Stephenson <*****@*****.**> .. moduleauthor:: Vinothan N. Manoharan <*****@*****.**> """ import numpy as np import structcol as sc import structcol.refractive_index as ri from structcol import montecarlo as mc from structcol import detector as det from structcol import phase_func_sphere as pfs from numpy.testing import assert_equal, assert_almost_equal ### Set parameters ### # Properties of source wavelength = sc.Quantity( '600 nm') # wavelengths at which to calculate reflectance # Geometric properties of sample particle_radius = sc.Quantity('0.130 um') # radius of the sphere particles volume_fraction_particles = sc.Quantity( 0.6, '') # volume fraction of the particles in the sphere boundary volume_fraction_bulk = sc.Quantity( 0.55, '') # volume fraction of the spheres in the bulk film sphere_boundary_diameter = sc.Quantity(10, 'um') # diameter of the sphere boundary boundary = 'sphere' boundary_bulk = 'film' # Refractive indices n_particle = ri.n('vacuum', wavelength) # refractive index of particle n_matrix = ri.n('polystyrene', wavelength) # refractive index of matrix
def test_reflection_core_shell(): # test that the reflection of a non-core-shell system is the same as that # of a core-shell with a shell index matched with the core seed = 1 nevents = 60 ntrajectories = 30 # Reflection using a non-core-shell system R, T = calc_montecarlo(nevents, ntrajectories, radius, n_particle, n_sample, n_medium, volume_fraction, wavelen, seed) # Reflection using core-shells with the shell index-matched to the core radius_cs = sc.Quantity(np.array( [100, 150]), 'nm') # specify the radii from innermost to outermost layer n_particle_cs = sc.Quantity(np.array( [1.5, 1.5]), '') # specify the index from innermost to outermost layer # calculate the volume fractions of each layer vf_array = np.empty(len(radius_cs)) r_array = np.array([0] + radius_cs.magnitude.tolist()) for r in np.arange(len(r_array) - 1): vf_array[r] = (r_array[r + 1]**3 - r_array[r]**3) / (r_array[-1:]**3) * volume_fraction n_sample_cs = ri.n_eff(n_particle_cs, n_matrix, vf_array) R_cs, T_cs = calc_montecarlo(nevents, ntrajectories, radius_cs, n_particle_cs, n_sample_cs, n_medium, volume_fraction, wavelen, seed) assert_almost_equal(R, R_cs) assert_almost_equal(T, T_cs) # Outputs before refactoring structcol R_before = 0.81382378303119451 R_cs_before = 0.81382378303119451 T_before = 0.1861762169688054 T_cs_before = 0.1861762169688054 assert_almost_equal(R_before, R, decimal=15) assert_almost_equal(R_cs_before, R_cs, decimal=15) assert_almost_equal(T_before, T, decimal=15) assert_almost_equal(T_cs_before, T_cs, decimal=15) # Test that the reflectance is the same for a core-shell that absorbs (with # the same refractive indices for all layers) and a non-core-shell that # absorbs with the same index # Reflection using a non-core-shell absorbing system n_particle_abs = sc.Quantity(1.5 + 0.001j, '') n_sample_abs = ri.n_eff(n_particle_abs, n_matrix, volume_fraction) R_abs, T_abs = calc_montecarlo(nevents, ntrajectories, radius, n_particle_abs, n_sample_abs, n_medium, volume_fraction, wavelen, seed) # Reflection using core-shells with the shell index-matched to the core n_particle_cs_abs = sc.Quantity(np.array([1.5 + 0.001j, 1.5 + 0.001j]), '') n_sample_cs_abs = ri.n_eff(n_particle_cs_abs, n_matrix, vf_array) R_cs_abs, T_cs_abs = calc_montecarlo(nevents, ntrajectories, radius_cs, n_particle_cs_abs, n_sample_cs_abs, n_medium, volume_fraction, wavelen, seed) assert_almost_equal(R_abs, R_cs_abs, decimal=6) assert_almost_equal(T_abs, T_cs_abs, decimal=6) # Outputs before refactoring structcol R_abs_before = 0.3956821177047554 #A:0.40749467236951037 #A/V:0.50534237684703909 R_cs_abs_before = 0.39568211770416667 # A:0.4074946723689386 #A/V:0.50534237684642402 T_abs_before = 0.009944245822685388 #A:0.0053095057615145302 #A/V:0.017215194324142709 T_cs_abs_before = 0.009944245822595715 #A:0.0053095057614589471 #A/V:0.017215194324029608 assert_almost_equal(R_abs_before, R_abs, decimal=3) assert_almost_equal(R_cs_abs_before, R_cs_abs, decimal=3) assert_almost_equal(T_abs_before, T_abs, decimal=3) assert_almost_equal(T_cs_abs_before, T_cs_abs, decimal=3) # Same as previous test but with absorbing matrix as well # Reflection using a non-core-shell absorbing system n_particle_abs = sc.Quantity(1.5 + 0.001j, '') n_matrix_abs = sc.Quantity(1. + 0.001j, '') n_sample_abs = ri.n_eff(n_particle_abs, n_matrix_abs, volume_fraction) R_abs, T_abs = calc_montecarlo(nevents, ntrajectories, radius, n_particle_abs, n_sample_abs, n_medium, volume_fraction, wavelen, seed) # Reflection using core-shells with the shell index-matched to the core n_particle_cs_abs = sc.Quantity(np.array([1.5 + 0.001j, 1.5 + 0.001j]), '') n_sample_cs_abs = ri.n_eff(n_particle_cs_abs, n_matrix_abs, vf_array) R_cs_abs, T_cs_abs = calc_montecarlo(nevents, ntrajectories, radius_cs, n_particle_cs_abs, n_sample_cs_abs, n_medium, volume_fraction, wavelen, seed) assert_almost_equal(R_abs, R_cs_abs, decimal=6) assert_almost_equal(T_abs, T_cs_abs, decimal=6) # Outputs before refactoring structcol R_abs_before = 0.27087005070007175 #A:0.29026980076407527 #A/V:0.37384878890851575 R_cs_abs_before = 0.27087005070007175 #A:0.29026980076407527 #A/V:0.37384878890851575 T_abs_before = 0.0006391960305096798 #A:0.0002140495990985143 #A/V:0.002180700021951509 T_cs_abs_before = 0.0006391960305096798 #A:0.0002140495990985143 #A/V:0.002180700021951509 assert_almost_equal(R_abs_before, R_abs, decimal=2) assert_almost_equal(R_cs_abs_before, R_cs_abs, decimal=2) assert_almost_equal(T_abs_before, T_abs, decimal=4) assert_almost_equal(T_cs_abs_before, T_cs_abs, decimal=4)
.. moduleauthor:: Victoria Hwang <*****@*****.**> .. moduleauthor:: Vinothan N. Manoharan <*****@*****.**> """ import structcol as sc from .. import montecarlo as mc from .. import detector as det from .. import refractive_index as ri import numpy as np from numpy.testing import assert_equal, assert_almost_equal import pytest # Define a system to be used for the tests nevents = 3 ntrajectories = 4 radius = sc.Quantity('150 nm') volume_fraction = 0.5 n_particle = sc.Quantity(1.5, '') n_matrix = sc.Quantity(1.0, '') n_medium = sc.Quantity(1.0, '') n_sample = ri.n_eff(n_particle, n_matrix, volume_fraction) angles = sc.Quantity(np.linspace(0.01, np.pi, 200), 'rad') wavelen = sc.Quantity('400 nm') # Index of the scattering event and trajectory corresponding to the reflected # photons refl_index = np.array([2, 0, 2]) def test_sampling(): # Test that 'calc_scat' runs
def calc_sphere_mc(): # caculate the effective index of the sample n_sample = ri.n_eff(n_particle, n_matrix, volume_fraction_particles) # Calculate the phase function and scattering and absorption coefficients #from the single scattering model # (this absorption coefficient is of the scatterer, not of an absorber #added to the system) p, mu_scat, mu_abs = mc.calc_scat(particle_radius, n_particle, n_sample, volume_fraction_particles, wavelength) # Initialize the trajectories r0, k0, W0 = mc.initialize(nevents, ntrajectories, n_matrix_bulk, n_sample, boundary, sample_diameter=sphere_boundary_diameter) r0 = sc.Quantity(r0, 'um') k0 = sc.Quantity(k0, '') W0 = sc.Quantity(W0, '') # Create trajectories object trajectories = mc.Trajectory(r0, k0, W0) # Generate a matrix of all the randomly sampled angles first sintheta, costheta, sinphi, cosphi, _, _ = mc.sample_angles( nevents, ntrajectories, p) # Create step size distribution step = mc.sample_step(nevents, ntrajectories, mu_scat) # Run photons trajectories.absorb(mu_abs, step) trajectories.scatter(sintheta, costheta, sinphi, cosphi) trajectories.move(step) # Calculate reflection and transmition (refl_indices, trans_indices, _, _, _, refl_per_traj, trans_per_traj, _, _, _, _, reflectance_sphere, _, _, norm_refl, norm_trans) = det.calc_refl_trans(trajectories, sphere_boundary_diameter, n_matrix_bulk, n_sample, boundary, p=p, mu_abs=mu_abs, mu_scat=mu_scat, run_fresnel_traj=False, return_extra=True) return (refl_indices, trans_indices, refl_per_traj, trans_per_traj, reflectance_sphere, norm_refl, norm_trans) ### Calculate phase function and lscat ### # use output of calc_refl_trans to calculate phase function, mu_scat, # and mu_abs for the bulk p_bulk, mu_scat_bulk, mu_abs_bulk = pfs.calc_scat_bulk( refl_per_traj, trans_per_traj, trans_indices, norm_refl, norm_trans, volume_fraction_bulk, sphere_boundary_diameter, n_matrix_bulk, wavelength) return p_bulk, mu_scat_bulk, mu_abs_bulk
def test_phase_function_absorbing_medium(): # test that the phase function using the far-field Mie solutions # (mie.calc_ang_dist()) in an absorbing medium is the same as the phase # function using the Mie solutions with the asymptotic form of the spherical # Hankel functions but using a complex k (mie.diff_scat_intensity_complex_medium() # with near_fields=False) wavelen = sc.Quantity('550 nm') radius = sc.Quantity('105 nm') n_matrix = sc.Quantity(1.47 + 0.001j, '') n_particle = sc.Quantity(1.5 + 1e-1 * 1.0j, '') m = index_ratio(n_particle, n_matrix) x = size_parameter(wavelen, n_matrix, radius) k = 2 * np.pi * n_matrix / wavelen ksquared = np.abs(k)**2 ## Integrating at the surface of the particle # with mie.calc_ang_dist() (this is how it's currently implemented in # monte carlo) diff_cscat_par_ff, diff_cscat_perp_ff = \ model.differential_cross_section(m, x, angles, volume_fraction, structure_type='glass', form_type='sphere', diameters=radius, wavelen=wavelen, n_matrix=n_sample, k=None, distance=radius) cscat_total_par_ff = model._integrate_cross_section( diff_cscat_par_ff, 1.0 / ksquared, angles) cscat_total_perp_ff = model._integrate_cross_section( diff_cscat_perp_ff, 1.0 / ksquared, angles) cscat_total_ff = (cscat_total_par_ff + cscat_total_perp_ff) / 2.0 p_ff = (diff_cscat_par_ff + diff_cscat_perp_ff) / (ksquared * 2 * cscat_total_ff) p_par_ff = diff_cscat_par_ff / (ksquared * 2 * cscat_total_par_ff) p_perp_ff = diff_cscat_perp_ff / (ksquared * 2 * cscat_total_perp_ff) # with mie.diff_scat_intensity_complex_medium() diff_cscat_par, diff_cscat_perp = \ model.differential_cross_section(m, x, angles, volume_fraction, structure_type='glass', form_type='sphere', diameters=radius, wavelen=wavelen, n_matrix=n_sample, k=k, distance=radius) cscat_total_par = model._integrate_cross_section(diff_cscat_par, 1.0 / ksquared, angles) cscat_total_perp = model._integrate_cross_section(diff_cscat_perp, 1.0 / ksquared, angles) cscat_total = (cscat_total_par + cscat_total_perp) / 2.0 p = (diff_cscat_par + diff_cscat_perp) / (ksquared * 2 * cscat_total) p_par = diff_cscat_par / (ksquared * 2 * cscat_total_par) p_perp = diff_cscat_perp / (ksquared * 2 * cscat_total_perp) # test random values of the phase functions assert_almost_equal(p_ff[3], p[3], decimal=15) assert_almost_equal(p_par_ff[50], p_par[50], decimal=15) assert_almost_equal(p_perp[83], p_perp_ff[83], decimal=15) ### Same thing but with a binary and polydisperse mixture ## Integrating at the surface of the particle # with mie.calc_ang_dist() (this is how it's currently implemented in # monte carlo) radius2 = sc.Quantity('150 nm') concentration = sc.Quantity(np.array([0.2, 0.7]), '') pdi = sc.Quantity(np.array([0.1, 0.1]), '') diameters = sc.Quantity( np.array([radius.magnitude, radius2.magnitude]) * 2, radius.units) diff_cscat_par_ff, diff_cscat_perp_ff = \ model.differential_cross_section(m, x, angles, volume_fraction, structure_type='polydisperse', form_type='polydisperse', diameters=diameters, pdi=pdi, concentration=concentration, wavelen=wavelen, n_matrix=n_sample, k=None, distance=diameters/2) cscat_total_par_ff = model._integrate_cross_section( diff_cscat_par_ff, 1.0 / ksquared, angles) cscat_total_perp_ff = model._integrate_cross_section( diff_cscat_perp_ff, 1.0 / ksquared, angles) cscat_total_ff = (cscat_total_par_ff + cscat_total_perp_ff) / 2.0 p_ff2 = (diff_cscat_par_ff + diff_cscat_perp_ff) / (ksquared * 2 * cscat_total_ff) p_par_ff2 = diff_cscat_par_ff / (ksquared * 2 * cscat_total_par_ff) p_perp_ff2 = diff_cscat_perp_ff / (ksquared * 2 * cscat_total_perp_ff) # with mie.diff_scat_intensity_complex_medium() diff_cscat_par, diff_cscat_perp = \ model.differential_cross_section(m, x, angles, volume_fraction, structure_type='polydisperse', form_type='polydisperse', diameters=diameters, pdi=pdi, concentration=concentration, wavelen=wavelen, n_matrix=n_sample, k=k, distance=diameters/2) cscat_total_par = model._integrate_cross_section(diff_cscat_par, 1.0 / ksquared, angles) cscat_total_perp = model._integrate_cross_section(diff_cscat_perp, 1.0 / ksquared, angles) cscat_total = (cscat_total_par + cscat_total_perp) / 2.0 p2 = (diff_cscat_par + diff_cscat_perp) / (ksquared * 2 * cscat_total) p_par2 = diff_cscat_par / (ksquared * 2 * cscat_total_par) p_perp2 = diff_cscat_perp / (ksquared * 2 * cscat_total_perp) # test random values of the phase functions assert_almost_equal(p_ff2[3], p2[3], decimal=15) assert_almost_equal(p_par_ff2[50], p_par2[50], decimal=15) assert_almost_equal(p_perp2[83], p_perp_ff2[83], decimal=15)
""" import structcol as sc from structcol import montecarlo as mc from structcol import refractive_index as ri from structcol import event_distribution as ed from structcol import detector as det import numpy as np from numpy.testing import assert_equal, assert_almost_equal, assert_array_less # Monte Carlo parameters ntrajectories = 30 # number of trajectories nevents = 300 # number of scattering events in each trajectory # source/detector properties wavelength = sc.Quantity(np.array(550), 'nm') # wavelength at which to run simulation # sample properties particle_radius = sc.Quantity('140 nm') # radius of the particles volume_fraction = sc.Quantity(0.56, '') # volume fraction of particles thickness = sc.Quantity('10 um') particle = 'ps' matrix = 'air' boundary = 'film' # indices of refraction n_particle = ri.n( 'polystyrene', wavelength) # refractive indices can be specified as pint quantities or n_matrix = ri.n( 'vacuum',
def calc_sigma(volume_fraction, radius, thickness, Sample, ntrajectories, nevents, run_num=100, plot=True, seed=None): """ Calculates the standard deviation of the multiple scattering calculations by running the multiple scattering code run_num times. Parameters ---------- volume_fraction : float volume fraction of scatterer in the system radius : float (in nm) radius of scatterer thickness : float (in um) film thickness of sample Sample : Sample object contains information about the sample that produced data ntrajectories : int number of trajectories for the multiple scattering calculations nevents : int number of scattering events for the multiple scattering calculations run_num : int or 100 number of runs from which to calculate the standard deviation plot : boolean If True, plot of the theoretical reflectance and transmittance uncertainties seed : int or None If seed is int, the simulation results will be reproducible. If seed is None, the simulation results are random. Returns ---------- sigma_r : ndarray standard deviation of the reflectance calculation sigma_t : ndarray standard deviation of the transmittance calculation """ wavelength = sc.Quantity(Sample.wavelength, 'nm') reflectance = np.zeros([run_num, len(wavelength)]) transmittance = np.zeros([run_num, len(wavelength)]) for n in np.arange(run_num): reflectance[n, :], transmittance[n, :] = calc_refl_trans( volume_fraction, radius, thickness, Sample, ntrajectories, nevents, seed) # Calculate mean and standard deviations of reflectance and transmittance sigma_r = np.std(reflectance, axis=0) * np.sqrt( len(wavelength) / (len(wavelength) - 1)) sigma_t = np.std(transmittance, axis=0) * np.sqrt( len(wavelength) / (len(wavelength) - 1)) mean_r = np.mean(reflectance, axis=0) mean_t = np.mean(transmittance, axis=0) #np.savetxt(os.path.join(os.getcwd(),'sigma.txt'), np.array([sigma_R, sigma_T]).T) if plot == True: # Plot the mean and standard deviations fig, (ax_r, ax_t) = plt.subplots(2, figsize=(8, 10)) ax_r.set(ylabel='Reflectance') ax_t.set(ylabel='Transmittance') ax_t.set(xlabel='Wavelength (nm)') ax_r.errorbar(wavelength.magnitude, mean_r, yerr=sigma_r, fmt='.') ax_t.errorbar(wavelength.magnitude, mean_t, yerr=sigma_t, fmt='.') ax_r.set( title= 'Theoretical reflectance and transmittance +/- 1 standard deviation' ) return (sigma_r, sigma_t)