예제 #1
0
파일: undulator.py 프로젝트: marcocamma/sr
    def as_srw(self, gap="min", energy=None, harmonic=1, **kwargs):
        use_srw = kwargs.get("use_srw", False)
        if energy is not None:
            pars = self.find_harmonic_and_gap(energy,
                                              sort_harmonics=True,
                                              use_srw=use_srw)[0]
            gap = pars["gap"]
            harmonic = pars["harmonic"]

        if isinstance(gap, str) and gap == "min":
            gap = self.min_gap
        import srwlib

        ebeam = beam.srw_ebeam(self.ebeam)
        harmB = srwlib.SRWLMagFldH()  # magnetic field harmonic
        harmB.n = harmonic  # harmonic number
        harmB.h_or_v = "v"  # magnetic field plane: horzontal ('h') or vertical ('v')
        harmB.B = self.field(gap=gap)  # magnetic field amplitude [T]
        und = srwlib.SRWLMagFldU([harmB])
        und.per = self.period / 1e3  # period length [m]
        und.nPer = self.N  # number of periods (will be rounded to integer)
        magFldCnt = srwlib.SRWLMagFldC(
            [und],
            srwlib.array("d", [0]),
            srwlib.array("d", [0]),
            srwlib.array("d", [0]),
        )  # Container of all magnetic field elements
        return ds(ebeam=ebeam, und=und, field=magFldCnt)
예제 #2
0
파일: undulator.py 프로젝트: marcocamma/sr
 def as_gsm(self,
            gap="min",
            energy=None,
            harmonic=1,
            distance=None,
            **kwargs):
     use_srw = kwargs.get("use_srw", False)
     if energy is not None:
         pars = self.find_harmonic_and_gap(energy,
                                           sort_harmonics=True,
                                           use_srw=use_srw)[0]
         gap = pars["gap"]
         harmonic = pars["harmonic"]
     b = self.photon_beam_characteristics(gap=gap,
                                          harmonic=harmonic,
                                          **kwargs)
     gsmh = GSM_Numeric(rms_size=b.sh,
                        rms_cl=b.gsm_sclh,
                        wavelen=b.wavelength * 1e-10)
     gsmv = GSM_Numeric(rms_size=b.sv,
                        rms_cl=b.gsm_sclv,
                        wavelen=b.wavelength * 1e-10)
     if distance is not None:
         gsmh = gsmh.propagate(distance)
         gsmv = gsmv.propagate(distance)
     return ds(h=gsmh, v=gsmv)
예제 #3
0
파일: p10.py 프로젝트: marcocamma/sr
def propagate(b0, aperture=20e-6, as_numpy=True):

    # optics is at 85, from source
    zb = free_space(85)

    # equivalent FL = 2.13
    F = lens(2.13)

    a = gaussian_aperture(aperture)
    δ = sympy.Symbol("δ", real=True)
    za = free_space(2.13 + δ)

    b1 = b0.apply(zb)
    div = b0.divergence.evalf() * 1e6
    print(f"Divergence @ source: {div:.2f} (μrad)")
    b2 = b1.apply(a)
    b3 = b2.apply(F)
    s1 = b1.rms_size.evalf() * 1e6
    s2 = b2.rms_size.evalf() * 1e6
    print(f"RMS size before aperture: {s1:.2f} (μm)")
    print(f"RMS size after  aperture: {s2:.2f} (μm)")
    b4 = b3.apply(za)
    if as_numpy:
        size = b4.rms_size.evalf()
        size = sympy.lambdify(size.free_symbols, size)
        cl = b4.rms_cl.evalf()
        cl = sympy.lambdify(cl.free_symbols, cl)
    return ds(size=size, cl=cl)
예제 #4
0
파일: beam.py 프로젝트: marcocamma/sr
def e_beam(lattice="EBS"):
    if isinstance(lattice, str):
        lattice = lattice.casefold()
        if not lattice in _e_lattices:
            raise KeyError(
                f"Can't find {lattice} in database; available lattices are (not case sensitive): {str(list(_e_lattices.keys()))}"
            )
        e = ds(_e_lattices[lattice].copy())
    else:
        e = ds(lattice.copy())
        lattice = "source"
    e.emitth = e.sh * e.divh
    e.emittv = e.sv * e.divv
    e.betah = e.sh / e.divh
    e.betav = e.sv / e.divv
    e.name = lattice
    return e
예제 #5
0
    def find_best_set_for_focal_length(self,
                                       energy=8,
                                       focal_length=10,
                                       accuracy_needed=0.1,
                                       verbose=False,
                                       beam_fwhm=None):
        """
        find lensset that has a certain focal_length (within accuracy_needed)
        and transmission if beam_fwhm is provided (and sort_by_transmission is
        """
        fl = self._focal_lengths(energy)
        delta_fl = np.abs(fl - focal_length)
        if np.isnan(focal_length):
            idx_best = 0
            idx_good = np.zeros_like(fl, dtype=bool)
            idx_good[0] = True
        elif fl[np.isfinite(fl)].max() < focal_length * 5:
            idx_best = 0
            idx_good = np.ones_like(fl, dtype=bool)
        else:
            idx_best = np.argmin(delta_fl)
            idx_good = delta_fl < accuracy_needed
        if idx_good.sum() == 0:
            if verbose:
                print(
                    f"Could not find good set within required accuracy ({accuracy_needed:.3f}); will try with factor 2 bigger"
                )
            return self.find_best_set_for_focal_length(
                energy=energy,
                focal_length=focal_length,
                accuracy_needed=2 * accuracy_needed,
                verbose=verbose)

        good_lensets = self.all_sets[idx_good]
        transmission = [
            g.transmission_central_ray(energy) for g in good_lensets
        ]
        if beam_fwhm is not None:
            transmission_gauss_beam = [
                g.transmission_gaussian_beam(energy, gauss_beam_fwhm=beam_fwhm)
                for g in good_lensets
            ]
            idx_best = np.argmax(transmission_gauss_beam)
        else:
            idx_best = np.argmin(delta_fl[idx_good])
            transmission_gauss_beam = None
        # deep copying best_lens_set in case it is modified as return value
        ret = ds(
            in_out=self.all_confs[idx_good][idx_best],
            focal_length=self.all_sets[idx_good][idx_best].focal_length(
                energy),
            best_lens_set=copy.deepcopy(self.all_sets[idx_good][idx_best]),
            all_delta_fl=delta_fl,
            good_lensets=self.all_sets[idx_good],
            transmission_central_ray=transmission[idx_best],
        )
        return ret
