Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linear polarization #29

Open
damienBloch opened this issue Apr 30, 2021 · 22 comments
Open

Linear polarization #29

damienBloch opened this issue Apr 30, 2021 · 22 comments

Comments

@damienBloch
Copy link

damienBloch commented Apr 30, 2021

Hi,

I've been using AtomECS a bit lately and I just had a question on how it is possible to handle linear polarization. In the doc it's mentionned that it should be possible to have a linear polarization by combining sigma_+/- beams, but I don't understand how that can be.

For example I'd like to consider the following situation: a magnetic field along e_z and a cooling light with wavevector along e_x linearly polarized along e_y. This light can induce sigma_+/- transition, but not pi.

However, when I look at the calculation for CalculateRateCoefficientsSystem, the term scatter3 for the pi component will always be non zero whatever the configuration I set for the polarization of the cooling lights.

What would be the correct way to handle this situation?

Thanks

@ElliotB256
Copy link
Collaborator

ElliotB256 commented Apr 30, 2021

I think the doc is incorrect in this case. The 'polarisation' is either +1 or -1 depending on whether the beam is sigma_+ or sigma_- polarised. The scatter1,2,3 components have prefactors which arise from transformation the E-field of the \sigma_\pm radiation into a frame aligned with the local B-field, and then expressing the field in that frame in terms of \sigma'_\pm, \pi' components (note the primed indices this time around)

We could add support for this - probably needs another field on the beam polarisation (the space for three polarisation components is 2D and so requires at least two numbers to describe all polarisation components, I think?)

@damienBloch
Copy link
Author

Could something like Jones vector be a solution to this ?

If we have two vectors e_x and e_y perpendicular to the wavevector, it's possible to define all fully polarized lights as a complex 2D vector pol = a * e_x + b * e_y with |a| ^ 2 + |b| ^ 2 = 1 .
But the issue would be how to get the vectors e_x and e_y in a consistent way for an arbitrary wavevector.

@damienBloch
Copy link
Author

In the end I couldn't find a satisfactory way to do it as previously described.

Therefore to implement arbitrary polarization I defined it as a normalized complex vector (polarization branch). With minor edits it passes the test, but it doesn't seem to be the most satisfactory solution because the user must ensure that the polarizatino is orthogonal to the wavector.

Also it seems that at some point it might be worth it to promote the polarization to a component.

Any suggestions about this ?

@ElliotB256
Copy link
Collaborator

but it doesn't seem to be the most satisfactory solution because the user must ensure that the polarizatino is orthogonal to the wavector

you could define private fields with a method used to create the struct? that would be a way to safeguard that the struct is in a 'valid' state when being used

Regarding polarization as a component, that could work. It seems like we may be moving towards a general consensus of working in the E-field domain, and then converting to intensities for calculations?

@MauriceZeuner
Copy link
Collaborator

Hi Damien, didn't notice you were here, that's super cool!

Nice coincidence! As Elliot mentioned, I am currently trying to put together a treatment in the complex E-field regime, where I use Jones-vectors as well. I ensure that they're orthogonal to the LinearGaussianEBeam::direction by creating them in constructors and having private fields.

Maybe another meeting on that to strategize would be nice?

@damienBloch
Copy link
Author

damienBloch commented May 12, 2021

Ok, I see. What puzzled me at the time with Jones vectors is that you need to choose a reference frame somewhat arbitrarly. But indeed adding methods to create the frame from the beam seems to do the trick.

Also I was wondering if you move to a E-field treatment, how would the polarization be handled when there are several beams intersecting, like in a MOT for example ?

@ElliotB256
Copy link
Collaborator

I think we probably need to distinguish between two regimes:

  • far-detuned/conversative dipole trap potentials. In this case the beams should interfere and the combined intensity should dictate the trapping potential (to allow eg lattices).
  • near-detuned/scattering force beams, such as in MOTs and slowers. In a real MOT there will be interference, e.g. polarisation gradients used in sub-doppler cooling methods. However, the current physical model of the scattering forces isn't sufficient to simulate these forces accurately*, so I think there is nothing to gain from considering interference of MOT beams on the current model. We could change the scattering model, and maybe we would consider such extensions in the future, but I think in the short term things like long-ranged rescattering forces are probably more useful to implement.

*although we consider the zeeman shift on the optical transitions, we don't really consider the zeeman sublevels for the purpose of atomic populations.

