oasys-elettra-kit / OASYS1-Wiser

The implementation of WISER into OASYS
MIT License
0 stars 1 forks source link

Numerical source/Cross-link with SRW #9

Closed aljosahafner closed 3 years ago

aljosahafner commented 4 years ago

Placeholder for numerical source issue... @maltissimo @capitanevs

maltissimo commented 4 years ago

Hello, I started digging into the code to get out the data needed. I do not know how you guys store the electric field. In any event this is how I got thus far: if you run and SRW undulator source in OASYS, then connect a python script widget:

srw_beam=in_object_1

print(dir(srw_beam))

['_SRWDatasrw_beamline', '_SRWDatasrw_wavefront', 'class', 'delattr', 'dict', 'dir', 'doc', 'eq', 'format', 'ge', 'getattribute', 'gt', 'hash', 'init', 'init_subclass', 'le', 'lt', 'module', 'ne', 'new', 'reduce', 'reduce_ex', 'repr', 'setattr', 'sizeof', 'str', 'subclasshook', 'weakref', 'get_srw_beamline', 'get_srw_wavefront', 'get_working_srw_beamline', 'reset_working_srw_beamline', 'set_working_srw_beamline']

srw_wf=srw_beam._SRWData__srw_wavefront

print(dir(srw_wf)) ['Rx', 'Ry', 'ScanningData', 'class', 'delattr', 'dict', 'dir', 'doc', 'eq', 'format', 'ge', 'getattribute', 'gt', 'hash', 'init', 'init_subclass', 'le', 'lt', 'module', 'ne', 'new', 'reduce', 'reduce_ex', 'repr', 'setattr', 'sizeof', 'str', 'subclasshook', 'weakref', 'addE', 'allocate', 'arElecPropMatr', 'arEx', 'arEy', 'arMomX', 'arMomY', 'arWfrAuxData', 'avgPhotEn', 'calc_stokes', 'copy_comp', 'dRx', 'dRy', 'decorateSRWWF', 'delE', 'duplicate', 'fromGenericWavefront', 'get_1D_intensity_distribution', 'get_2D_intensity_distribution', 'get_dimension', 'get_flux', 'get_intensity', 'get_intensity_from_electric_field', 'get_phase', 'get_photon_energy', 'get_wavelength', 'mesh', 'numTypeElFld', 'partBeam', 'presCA', 'presFT', 'scanned_variable_data', 'setScanningData', 'toGenericWavefront', 'unitElFld', 'unitElFldAng', 'xc', 'yc']

These are all the methods available. Their definitions sits in srw_wavefront.py, which in turns is: wofrysrw.propagator.wavefront2D

Inside there, there are methods for converting wavefronts between numpy-digestible and SRW-data: SRWEFieldAsNumpy SRWWavefrontFromElectricField numpyArrayToSRWArray SRWArrayToNumpy

Please do tell me if this helps, or if you need further info.

capitanevs commented 4 years ago

Thanks, very helpful

Do we need a source only, or a source + a "continuation plane"? Can you also provide an example file, with a configured test source?

I do not work on OASYS-GUI right now, so I will need someone's assistance to extract the field.

@aljosahafner: can you do that, just throwing out a .txt? Guess that np.savetxt('outfile.txt', MyArray.view(complex)) would be fine. I think that the candidate function is SRWEFieldAsNumpy. Let's start with a distance of... 5m from the source.

Thanks.

maltissimo commented 4 years ago

You cannot have a source without a continuation plane: SRW calculates the field at a certain, specified distance. See attached ows file. SRW field data extraction.ows.zip

aljosahafner commented 4 years ago

After being generated (through GUI or script), the wavefront is accessible as:

import wofrysrw

srwData = in_object_1
srwWavefront = srwData._SRWData__srw_wavefront

efield = np.array(srwWavefront.arEx) # 1D array of E field with real, imaginary, real, etc. components

dim_x = srwWavefront.mesh.nx # Number of points - sampling
dim_y = srwWavefront.mesh.ny

re = np.array(efield[::2], dtype=np.float)
im = np.array(efield[1::2], dtype=np.float)

e = re + 1j * im # Make the array out of real and imaginary components 
e = e.reshape((dim_x, dim_y)) # If necessary, reshape into a regular grid

 # Access single wavelength, single polarization from the 4D array
asNpy = wofrysrw.propagator.wavefront2D.srw_wavefront.SRWEFieldAsNumpy(srwWavefront)[0][0, :, :, 0]
capitanevs commented 3 years ago

I inspected a little bit SRW data.

All the intensity profiles immagine

The averaged Intensity immagine

This seems to be what is shown in the "screen" widget.

immagine

I need info on what the widget that converts from 2D wavefront <--> 1d wavevront does. (by the way, where is it?).

As a general concept I guess it ahs the following parameters

aljosahafner commented 3 years ago

2D wavefront -> 1D wavefront widget is a part of Wofry widgets Tools package: https://github.com/oasys-kit/OASYS-WOFRY/tree/master/orangecontrib/wofry/widgets/tools

capitanevs commented 3 years ago

Ok, to complete the link b\w SRW and WISER we need the long awaited SourceNumeric. This is what I have in mind: immagine

I don't think we can plug "wofry2wiser" directly to wiser. WISER does need a "source" and I would be quite surprised if the direct link wored.

capitanevs commented 3 years ago

I have implemented SourceNumeric in Optics.py immagine

And its usage is (for example) immagine

The novelty is line88.

The present design of LibWiser is such that CoreOptics contain the geometric information and OpticalElement contains ComputationData, where the field is stored. A posteriori this is not that great, but line 88 just fills the computation data in the right way.

I am not still sure that it works PERFECTLY, but it runs and it returns nice plots

immagine

When you have time, can you start drafting the widget Numerical Source?

It can work in two ways:

I would prioritize the second, leaving just disabled input for the file.

What do you think?

aljosahafner commented 3 years ago

Please go through the code of the Wofry to Wiser widget a bit, there is a creation of a Dummy source in between.

capitanevs commented 3 years ago

Very interesting. Apparently Luca created a LibWiser-like DummyElement  that subclasses the GaussianSource and has a specific function for evaluating the field. This is  very good. However I do not expect it is gonna work: GaussianSource subclasses OpticsAnalytical, whereas here we need an OpticsNumerical. The propagation manager (in LibWiser) behaves in two different ways accprding to the kind of optical element. I guess that just by replacing SourceGaussian with SourceNumerical we can get things (almost) done. Shall we try? M Sidenote: for how Luca's code is done, DummyElement could inherit from any numerical optical element, e.g. a Mirror or tthe detector.

-- Inviato da myMail per Android Venerdì, 25 Giugno 2021, 09:05AM +02:00 da aljosahafner @.*** :

Please go through the code of the Wofry to Wiser widget a bit, there is a creation of a Dummy source in between.

capitanevs commented 3 years ago

So, if we link wofry2wiser---> Wiser detector we get something, but we should understand what.

immagine

immagine

immagine

capitanevs commented 3 years ago

Comment 1

The behavior of an optical element which is place at "0 distance" from the previous one does not appear to be so well defined in LibWiser

immagine

immagine

immagine

A still different behavior occurs if the positioning method is chosen.

This can not be a problem in the 95% of cases, since there is no need to put a detector at 0 distance from an optical element.

The first case I have encountered is the case of a "numeric source" (or wofry surce), where it comes as spontaneous to put a wiser detector widget downstream wofrywise widget, to see what is going on.

capitanevs commented 3 years ago

Comment2) How do we want wiser to be have with a layout like this? (probably nothing)

immagine

The reason of this (apparently) is simple: to make an effective test of the numerical source we would need the following layout

Source ---->detector ---->save field

load field --->numerical source ----> beamline

and compare it with

Soure --->beamline

(if not clear, I will explain it orally)

capitanevs commented 3 years ago

I see that there are writers and readers for wavefronts in wofry

immagine

Probably this is al what we need.

capitanevs commented 3 years ago
1. Wiser Numerical source is not tested yet
2. the best way to test it is in oasys: this needs to wire some tools =>
    in principle wiser2wofry+wofry_write + wofry read + wofry2wiser should be all that we needs

2b: once the pipeline is ok, we can play with making a gaussian source numerical, then propagate it, and check the differences.

3. In principle it would be nice if we had an "all wiser" numerical source with access from file
    => the advantage is the auto-generation of wiser code (supported at 80%)

    BUT

    This is not a priority

    BUT

    Let's keep this in mind.

    Field = sqrt(Intensity) * exp(1j*phase)

    def get_numerical_source(wofry_wavefront):

    Lambda = wofry_wavefront.get_wavelength() % => check the units

    L = wofry_wavefront.get_size() # checl

    Field = sqrt(wofry_wavefront.get_intensity()) * exkp(1j * wofry_wavefront.get_phase())

    # build the Numerical source here

     WiserNumericalSource = OpticalElement(
                                CoreOptics.SourceNumerical
                                    (....
                                    ...)
                                 PositioningDirectives(     )

                                 # CAUTION!: where to put the numerical source?
                                Numerical sources are not set at the waist z=0, but a little bit downstream, e.g. z=5 (numerical reasons).
                                This means that all the distances are offsetted.
                                CONSEQUENCE => we need to introduce an extra field called "Virtual Offset" or "Offset to real source waist" 
                                (suggestions on naming are welcome) that is then used by all the functions computing the distances 
                                in LibWiser I am doing it now

                                This also means that we need a new field in the conversion widget. Without this, all the distances would be screwed up.

    return Foundation.OpticalElement(Name="Wofry Source",
                                     IsSource=True,
                                     CoreOpticsElement=DummyElement(wofry_wavefront=wofry_wavefront),
                                     PositioningDirectives=Foundation.PositioningDirectives(ReferTo=Foundation.PositioningDirectives.ReferTo.AbsoluteReference,
                                                                                            XYCentre=[0.0, 0.0],
                                                                                            Angle=0.0))