예제 #6
0
파일: beam.py 프로젝트: marcocamma/sr
def ebs_minibeta(beta_h=6.8, beta_v=2.7):
    eps_h = 130e-12
    eps_v = 10e-12
    return ds(sh=np.sqrt(eps_h * beta_h),
              divh=np.sqrt(eps_h / beta_h),
              sv=np.sqrt(eps_v * beta_v),
              divv=np.sqrt(eps_v / beta_v),
              ebeam_energy=6,
              sr_cur=0.2,
              rms_energy_spread=0.00094,
              name="minibeta")
예제 #7
0
파일: mirror.py 프로젝트: marcocamma/sr
def find_mirror(
    E,
    mirrors=id18_mirrors,
    reflectivity=0.8,
    angles=np.linspace(2.5e-3, 4.5e-3, 201),
    reduction_factor=0.95,
    max_3E_reflectivity=1e-5,
    verbose=True,
):
    """ mirrors should be ordered from lighter to heavier element """
    for mirror in mirrors:
        r = np.empty_like(angles)
        r3 = np.empty_like(angles)
        for i, a in enumerate(angles):
            r[i] = mirror.reflectivity2(E, a)
            r3[i] = mirror.reflectivity2(E * 3, a)
        idx1 = r > reflectivity
        idx3 = r3 < max_3E_reflectivity
        if (idx1 & idx3).sum() > 0:
            idx = np.argwhere(idx1 & idx3).ravel()[-1]  # take biggest angle
            return ds(
                surface=mirror.material_name,
                angle=angles[idx],
                two_bounces_reflectivity=r[idx],
                two_bounces_reflectivity_3E=r3[idx],
            )
    if verbose:
        print(
            f"Could not find any mirror with reflectivity@E>{reflectivity:.2f} and reflectivity@3E < {max_3E_reflectivity:.3e}"
            )
        print(
            f"For {mirrors[-1].material_name}, max of reflectivity@E {r.max():.3e} and min reflectivity@3E {r3.min():.3e}"
            )
        print(f"will try with reflectivity = {reflectivity*reduction_factor:.2f}")
    if reflectivity < 0.01:
        # start new search with higher max_3E_reflectivity
        return find_mirror(
            E,
            mirrors=mirrors,
            reflectivity=1,
            angles=angles,
            max_3E_reflectivity=max_3E_reflectivity*10,
            verbose=verbose,
        )
    else:
        return find_mirror(
            E,
            mirrors=mirrors,
            reflectivity=reflectivity * reduction_factor,
            angles=angles,
            max_3E_reflectivity=max_3E_reflectivity,
            verbose=verbose,
        )
예제 #8
0
def beamsize_mono_vibrations(focus_dist=200,
                             mono_dist=40,
                             lens_dist=60,
                             rms_vibration=0.1e-6,
                             source_rms_size=5e-6,
                             source_rms_div=5e-6,
                             lens_rms_aperture=300e-6,
                             z=None):
    """  based on ttp://dx.doi.org/10.1107/S16005775160111881 
         Parameters
         ----------
         mono_dist : float
             distance from mono to source
    """
    # eq 10
    zi = focus_dist - lens_dist
    if z is None: z = np.linspace(zi - 10, zi + 10, 1001)
    zo = lens_dist
    zm = mono_dist
    beam_size = zi / zo * np.sqrt(source_rms_size**2 +
                                  (2 * zm * rms_vibration)**2)
    beam_size_no_vibration = zi / zo * source_rms_size

    A = lens_rms_aperture
    B = lens_dist * source_rms_div
    #
    r = zi / zo
    r2 = (zi / zo)**2
    position_focus_from_lens = zi - zi * (2 * rms_vibration *
                                          zm)**2 * (1 / A**2 * r2 + 1 / B**2 *
                                                    (r2 + r - zi / zm))
    position_focus = position_focus_from_lens + lens_dist
    # eq 4
    c_z = z / zo - (zo - zm) * (z - zi) / (zm * zi * (1 + B**2 / A**2))
    s_0_z_squared = source_rms_size**2 * r2 + (z / zi - 1)**2 / (1 / A**2 +
                                                                 1 / B**2)
    s_z_squared = s_0_z_squared + (2 * zm * rms_vibration *
                                   c_z)**2 / (1 + (2 * rms_vibration *
                                                   (zo - zm))**2 /
                                              (A**2 + B**2))
    s_z = np.sqrt(s_z_squared)

    ret = ds(
        rms_beamsize_at_dist=beam_size,
        fwhm_beamsize_at_dist=beam_size * 2.35,
        fractional_change_of_beamsize=(beam_size - beam_size_no_vibration) /
        beam_size_no_vibration,
        focus_position=position_focus,
        fraction_change_of_focus_position=(position_focus - focus_dist) /
        focus_dist,
        z=z + lens_dist,
        rms_beamsize=s_z)
    return ret
예제 #9
0
 def f(z, z0=30, F=30):
     if isinstance(z, (float, int)):
         z = np.asarray([z])
     res = dict()
     for bname in beams.keys():
         res[bname] = dict()
         for name in attrs.keys():
             res[bname][name] = dict()
             y = np.zeros_like(z)
             idx = z < z0
             y[idx] = funcs[bname][name]["before"](z[idx]) * 1e6
             y[~idx] = funcs[bname][name]["after"](z0, F,
                                                   z[~idx] - z0) * 1e6
             res[bname][name] = y
     return ds(res)