@MauriceZeuner
Copy link
Collaborator

MauriceZeuner commented May 12, 2021

I fully agree with the two regimes - especially since it would become hard to use our Rate Equation approach together with Intensity from superposed E-fields (We couldn't tell from which beam the photon comes). I talked with Ulrich about this very question (from a physics perspective only) and we came to the conclusion that it is probably easiest to explicitly tell AtomECS which beams interfere with each other.
Wild idea how that could look like without impeding performance too much:
Since we only need E-field treatment for interference effects (otherwise working in the intensity frame is completely sufficient and, of course faster) define a marker component like InterferenceOn which is attached to all Electric field gaussian beams that are wanted to do interference. The code in a system like SampleElectricFieldSystem then would first add complex E-fields for entities with InterferenceOn and store them in an ElectricFieldSampler . Then, SampleLaserIntesityGradientSystem would read ElectricFieldSampler, convert the result to an intensity gradient and then add it to the intensity of the beams without InterferenceOn. The result is stored in a LaserIntensityGradienSampler.

Do you think that would be suitable?

@ElliotB256
Copy link
Collaborator

Side note - Also worth bearing in mind the current approach, which is intensity based and does not have interference, has a 'one-to-N' concept in the sampler, because each atom must sample N beams. Allowing interference changes this - some beams must be combined, so an atom's sampler doesn't hold the values of N beams, but M fields?

@MauriceZeuner
Copy link
Collaborator

Absolutely, very important! I think either that (reducing from N laser to M "fields") could work or... Actually, why not still have N entries in ElectricFieldSampler where the ones corresponding to entities without InterferenceOn are zero (or not initialized) and let the interference happen in SampleLaserIntesityGradientSystem which would then still write N entries into the LaserIntensityGradientSampler but only "average" values for those entries corresponding to entities with InterferenceOn. It is actually important to still distinguish the beams/fields that have not interference because we need to know their frequency later for the polarizability.

Or... one could push it even another step further back and only add everything together at the Force level.

@MauriceZeuner
Copy link
Collaborator

@damienBloch : Just looking through your code, I have a few comments/questions, if that's okay:

  1. I think it is not necessary to hard-code a random vector to get an orthonormal system.

I would say it is safer to actually give two vectors as arguments for the function, one being the wavevector direction (= your `direction`) and another one that gives the initial direction of the E-field. Otherwise, you could end up getting a linear polarization in an arbitrary direction. And this is actually my biggest question: Since you use the direction of that (potentially) arbitrary polarization vector (which can be anywhere in the plane orthogonal to the wavevector) here

, to me, this looks dangerous. As far as I understand, you are calculating the dot-product of the sigma-pi vector (which is produced using the quantization vector) and your polarization vector, right? If this polarization vector is rather arbitrary in the beam-plane, so is the dot-product and hence its contribution to the rate, right? Could also be me being confused but I'd be happy to hear your response :)

One way to potentially solve this I came up with, is to introduce an extra component, that I called PolarizationReferenceFrame which then allows using two-dimensional Jones vectors which I am personally more used to (since we are in a plane perpendicular to the wave-vector). If you're interested, see: https://github.com/TeamAtomECS/AtomECS/blob/alternative_dipole_force/src/dipole/polarization.rs Have not yet touched the laser cooling physics, though.

  1. I think your question about choosing a reference frame is very important. Currently, AtomECS does it differently but I would personally think it might be more intuitive to switch to the convention to define polarization (and its handedness) in the frame of the beam (with respect to its wave vector). There are, however, arguments against that, for example, you'd have a potentially harder time deciding which Zeeman shift to "use". Something we should keep in mind/discuss.

  2. Because you need the direction to calculate the PolarizedLight, the CoolingLight component now does already contain this information implicitly. I think it would be cleaner to separate this - either into an own component or even two (like I am currently trying with the PolarisationReferenceFrame and JonesVector on the alternative-dipole-force branch).

  3. Have you tried this with an existing example file? Are the results like you would expect them? (Just curious) If so, did you find it hard to convert the "old" system of +1 and -1 and especially its reference frame to yours?

@ElliotB256
Copy link
Collaborator

Currently, AtomECS does it differently but I would personally think it might be more intuitive to switch to the convention to define polarization (and its handedness) in the frame of the beam (with respect to its wave vector)

@MauriceZeuner I think the current definition of polarisation in atomecs defines either sigma+ or sigma- with respect to the direction of propagation of the beam. To me that seems a sensible choice of frame. It means that for a 3D MOT you need two beams sigma+ (axial, outward field), and four beams sigma- (radial, inward field). The direction of the local B-field with respect to the beam propagation changes for these two cases, hence the need for a change in polarisation.

I think it would be cleaner to separate this ... into an own component or even two

When deciding on how many components to have, consider: (i) is the data always going to be used together (if so, one component makes more sense), and (ii) whether splitting the components will allow easier parallel execution due to read/write conflicts.

If the code were only laser cooling, polarisation would be best suited as a field on CoolingLight, but now there may be re-use (eg with dipole trap stuff) if we split. If we do split the components, we may also want to consider renaming CoolingLight, as the scope of that component may have changed - it is no longer all the info required to characterise something as light for cooling. I'm welcome to suggestions as what it should be called.

Two components (PolarisationReferenceFrame and JonesVector) is probably too many components - it sounds like they would always be used together?

@ElliotB256
Copy link
Collaborator

(also @MauriceZeuner - please post code as snippets rather than images, it makes it easier to copy/paste and index :) )

