Conversation
For opening files from Woolam m2000 ellipsometers, exported as text files
Advice from Andy Co-Authored-By: Andrew Nelson <andyfaff@users.noreply.github.com>
Sellmeier was modifying the passed wavelength array in-place, which produced confusing behaviour.
Delta is in polar coordinates (0-360˚), this needs to be taken into account when calculating error. This problem is most obvious when data is 359˚ and model is 1˚ (for example). Old implementation calculates residual as 358, whereas the correct residual is 2. The change calculates error as: 2*arcsin(sin(Delta_data/2)-sin(Delta_model/2)) The factor of 2 ensures a functional relationship between error and Delta. The maximum possible residual (unweighted) is 180, which is of the same magnitude as before the change.
I had extended the silicon RI model so that it had a larger spectral range. This was causing some tests to fail, as the WVASE model we are comparing to used the old Si model.
Residuals are now calculated by self.residuals, so that the tests now pass and circular delta error is taken into account.
Cauchy, load_materials and RI are now imported from refellips.dispersion
This time with -180 flag
|
|
||
| def open_M2000file(fname, dropdatapoints=1): | ||
| """ | ||
| Open and load in an Accurion EP4 formmated data file. |
There was a problem hiding this comment.
| Open and load in an Accurion EP4 formmated data file. | |
| Open and load in an Accurion EP4 formatted data file. |
| return time_dict | ||
|
|
||
|
|
||
| def open_FilmSenseFile(fname): |
|
|
||
|
|
||
| def open_woolam_time_series(fname, take_every=1): | ||
| df = pd.read_csv( |
|
@igresh, working locally you can add a remote as You can then create a feature branch off of 0ef6cdc, push that feature branch to igresh/refellips, then make a PR against the Things this PR needs:
|
|
|
||
| psi, delta = self.model(wavelength_aoi) | ||
| return np.r_[psi - psi_d, delta - delta_d] | ||
| delta_err = 2 * np.rad2deg( |
There was a problem hiding this comment.
If you want to do this calculation, we should probably do it in ReflectModelSE.
There was a problem hiding this comment.
Actually, scrub that. What we're working out here is the residual for delta, which you're effectively changing to:
2 * asin(sin(delta/2) - sin(delta_d/2))
? Why is the residual for delta worked out this way? Do you have a reference?
There was a problem hiding this comment.
Delta is measured in degrees; as such, the difference between ∆=359 and ∆=1 should be 2˚. In the old implementation, it was 358˚.
In this method, the delta values are transformed such that 180˚=1, and 0/360˚=0. The residual is calculated, then rescaled such that the magnitude is the same as a simple difference.
I haven't managed to find a reference for the 'correct' way to calculate the residual, but I can demonstrate why the new method makes more sense than the old method by way of example (& comparison to WVASE).
I haven't convinced myself that this is the best way of calculating the residual - will do more searching.
There was a problem hiding this comment.
Some sort of circular modulus
There was a problem hiding this comment.
For reference, the problem with the old approach is:
wavelength = np.linspace(400,900,100)
si = load_material("silicon")
sio2 = load_material("silica")
polymer = Cauchy(A=1.39, B=0.0005)
solvent = Cauchy(A=1.45, B=0.0005)
polymer_layer_1 = polymer(50)
polymer_layer_1.vfsolv.setp(value=0.5)
polymer_layer_2 = polymer(50)
polymer_layer_2.vfsolv.setp(value=0.6)
struc_1 = solvent() | polymer_layer_1 | sio2(20) | si()
struc_1.solvent = solvent
struc_2 = solvent() | polymer_layer_2 | sio2(20) | si()
struc_2.solvent = solvent
model_1 = ReflectModelSE(struc_1)
model_2 = ReflectModelSE(struc_2)
psi1, delta1 = model_1(np.c_[wavelength, np.ones_like(wavelength) * 70])
psi2, delta2 = model_2(np.c_[wavelength, np.ones_like(wavelength) * 70])

@igresh