예제 #10
0
파일: coherent.py 프로젝트: marcocamma/sr
    def __init__(self,
                 photon_beam=None,
                 sh=None,
                 sv=None,
                 divh=None,
                 divv=None,
                 wavelength=None):
        if photon_beam is None:
            photon_beam = srbeam.Photon_Beam()
        if sh is None:
            sh = photon_beam.sh
        if sv is None:
            sv = photon_beam.sv
        if divh is None:
            divh = photon_beam.divh
        if divv is None:
            divv = photon_beam.divv
        emitth = sh * divh
        emittv = sv * divv
        if wavelength is None:
            wavelength = photon_beam.wavelength
        if wavelength > 1e-6:
            wavelength = wavelength * 1e-10

        beam = ds(
            sh=sh,
            sv=sv,
            divh=divh,
            divv=divv,
            emitth=emitth,
            emittv=emittv,
            wavelength=wavelength,
        )
        self.beam = beam

        k = 2 * np.pi / wavelength
        # source coherence length ξ_{Sx,y}
        self.sclh = 2 * sh / np.sqrt(4 * k**2 * emitth**2 - 1)  # eq 33
        self.sclv = 2 * sv / np.sqrt(4 * k**2 * emittv**2 - 1)  # eq 33
        self.qh = self.sclh / sh
        self.qv = self.sclv / sv
        self.cofh = self.qh / np.sqrt(4 + self.qh**2)
        self.cofv = self.qv / np.sqrt(4 + self.qv**2)

        # cldiv = coherence length divergence
        self.cldivh = 1 / (2 * k * sh) * np.sqrt(4 + self.qh**2)
        self.cldivv = 1 / (2 * k * sv) * np.sqrt(4 + self.qv**2)
예제 #11
0
파일: beam.py 프로젝트: marcocamma/sr
 def rms_size_at_dist(self, D=100):
     sh = _sqrt_squares(self.sh, self.divh * D)
     sv = _sqrt_squares(self.sv, self.divv * D)
     divh = self.divh
     divv = self.divv
     return ds(sh=sh, sv=sv, divh=divh, divv=divv)
예제 #12
0
파일: scan.py 프로젝트: marcocamma/openlab
def _general_scan(motors=None,positions=None,acquire=None,fname=None,force=False):
    """
    general porpouse scanning macro

    Parameters
    ----------
    motors : list|tuple
        each item should be a motor/stage
    positions : 2D arrayable
        2D array scan_point,num_mot
    acquire : function
        a function that must be provided, it must return a dictionary with
        items to pack as output
        the keys starting with _ will only be included one (and not in all
        scan points)
        example: def read(): time.sleep(1); return dict(val=3,_x=np.arange(10))
        like data structure
    """

    if fname is None:
        print("Must give fname")
        return

    # motors must be a list
    if not isinstance(motors,Iterable): motors = [motors,]

    # positions bust be 2D-like array
    positions = np.asarray( positions )
    if positions.ndim == 1: positions = positions[:,np.newaxis]

    info = ds()
    info.num_motors = len(motors)
    info.motors = [m.mne for m in motors]
    info.motors_paramters = ds()
    for m in motors:
        info.motors_paramters[m.mne] = m.get_info_str()

    info.positions = np.squeeze(positions)
    info.npoints_per_axis = _get_npoints(positions)
    info.time_start = now()


    # ensure we are using pathlib
    fname = pathlib.Path(fname)

    # check if file exists
    if fname.exists() and not force:
        print("File %s exists, returning"%fname)
        return
    else:
        fname.parent.mkdir(exist_ok=True,parents=True)
        print("Will save in",str(fname))

    data_buffer = []
    # can't use enumerate because of tqdm missing support (at least for 4.11)
    for iscan in tqdm.trange(len(positions)):
        acquire_positions = positions[iscan]

        tosave = ds(info=info)
        # first axis is scan points
        tosave.positions = np.squeeze(positions[:iscan+1])
        tosave.npoints_per_axis = _get_npoints(positions[:iscan+1])

        # ask to move motors to positions
        for motor,position in zip(motors,acquire_positions):
            motor.move(position)

        # wait until they arrive
        for motor in motors: motor.wait()

        _data = acquire()
        data_buffer.append( _data )

        for key in _data:
            if key[0] == "_": continue
            tosave[key] = np.asarray( [data[key] for data in data_buffer] )
            tosave[key] = np.squeeze( tosave[key] )
        keys_tosave_once = list(_data.keys())
        keys_tosave_once = [key for key in keys_tosave_once if key[0] == "_"]

        for key in keys_tosave_once:
            tosave[key.strip("_")] = _data[key]
        tosave.info.time_last_save = now()

        tosave.save(str(fname))
    return tosave
예제 #13
0
def propagate(
    beam=id18h,
    optics=[[40, "x1", "coll"], [150, "x1", "focus@200"]],
    z=np.arange(0, 230, 0.5),
    use_transfocator=True,
    transfocator=transfocator,
    fixed_f=None,
    fname=None,
    force=False,
):
    """ 
    beam is a GSM or a GSM_Numeric beam

    optics = [
        [ pos1, aperture1, coll|flat|focus@dist|sizeUM@dist,None ],
        [ pos2, aperture2, coll|flat|focus@dist|sizeUM@dism,None ],
        [ .................................... ],
        ]

    sizeUM@dist means that FL to get as close as possible to UM um  at a certain
    distance will be calculated (and used)


    if optics element starts with 'crl_', a CRL lens set will be used.
    The use of lenses can be 'forced' using use_transfocator=True
    transfocator is either an istance of crl.Transfocator or a dictionary of 
    transfocator instances (key is the distance from source)

    fixed_f is to take into account constrains in one direction (e.g. h)
    imposed by having fixed focal length or lenses in v direction
        It should be a dictionary of focal length or CRL lenset (key is distance)

    aperture can be an:
      - absolute value, "x1.2" is 1.2 times the FWHM CL, coherence length, None

    """
    print(
        "WARNING: hard apertures are implemented as shown in Vartanyans 2013 JSR paper"
    )
    print("         They are an approximations (valid in the far field?)")

    if not force and fname is not None and os.path.isfile(fname):
        data = datastorage.read(fname)
        return data

    info = ds()

    energy = 12.398 / (beam.wavelen * 1e10)

    positions = [0]
    apertures = [None]
    focal_lengths = [None]
    desired_focal_lengths = [None]
    lenses = [None]
    beams_before_aperture_before_optics = [beam]
    beams_after_aperture_before_optics = [beam]
    beams_after_aperture_after_optics = [beam]
    log = ["source"]

    for i, o in enumerate(optics, start=1):
        _log = []
        _log.append(f"Working on optics element {i}")
        _pos, _aperture, _element = o

        if isinstance(_element, (int, float)):
            fl = _element
            _element = ""  # to make if statements below happy
            _log.append(f"Explicitly asked to use {fl:.3f} focal_length")

        if _element is not None and _element.startswith("crl_"):
            _use_transfocator = True
            _element = _element[:4]
            _log.append(
                "Will use CRL for this element because element name starts with crl_"
            )
        else:
            _use_transfocator = False

        _use_transfocator = _use_transfocator or use_transfocator

        positions.append(_pos)

        dpos = _pos - positions[i - 1]

        # babo = before aperture, before optics
        babo = beams_after_aperture_after_optics[-1].propagate(dpos)

        ### WORK ON APERTURE ###
        # aabo = after aperture, before optics
        if _aperture is None:
            aabo = babo
            apertures.append(None)
            _log.append("no aperture for this element")
        else:
            if isinstance(_aperture, str):
                # "x1.2"
                aperture_as_fraction_of_cl = float(_aperture[1:])
                _aperture = aperture_as_fraction_of_cl * babo.rms_cl * 2.35
                _log.append(
                    f"aperture defined as {aperture_as_fraction_of_cl:.2f} of FWHM CL {babo.rms_cl * 2.35:.2e} m"
                )
            _log.append(f"aperture of {_aperture:.2e} m")
            apertures.append(_aperture)
            _aperture = hard_aperture(_aperture)
            aabo = babo.apply(_aperture)

        ### WORK ON FOCUSING OPTICS ###
        # aaao = after aperture, after optics
        if _element is None:
            aaao = aabo
            focal_lengths.append(None)
            desired_focal_lengths.append(None)
            lenses.append(None)
            _log.append(f"No focusing optics for this element")
        else:
            if fixed_f is not None and _pos in fixed_f:
                c = fixed_f[_pos]
                _log.append("Adding constrain from other direction:" + str(c))
                if isinstance(c, (float, int)):
                    c = lens(c)
                aabo = aabo.apply(c)
            if _element[:4].lower() == "coll":
                fl = aabo.radius
                _log.append(
                    f"Asked for collimating, will try to use focal length = radius of curvature {fl:.3e} m"
                )
            if _element[:5].lower() == "focus":
                where_to_focus = float(_element.split("@")[1])
                dist_to_focus = where_to_focus - _pos
                _log.append(
                    f"Asked for focusing at {where_to_focus:.3f} m from source (meaning {dist_to_focus:.3f} m from optical element)"
                )
                fl = find_fl(aabo, dist_to_focus)
                _log.append(f"Found the FL needed: {fl:.3f} m")
            if _element[:4].lower() == "size":
                size, where = _element[4:].split("@")
                size = float(size) * 1e-6
                dist = float(where) - _pos
                _log.append(
                    f"Asked for imaging beam to a size of {size:.3e} m at a distance of {float(where):.3f} m (meaning {float(dist):.3f} m from optical element)"
                )
                fl = find_fl_to_get_size(aabo, dist, size)
                _log.append(f"Found the FL needed: {fl:.3f} m")
            desired_focal_lengths.append(fl)
            if _use_transfocator:
                _log.append(
                    f"Using transfocator, finding best combination for FL {fl:.3f} m"
                )
                if not isinstance(transfocator, Transfocator):
                    _transfocator = transfocator[_pos]
                else:
                    _transfocator = transfocator
                ret_transf = _transfocator.find_best_set_for_focal_length(
                    energy=energy,
                    focal_length=fl,
                    accuracy_needed=min(fl / 1000, 0.1),
                    beam_fwhm=None,
                )
                fl = ret_transf.focal_length
                _log.append(
                    f"Using transfocator, found set with FL of {fl:.2f} m")
                _log.append(
                    f"Using transfocator, using set {str(ret_transf.best_lens_set)}"
                )
                fl_obj = ret_transf.best_lens_set
                _log.append(fl_obj)
                lenses.append(fl_obj)
            else:
                lenses.append(fl)
                fl_obj = lens(fl)
            focal_lengths.append(fl)
            if fl == np.inf:
                aaao = aabo
            else:
                aaao = aabo.apply(fl_obj)
        beams_before_aperture_before_optics.append(babo)
        beams_after_aperture_before_optics.append(aabo)
        beams_after_aperture_after_optics.append(aaao)
        log.append(_log)
        print("\n".join([l for l in _log if isinstance(l, str)]))
    positions = np.asarray(positions)
    info = ds(
        log=log,
        inputs=optics,
        optics_positions=positions,
        apertures=apertures,
        focal_lengths=focal_lengths,
        desired_focal_lengths=desired_focal_lengths,
        beams_before_aperture_before_optics=beams_before_aperture_before_optics,
        beams_after_aperture_before_optics=beams_after_aperture_before_optics,
        beams_after_aperture_after_optics=beams_after_aperture_after_optics,
        lenses=lenses)

    size = np.zeros_like(z)
    cl = np.zeros_like(z)
    gdc = np.zeros_like(z)
    radius = np.zeros_like(z)

    for i, zi in enumerate(z):
        print(f"calculating {i}/{len(z)}", end="\r")
        # calc which beam to use
        div = np.floor_divide(positions, zi)
        temp_idx = np.ravel(np.argwhere(div == 0))
        if len(temp_idx) == 0:
            idx = 0
        else:
            idx = temp_idx[-1]
        beam = beams_after_aperture_after_optics[idx]
        dpos = zi - positions[idx]
        b = beam.propagate(dpos)
        size[i] = b.rms_size * 2.35 * 1e6
        cl[i] = b.rms_cl * 2.35 * 1e6
        gdc[i] = b.global_degree_of_coherence
        radius[i] = b.radius
    divergence = np.gradient(size, z)
    ret = ds(
        z=z,
        divergence=divergence,
        fwhm_size=size,
        fwhm_cl=cl,
        global_degree_of_coherence=gdc,
        radius=radius,
        info=info,
    )
    if fname is not None:
        ret.save(fname)
    return ret
예제 #14
0
import numpy as np
import datastorage
from datastorage import DataStorage as ds

## create storage ##

# empty
data = ds()

# from a dict
data = ds(dict(key1='value1'))

# with keywords arguments
data = ds(key1=3)

## adding stuff ... ##

# as if it is a dict
data['key2'] = 1234

# this is even nicer ... ;)
data.key3 = 34

# addidng another key (by default it will converted to a DataStorage instance if possible)
data.key4 = dict(key4_1=3, key4_2="ciao")

print("is key4 a datastorage ?", isinstance(data.key4, ds))

# can also handle lists/tuples ...
data.key5 = [1, 2, 3]
예제 #15
0
    def calc_focusing_GSM(
        self,
        gsm=DEFAULT_GSM,
        source_distance=None,
        slit_opening=4e-3,
        verbose=True,
    ):
        """ Based on Singer&Vartanyans JSR 2014 """
        if source_distance is None:
            gsm_at_lens = gsm
        else:
            gsm_at_lens = gsm.propagate(source_distance)
        if isinstance(gsm_at_lens, GSM): gsm_at_lens.evalf()

        input_rms_beam = float(gsm_at_lens.rms_size)
        input_R = float(gsm_at_lens.radius)
        input_cl = float(gsm_at_lens.rms_cl)

        energy = wavelength_to_energy(gsm_at_lens.wavelen * 1e10)
        k = 2 * np.pi / gsm_at_lens.wavelen

        # calculate lens opening (called Ω in paper)
        gauss_slit_opening = slit_opening / 4.55
        aperture = min(self.gaussian_aperture(), gauss_slit_opening)
        abs_opening = self.absorption_opening(energy)
        lens_effective_aperture = _calc_sum_square_inverse(
            [aperture, abs_opening])

        fl = self.focal_length(energy)
        # tilde_ are values at lens
        tilde_Sigma = _calc_sum_square_inverse(
            [input_rms_beam, lens_effective_aperture])
        tilde_R = _calc_sum_inverse([input_R, -fl])
        tilde_cl = input_cl  # Coherence length is not modified by the lens
        # gdc = global_degree_of_coherence
        tilde_gdc = 1 / np.sqrt(1 + (2 * tilde_Sigma / tilde_cl)**2)

        Z_L = 2 * k * tilde_Sigma**2 * tilde_gdc

        # distance_at which it will focus (1/a+1/b=1/f)
        focus_distance = -tilde_R / (1 + (tilde_R / Z_L)**2)
        focus_rms_size = tilde_Sigma / np.sqrt(1 + (Z_L / tilde_R)**2)
        focus_cl = tilde_cl / np.sqrt(1 + (Z_L / tilde_R)**2)
        rayleigh_range = 4 * k * focus_rms_size**2 * tilde_gdc

        t = self.transmission_central_ray(energy)

        if isinstance(gsm_at_lens, GSM):
            beam_at_focus = GSM(
                wavelen=gsm_at_lens.wavelen,
                rms_size=focus_rms_size,
                rms_cl=focus_cl,
                auto_apply_evalf=gsm_at_lens.auto_apply_evalf,
            )
        else:
            beam_at_focus = GSM_Numeric(
                wavelen=gsm_at_lens.wavelen,
                rms_size=focus_rms_size,
                rms_cl=focus_cl,
            )

        gsm_at_lens = beam_at_focus.propagate(-focus_distance)

        res = ds(focal_length=fl,
                 focus_distance=focus_distance,
                 fwhm_unfocused=input_rms_beam * 2.35,
                 fwhm_at_waist=focus_rms_size * 2.35,
                 cl_fwhm_at_waist=focus_cl * 2.35,
                 rayleigh_range=rayleigh_range,
                 energy=energy,
                 lens_set=self.lens_set,
                 transmission_central_ray=t,
                 gsm_at_focus=beam_at_focus,
                 gsm_at_lens=gsm_at_lens)

        if verbose:
            print("FWHM   @ lens        : %.3e" % (input_rms_beam * 2.35))
            print("RMS    @ lens        : %.3e" % input_rms_beam)
            print("RMS CL @ lens        : %.3e" % input_cl)
            print("GDC    @ lens        : %.3e" %
                  float(gsm_at_lens.global_degree_of_coherence))
            print("--------------------------------")
            print("focus distance       : %.3e" % focus_distance)
            print("focal length         : %.3e" % fl)
            print("Lens set abs opening : %.3e" % abs_opening)
            print("Lens set opening     : %.3e" % lens_effective_aperture)
            print("beam FWHM after lens : %.3e" % (tilde_Sigma * 2.35))
            print("--------------------------------")
            print("FWHM   @ waist       : %.3e" % (focus_rms_size * 2.35))
            print("RMS    @ waist       : %.3e" % focus_rms_size)
            print("RMS CL @ waist       : %.3e" % focus_cl)
            print("GDC    @ waist       : %.3e" % tilde_gdc)
            print("rayleigh_range       : %.3e" % rayleigh_range)
        return res
예제 #16
0
파일: undulator.py 프로젝트: marcocamma/sr
    def srw_photon_flux_density(
        self,
        gap="min",
        energy=None,
        dist=30,
        h=[-0.6, 0.6],
        nh=13,
        v=[-0.5, 0.5],
        nv=11,
        e=[1, 30],
        ne=200,
        harmonic="auto",
        abs_filter=None,
        **kwargs,
    ):
        """
        Calculate intensity spectral density (ph/sec)
        Parameters
        ----------
        dist : float [m]
            distance from source
        h : (min,max) or value [mm]
            start/end of horizontal slit. If a single value the range -value/2,value/2 is used
        nh: int
            number of point in horizontal direction
        v : (min,max) or value [mm]
            start/end of vertical slit. If a single value the range -value/2,value/2 is used
 
        nv: int
            number of point in vertical direction
        e : [min,max] or value [keV]
            start/end of energy spectral_photon_flux.
        ne: int
            number of point in energy spectral_photon_flux. If e is single value, forced to 1
        harmonic : int|(min,max)|"auto"
            harmonic to consider, if auto it is autodetected based on energy range
            if int, only that harmonic is used
        abs_filter : None or object with calc_transmission(energy_kev)
                     method
        """
        import srwlib
        import srwlpy

        if energy is not None:
            pars = self.find_harmonic_and_gap(energy,
                                              sort_harmonics=True,
                                              use_srw=True)[0]
            gap = pars["gap"]
            # harmonic will be determined below

        if isinstance(gap, str) and gap == "min":
            gap = self.min_gap
        if isinstance(v, (float, int)):
            v = [-v / 2, v / 2]
        if isinstance(h, (float, int)):
            h = [-h / 2, h / 2]

        data = self.as_srw(gap=gap)

        if harmonic == "auto":
            harmonic_m = int(max(np.floor(e[0] / self.fundamental(gap=gap)),
                                 1))
            harmonic_M = int(max(np.ceil(e[1] / self.fundamental(gap=gap)), 1))
        elif isinstance(harmonic, int):
            harmonic_m = harmonic
            harmonic_M = harmonic
        else:
            harmonic_m, harmonic_M = harmonic

        if isinstance(e, (float, int)):
            if e == 0:
                e = self.photon_energy(gap=gap, harmonic=harmonic[0])
            e = [e, e]
            ne = 1
        elif isinstance(e, (tuple, list)) and e[0] < 0:
            e0 = self.photon_energy(gap=gap, harmonic=harmonic_m)
            e = [e0 + e[0], e0 + e[1]]

        calc_flux = nh == 1 and nv == 1

        arPrecF = [0] * 5  # for spectral flux vs photon energy
        arPrecF[0] = harmonic_m  # initial UR harmonic to take into account
        arPrecF[1] = harmonic_M  # final UR harmonic to take into account
        arPrecF[2] = 1.5  # longitudinal integration precision parameter
        arPrecF[3] = 1.5  # azimuthal integration precision parameter
        if calc_flux:
            arPrecF[4] = 1  # calculate flux (1) or flux per unit surface (2)
        else:
            arPrecF[4] = 2  # calculate flux (1) or flux per unit surface (2)

        stk = srwlib.SRWLStokes()  # for spectral_photon_flux
        shape = (ne, nh, nv)
        stk.allocate(
            *shape
        )  # numbers of points vs horizontal and vertical positions (photon energy is not taken into account)
        stk.mesh.zStart = dist  # longitudinal position [m] at which power density has to be calculated
        stk.mesh.xStart = h[0] / 1e3  # initial horizontal position [m]
        stk.mesh.xFin = h[1] / 1e3  # final horizontal position [m]
        stk.mesh.yStart = v[0] / 1e3  # initial vertical position [m]
        stk.mesh.yFin = v[1] / 1e3  # final vertical position [m]
        stk.mesh.eStart = e[0] * 1e3
        stk.mesh.eFin = e[1] * 1e3

        srwlpy.CalcStokesUR(stk, data.ebeam, data.und, arPrecF)
        e = np.linspace(e[0], e[1], ne)

        dh = h[1] - h[0]
        dv = v[1] - v[0]

        h = np.linspace(h[0], h[1], nh)
        v = np.linspace(v[0], v[1], nv)

        if calc_flux:
            spectral_photon_flux = np.asarray(stk.arS[:ne])
            spectral_photon_flux_density = (
                spectral_photon_flux[:, np.newaxis, np.newaxis] / dh / dv)
        else:
            spectral_photon_flux_density = np.reshape(stk.arS[:ne * nh * nv],
                                                      (nv, nh, ne))
            spectral_photon_flux_density = np.moveaxis(
                spectral_photon_flux_density, 2, 0)
            spectral_photon_flux = _integrate2d(h, v,
                                                spectral_photon_flux_density)

        if abs_filter is not None:
            t = abs_filter.calc_transmission(e)
            spectral_photon_flux_density *= t[:, np.newaxis, np.newaxis]
            spectral_photon_flux *= t
            abs_info = "absorption filter : " + str(abs_filter)
        else:
            abs_info = "no absorption filter"

        data = ds(
            energy=e,
            h=h,
            v=v,
            spectral_photon_flux_density=spectral_photon_flux_density,
            spectral_photon_flux=spectral_photon_flux,
            undulator_info=str(self),
            info=f"harmonic = {str(harmonic)}, " + abs_info,
            ebeam=self.ebeam,
        )
        data = _photon_flux_density_helper(data)
        # calculates more things ...

        return data