@damienBloch
Copy link
Author

So just to try to answer some of the points you raised @MauriceZeuner:

1: get_ortho_basis is only used to produce a circular polarization with respect to a wavector with PolarizedLight::circular_right(wavector). If the wavevector is along e_z, the circular right polarization can be e_x - i e_y, but any other e_x' - i e_y' (with the primes obtained by an arbitrary rotation along e_z) is also a valid circulra right polarization. I think that's why it's possible to use a random vector for the reference plane but the squared dot product remains invariant with respect to this choice.
The limitation with this is that it is not possible to add two circular polarizations to get a linear one in a know direction. That's why I added a utility to directly create a polarization vector parallel to a direction choosen by the user.

2-3: With this the polarization is defined as a vector in 3D space and can be independant of the wavector. It's the reponsability of the instantiation to choose the convention for the polarization. For example it's possible to create circular right light with respect to the wavector or the magnetic field with PolarizedLight::circular_right(wavector) or PolarizedLight::circular_right(magnetic_field). The issue with this is that it's possible to define unphysical light, which might be useful sometimes, but is indeed also probably dangerous.

4: I've tried this method in the cooling and rate tests and it passes them. In this case I had to replace +1 by PolarizedLight::circular_right(wavector) and -1 by PolarizedLight::circular_left(wavector)

However, in the end, having a reference frame for the beam as you did might be a better way to do it. For example, in addition to the polarization, it can also probably be used to define gaussian beams with elliptical intensity profile (as in issue #33), where you would also need to define a long and a short axis perpendicular to the wavevector.
Maybe this might be a reason to keep the reference frame as a separate component from the polarization?

@MauriceZeuner
Copy link
Collaborator

Hi Damien,

on a humorous note: It took me extremely long to notice that you (almost) consistently wrote "wavector" instead of "wavevector" and I wonder if we could make that the official spelling, please, it's a cool way to shorten things :D (Although I find it hard to pronounce that way ^^)

  1. I have to think about that again... still tiny bit sceptical but probably just my error in my thought process. What about linear polarization? In that case it's not allowed to rotate in the plane, right? And hence, dot-invariance is no longer given, I think. I think it's a good approach to have that second option but if I recall correctly, adding two 2-dimensional jones vectors representing sigma plus and sigma minus, actually always gives you linear polarization and would fulfil this requirement quite elegantly. Maybe another reason for reference-frame+2D JonesVector?

2-3. Is there really a realistic use-case for this? Otherwise, I'd probably go with safety and make the instantiation "fool-proof" and stick to the 2D Jones vector representation within a defined orthogonal plane, if that would be fine with you.

  1. great, also just tested your implementation and replacement strategy with a blue Sr-MOT example: everything like before, amazing!

Absolutely! @ElliotB256, Tiffany and I discussed the reference frame issue briefly on Friday. I think we also concluded that it makes sense to have a general Frame component in the laser module that can then be used for the polarization, but also for elliptical gaussian beams. We will likely re-structure the laser module a little since we now not only use lasers for laser cooling but for dipole trapping as well. That's why it's nice to make the polarisation a more general component instead of a sub-attribute of CoolingLight.

Since your implementation is similar in many ways to what I've been up to in the alternative-dipole-force, I think it makes sense to decide who does the compatible implementation of it that goes onto master later. I'm happy to do it since I will re-sort the laser module anyway (into separate laser, laser-cooling and dipole modules) but I'd leave that choice to you since it's also a nice way of getting started here. So feel free to decide if you want to spend some time on this, if you're interested :) (but absolutely no pressure, obviously)

We can always have a chat over video, if you preferred that :)

