kklmn / xrt

Package xrt (XRayTracer) is a python software library for ray tracing and wave propagation in x-ray regime. It is primarily meant for modeling synchrotron sources, beamlines and beamline elements.
MIT License
80 stars 29 forks source link

ParabolicalMirrorParam optics with figure error #169

Closed leahwang1962 closed 2 weeks ago

leahwang1962 commented 7 months ago

Hi, For ParabolicalMirrorParam, do I need to write def xyz_to_param() to over write the default one? My ParabolicalMirrorParam optic figure error is measured as Z at each surface point (x,y) (real Z - theoretical Z). Do you have a formula in hand to get local_r_distorted, local_n_distorted base on the measured (x,y,z)? Thanks.

kklmn commented 7 months ago

Hi,

I have found my old example of applying NOM measurements to a parametric mirror and extracted it to a file examples/withRaycing/13_Warping/ExampleOfSlopeErrorMapToParametricMirror.py in this repository.

It defines a distorted parametric mirror, with the distortions given in a 3-column file as (x, y, z). You should adapt it to your file structure and your definitions of x, y, z in the file.

import numpy as np
from scipy import ndimage

import xrt.backends.raycing.oes as roe

class EllipticalMirrorParamNOM(roe.EllipticalMirrorParam):
    def __init__(self, *args, **kwargs):
        kwargs = self.__pop_kwargs(**kwargs)
        super().__init__(*args, **kwargs)
        self.nom_read()

    def __pop_kwargs(self, **kwargs):
        self.waviness = kwargs.pop('figureError')  # file name
        return kwargs

    def nom_read(self):
        # here, the file self.waviness has this structure:
        # x[mm]  y[mm]  z[nm]
        # 30.00  29.75  -31.498373
        # 30.50  29.75  -32.992258
        # 31.00  29.75  -33.864061
        # 31.50  29.75  -34.240630
        # ...
        xL, yL, zL = np.loadtxt(self.waviness, unpack=True)
        nX = (yL == yL[0]).sum()
        nY = (xL == xL[0]).sum()
        x = xL[:nX]
        y = yL[::nX]
        z = zL.reshape((nY, nX))
        dx = x[1] - x[0]
        dy = y[1] - y[0]
        b, a = np.gradient(z)  # NOM x is along (our y) and y is across (our x)
        # a, b = np.gradient(z)  # NOM x is across and y is along
        a = np.arctan(a/dy)
        b = np.arctan(b/dx)
        self.nom_rmsA = ((a**2).sum() / (nX * nY))**0.5
        self.nom_rmsB = ((b**2).sum() / (nX * nY))**0.5
        self.nom_splineZ = ndimage.spline_filter(z.T) * 1e-6  # mm to nm
        self.nom_splineA = ndimage.spline_filter(a.T) * 1e-6  # rad to µrad
        self.nom_splineB = ndimage.spline_filter(b.T) * 1e-6  # rad to µrad
        self.nom_nX = nX
        self.nom_nY = nY
        self.nom_x = x
        self.nom_y = y

    def local_r_distorted(self, s, phi):
        r = self.local_r(s, phi)
        x, y, z = self.param_to_xyz(s, phi, r)
        # if NOM x is along (our y) and y is across (our x):
        coords = np.array([
            (y/(self.nom_x[-1]-self.nom_x[0]) + 0.5) * (self.nom_nX-1),
            (x/(self.nom_y[-1]-self.nom_y[0]) + 0.5) * (self.nom_nY-1)])
        # coords.shape = (2, self.nrays)
        z += ndimage.map_coordinates(self.nom_splineZ, coords, prefilter=True)
        s1, phi1, r1 = self.xyz_to_param(x, y, z)
        return r1 - r

    def local_n_distorted(self, s, phi):
        r = self.local_r(s, phi)
        x, y, z = self.param_to_xyz(s, phi, r)
        # if NOM x is along (our y) and y is across (our x):
        coords = np.array([
            (y/(self.nom_x[-1]-self.nom_x[0]) + 0.5) * (self.nom_nX-1),
            (x/(self.nom_y[-1]-self.nom_y[0]) + 0.5) * (self.nom_nY-1)])
        # coords.shape = (2, self.nrays)
        a = ndimage.map_coordinates(self.nom_splineA, coords, prefilter=True)
        b = ndimage.map_coordinates(self.nom_splineB, coords, prefilter=True)
        return -a, -b

For the visualization of your NOM results you may want to examine the main example in the same folder.

leahwang1962 commented 7 months ago

It's really helpful. Greatly appreciate.

leahwang1962 commented 6 months ago

Hello Need more help to understand the simulation results. With a parabolic cylindrical mirror, when I copied the waveness figure error from warp.py, the results is correct. Then I modified the waveness figure error from cos function(only along ray direction) to a square wave function. I don't understand why there is bright line in the middle. I am expecting it should be a dark line as the other 2 dark lines, because it is the location with the biggest local_r_distorted and local_n_distorted just like the other 2 locations. Thanks.

with cos function figure error: image image with square wave function figure error: image image