예제 #17
0
파일: undulator.py 프로젝트: marcocamma/sr
    def srw_power_density(
        self,
        gap="min",
        dist=30,
        energy=None,
        h=[-3, 3],
        nh=151,
        v=[-2, 2],
        nv=101,
        **kwargs,
    ):
        """
        Calculate power density (W/mm^2)
        Parameters
        ----------
        dist : float [m]
            distance from source
        h : [min,max] or gap [mm]
            start/end of horizontal slit. If a single value the range -gap/2,gap/2 is used
        nh: int
            number of point in horizontal direction
        v : [min,max] or gap [mm]
            start/end of vertical slit. If a single value the range -gap/2,gap/2 is used
        nv: int
            number of point in vertical direction
        """
        if energy is not None:
            pars = self.find_harmonic_and_gap(energy,
                                              sort_harmonics=True,
                                              use_srw=True)[0]
            gap = pars["gap"]
            harmonic = pars["harmonic"]
        if isinstance(gap, str) and gap == "min":
            gap = self.min_gap
        import srwlib
        import srwlpy

        if isinstance(v, (float, int)):
            v = [-v / 2, v / 2]
        if isinstance(h, (float, int)):
            h = [-h / 2, h / 2]

        data = self.as_srw(gap=gap)

        arPrecP = [0] * 5  # for power density
        arPrecP[0] = 1.5  # precision factor
        arPrecP[
            1] = 2  # power density computation method (1- "near field", 2- "far field")
        arPrecP[2] = (
            -self.length / 2
        )  # initial longitudinal position (effective if arPrecP[2] < arPrecP[3])
        arPrecP[3] = (
            self.length / 2
        )  # final longitudinal position (effective if arPrecP[2] < arPrecP[3])
        arPrecP[
            4] = 20000  # number of points for (intermediate) trajectory calculation

        stkP = srwlib.SRWLStokes()  # for power density
        shape = (1, nh, nv)
        stkP.allocate(
            *shape
        )  # numbers of points vs horizontal and vertical positions (photon energy is not taken into account)
        stkP.mesh.zStart = dist  # longitudinal position [m] at which power density has to be calculated
        stkP.mesh.xStart = h[0] / 1e3  # initial horizontal position [m]
        stkP.mesh.xFin = h[1] / 1e3  # final horizontal position [m]
        stkP.mesh.yStart = v[0] / 1e3  # initial vertical position [m]
        stkP.mesh.yFin = v[1] / 1e3  # final vertical position [m]
        #        stkP.mesh.eStart =
        #        stkP.mesh.eFin = 8000
        srwlpy.CalcPowDenSR(stkP, data["ebeam"], 0, data["field"], arPrecP)
        pd = np.asarray(stkP.arS[:nh * nv]).reshape((nv, nh))
        h = np.linspace(h[0], h[1], nh)
        v = np.linspace(v[0], v[1], nv)
        power_total = _integrate2d(h, v, pd)
        units_power_density = "W/mm2"
        units_power_total = "W"
        units_h = "mm"
        units_v = "mm"
        return ds(
            h=h,
            v=v,
            power_density=pd,
            power_density_max=pd.max(),
            power_total=power_total,
            units_power_density=units_power_density,
            units_power_density_max=units_power_density,
            units_power_total=units_power_total,
            units_h=units_h,
            units_v=units_v,
        )
예제 #18
0
파일: undulator.py 프로젝트: marcocamma/sr
    def xrt_photon_flux_density(
        self,
        gap="min",
        energy=None,
        dist=30,
        h=[-0.6, 0.6],
        nh=13,
        v=[-0.5, 0.5],
        nv=11,
        e=[1, 30],
        ne=200,
        harmonic="auto",
        abs_filter=None,
        **kwargs,
    ):
        """
        Calculate intensity spectral density (ph/sec)
        Parameters
        ----------
        dist : float [m]
            distance from source
        h : (min,max) or value [mm]
            start/end of horizontal slit. If a single value the range -value/2,value/2 is used
        nh: int
            number of point in horizontal direction
        v : (min,max) or value [mm]
            start/end of vertical slit. If a single value the range -value/2,value/2 is used
 
        nv: int
            number of point in vertical direction
        e : [min,max] or value [keV]
            start/end of energy spectral_photon_flux.
            if emin is neg it is interpreted as around harmonic[0]
            if e is float and == 0, the extact harmonic is used.
        ne: int
            number of point in energy spectral_photon_flux. If e is single value, forced to 1
        harmonic : int|list|None
            harmonic to consider, if auto it is autodetected based on energy range
            if int, only that harmonic is used
        """
        use_srw = kwargs.get("use_srw", False)
        if energy is not None:
            pars = self.find_harmonic_and_gap(energy,
                                              sort_harmonics=True,
                                              use_srw=use_srw)[0]
            gap = pars["gap"]
            harmonic = pars["harmonic"]

        if isinstance(e, (int, float)) and e != 0:
            e = [e, e]

        if harmonic == "auto":
            harmonic_m = int(max(np.floor(e[0] / self.fundamental(gap=gap)),
                                 1))
            harmonic_M = int(max(np.ceil(e[1] / self.fundamental(gap=gap)), 1))
            harmonic = range(harmonic_m, harmonic_M + 1)
        if isinstance(harmonic, int):
            harmonic = (harmonic, )
        else:
            pass

        if isinstance(v, (float, int)):
            v = [-v / 2, v / 2]
        if isinstance(h, (float, int)):
            h = [-h / 2, h / 2]
        if isinstance(e, (float, int)):
            if e == 0:
                e = self.photon_energy(gap=gap, harmonic=harmonic[0])
            e = [e, e]
            ne = 1
        elif isinstance(e, (tuple, list)) and e[0] < 0:
            e0 = self.photon_energy(gap=gap, harmonic=harmonic[0])
            print("Working around", e0)
            e = [e0 + e[0], e0 + e[1]]

        if nh == 1:
            nh = 2
        if nv == 1:
            nv = 2

        dh = h[1] - h[0]
        dv = v[1] - v[0]

        h = np.linspace(h[0], h[1], nh)
        v = np.linspace(v[0], v[1], nv)
        e = np.linspace(e[0], e[1], ne)

        theta = h * 1e-3 / dist
        psi = v * 1e-3 / dist

        dtheta = dh * 1e-3 / dist
        dpsi = dv * 1e-3 / dist

        u = self.as_xrt(gap=gap)

        spectral_photon_flux_density, *_ = u.intensities_on_mesh(
            energy=e * 1e3, theta=theta, psi=psi, harmonic=harmonic)
        # sum over harmonics
        spectral_photon_flux_density = spectral_photon_flux_density.sum(-1)

        # convert from flux per unit solid angle into flux per mm^2
        spectral_photon_flux_density *= (dtheta * dpsi) / (dh * dv)
        spectral_photon_flux_density = np.swapaxes(
            spectral_photon_flux_density, 1, 2)

        if abs_filter is not None:
            t = abs_filter.calc_transmission(e)
            spectral_photon_flux_density *= t[:, np.newaxis, np.newaxis]
            abs_info = "absorption filter : " + str(abs_filter)
        else:
            abs_info = "no absorption filter"

        # integrate2d works only if len(axis) > 1
        if nh == 1 or nv == 1:
            spectral_photon_flux = spectral_photon_flux_density * (dh * dv)
        else:
            spectral_photon_flux = _integrate2d(h, v,
                                                spectral_photon_flux_density)

        data = ds(
            energy=e,
            h=h,
            v=v,
            spectral_photon_flux_density=spectral_photon_flux_density,
            spectral_photon_flux=spectral_photon_flux,
            undulator_info=str(self),
            info=f"harmonic = {str(harmonic)}, " + abs_info,
            ebeam=self.ebeam,
        )
        data = _photon_flux_density_helper(data)
        # calculates more things ...
        return data
예제 #19
0
파일: beam.py 프로젝트: marcocamma/sr
 def get_ebeam(self):
     return ds(self.ebeam.copy())
예제 #20
0
파일: beam.py 프로젝트: marcocamma/sr
 def rms_cl_at_dist(self, dist=100):
     sh, dh = self.gsm_sclh, self.gsm_cldivh
     sv, dv = self.gsm_sclv, self.gsm_cldivv
     clh, clv = _sqrt_squares(sh, dh * dist), _sqrt_squares(sv, dv * dist)
     return ds(clh=clh, clv=clv, cldivh=dh, cldivv=dv)
예제 #21
0
    def calc_focusing(
        self,
        energy,
        distance=4,
        source_distance=150,
        fwhm_beam=500e-6,
        slit_opening=4e-3,
        verbose=True,
    ):
        # calculate effective beamsize
        input_rms_beam = fwhm_beam / 2.35
        gauss_slit_opening = slit_opening / 4.55  # see single&vartanyans JSR 2014
        aperture = min(self.gaussian_aperture(), gauss_slit_opening)
        absorption_ap = self.absorption_opening(energy)
        lens_effective_aperture = _calc_sum_square_inverse(
            [aperture, absorption_ap])
        rms_beam = _calc_sum_square_inverse(
            [input_rms_beam, lens_effective_aperture])

        fl = self.focal_length(energy)

        # distance_at which it will focus (1/a+1/b=1/f)
        focus_distance = source_distance * fl / (source_distance - fl)

        lam = energy_to_wavelength(energy) * 1e-10

        w_unfocused = rms_beam * 2
        # assuming gaussian beam divergence =
        # = w_unfocused/focus_distance we can obtain
        waist = lam / np.pi * focus_distance / w_unfocused
        waist_fwhm = waist * 2.35 / 2.0
        rayleigh_range = np.pi * waist**2 / lam
        size = waist * np.sqrt(1.0 + (distance - focus_distance)**2.0 /
                               rayleigh_range**2)
        fwhm_at_dist = size / 2 * 2.35
        t = self.transmission_central_ray(energy)
        res = ds(
            focal_length=fl,
            focus_distance=focus_distance,
            distance=distance,
            fwhm_at_dist=fwhm_at_dist,
            fwhm_unfocused=fwhm_beam,
            fwhm_at_waist=waist_fwhm,
            rayleigh_range=rayleigh_range,
            energy=energy,
            lens_set=self.lens_set,
            transmission_central_ray=t,
        )

        if verbose:
            print("beam FWHM @ lens     : %.3e" % (input_rms_beam * 2.35))
            print("Lens set opening     : %.3e" % lens_effective_aperture)
            print("beam FWHM after lens : %.3e" % (rms_beam * 2.35))
            print("------------------------")
            print("focus distance       : %.3e" % focus_distance)
            print("focal length         : %.3e" % fl)
            print("------------------------")
            print("waist                : %.3e" % waist)
            print("waist FWHM           : %.3e" % waist_fwhm)
            print("rayleigh_range       : %.3e" % rayleigh_range)
            print("------------------------")
            print("size @ dist          : %.3e" % size)
            print("size FWHM @ dist     : %.3e" % fwhm_at_dist)
        return res
예제 #22
0
파일: beam.py 프로젝트: marcocamma/sr
_e_lattices = ds(
    esrf_highb=ds(
        sh=387.80e-6,
        divh=10.31e-6,
        sv=5.43e-6,
        divv=1.84e-6,
        ebeam_energy=6.04,
        sr_cur=0.2,
        rms_energy_spread=0.001,
    ),
    ebs=ebs_minibeta(beta_h=6.8, beta_v=2.8),
    super_source=ds(
        sh=3e-6,
        divh=1e-6,
        sv=1.0e-6,
        divv=1e-6,
        ebeam_energy=6,
        sr_cur=0.2,
        rms_energy_spread=0.00094,
    ),
    jsr2019=ds(
        sh=4.47e-6,
        divh=2.24e-6,
        sv=4.47e-6,
        divv=2.24e-6,
        ebeam_energy=6,
        sr_cur=0.1,
        rms_energy_spread=0.001,
    ),
    jsr2019es0=ds(
        sh=4.47e-6,
        divh=2.24e-6,
        sv=4.47e-6,
        divv=2.24e-6,
        ebeam_energy=6,
        sr_cur=0.1,
        rms_energy_spread=0.000,
    ),
)