@ElliotB256
Copy link
Collaborator

2-3: I like @damienBloch 's suggestion. Being able to define 'unphysical' polarisations is not a problem if we provide a clear API for people to use. Generally, the component data layout should prioritize speed, and we should provide constructor methods to create valid structs.

4: I really like the syntax PolarizedLight::circular_right(wavector). One comment is that left/right is ambiguous - could we instead refer to them as sigma_plus and sigma_minus? My issue with right/left is it sounds like a direction rather than a rotation (I assume it's an informal shorthand for right-handed or left-handed, but even that conjures symmetry rather than rotation). Polarization sounds like a good component name, given that it will be attached to light entities.

Higher-level API?

PolarizedLight::circular_right(wavector) - the wavevector is redundant given that it is also in other beam related components, but it is required in this constructor to initialize the PolarizedLight component.

This discussion could lead to a higher-level API for constructing laser beams. We could define a factory struct for creating laser beam entities, which exposes a fluent interface to configure the new beam before it is inserted into the world. e.g.:

BeamBuilder()
   .in_direction(Vector3::x)
   .with_gaussian_profile(waist, power)
   .with_sigma_plus_polarisation()
   .for_cooling(Strontium)
   .with_detuning(-32.0)
   .build(&mut world);

The advantages of this are:

  • user doesn't need to know components required.
  • guarantees safe use/initialization of components.
  • potentially nicer, more readable code.
  • reduces redundancy for duplicate variables (e.g. wavevector)
  • nicer editor integration, e.g. autocomplete/prompting. Helpful for new atomecs users.

Disadvantages:

  • more code to maintain.
  • multiple ways to define things (though this can be alleviated by funnelling users towards higher-level functionality in an atomecs::prelude::* mod.

@ElliotB256
Copy link
Collaborator

I think it makes sense to decide who does the compatible implementation of it that goes onto master later.

I think Damien was on this issue first :) Maurice would you like to look into fluent interface/builder components and a higher-level API? The time I can spare in the next few days will be spent upgrading us to the newest specs package so that we can get back to the most recent toolchain and stop panicing :)

@MauriceZeuner
Copy link
Collaborator

I like the idea of a higher-level API! I think it absolutely makes sense to have something like that, although I think it's nice to keep the nature of modularity and configurability at least as an option. As long as it does not undermine the original idea, I would love if one could also have a more custom syntax (rather than many, many function names) like this (just as an example):

BeamBuilder()
   .in_direction(Vector3::x)
   .intersecting_at(Vector3::new(0.0,0.0,0.0))
   .with_profile(GaussianFull(waist, power, wavelength, ellipticity))
   .with_polarization(sigma_plus)
   .for_cooling(Strontium_blue)
   .with_detuning(-32.0)
   .build(&mut world);

Where, if I wanted it simpler, I could still insert a Gaussian(waist, power) (into line 3) that does have an infinite rayleigh range and and no ellipticity by default.

@MauriceZeuner
Copy link
Collaborator

Maurice would you like to look into fluent interface/builder components and a higher-level API?

Sure, when you say "look into", you mean coding a draft of it that we can then discuss? Or is there something preexisting in Rust/Specs that is commonly used for building APIs in a more general sense?

@MauriceZeuner
Copy link
Collaborator

@ElliotB256 is the API thing worth a new issue?

@damienBloch
Copy link
Author

Since your implementation is similar in many ways to what I've been up to in the alternative-dipole-force, I think it makes sense to decide who does the compatible implementation of it that goes onto master later.

I'm also happy to give it a try in the next days, but I wouldn't mind having a quick call at some point to get a few pointers.

@damienBloch
Copy link
Author

Sorry for the delay, got a bit sidetracked.

I made small changes to upgrade polarization to a component, based on the same formalism as I used before (vector-polarization branch).

It passes all the tests but I didn't correct the examples if clean up or further changes are needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants