From 860996bb60f2233f16b1b7d0ea98fc85e212d9ea Mon Sep 17 00:00:00 2001 From: VEZINET Didier Date: Mon, 2 Dec 2019 17:29:34 +0100 Subject: [PATCH 01/27] [Issue202] Improved CrystBragg docstrings for get_approx_detector_frame() and get_local_noute1e2() and fallback to self._DEFLAMB in self._checkformat_bragglamb() if nothing provided --- tofu/geom/_core_optics.py | 54 ++++++++++++++++++++++++++++++++++++++- tofu/version.py | 2 +- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/tofu/geom/_core_optics.py b/tofu/geom/_core_optics.py index e58138373..1c33654bd 100644 --- a/tofu/geom/_core_optics.py +++ b/tofu/geom/_core_optics.py @@ -729,6 +729,9 @@ def sample_outline_Rays(self, res=None): def _checkformat_bragglamb(self, bragg=None, lamb=None, n=None): lc = [lamb is not None, bragg is not None] + if not any(lc): + lamb = self._DEFLAMB + lc[0] = True assert np.sum(lc) == 1, "Provide lamb xor bragg!" if lc[0]: bragg = self.get_bragg_from_lamb(np.atleast_1d(lamb), @@ -856,7 +859,31 @@ def get_lamb_from_bragg(self, bragg, n=None): def get_approx_detector_frame(self, bragg=None, lamb=None, rcurve=None, n=None, plot=False): - """ See notes for details on notations """ + """ Return approximate ideal detector geometry + + Assumes infinitesimal and ideal crystal + Assumes detector center tangential to Rowland circle + Assumes detector center matching lamb (m) / bragg (rad) + + Detector described by center position, and (nout, ei, ej) unit vectors + By convention, nout = np.cross(ei, ej) + Vectors (ei, ej) define an orthogonal frame in the detector's plane + + Return: + ------- + det_cent: np.ndarray + (3,) array of (x, y, z) coordinates of detector center + det_nout: np.ndarray + (3,) array of (x, y, z) coordinates of unit vector + perpendicular to detector' surface + oriented towards crystal + det_ei: np.ndarray + (3,) array of (x, y, z) coordinates of unit vector + defining first coordinate in detector's plane + det_ej: np.ndarray + (3,) array of (x, y, z) coordinates of unit vector + defining second coordinate in detector's plane + """ # Check / format inputs if rcurve is None: @@ -891,6 +918,31 @@ def get_approx_detector_frame(self, bragg=None, lamb=None, return det_cent, det_nout, det_ei, det_ej def get_local_noute1e2(self, theta, psi): + """ Return (nout, e1, e2) associated to pts on the crystal's surface + + All points on the spherical crystal's surface are identified + by (theta, psi) coordinates, where: + - theta = np.pi/2 for the center + - psi = 0 for the center + They are the spherical coordinates from a sphere centered on the + crystal's center of curvature. + + Return the pts themselves and the 3 perpendicular unti vectors + (nout, e1, e2), where nout is towards the outside of the sphere and + nout = np.cross(e1, e2) + + Return: + ------- + summit: np.ndarray + (3,) array of (x, y, z) coordinates of the points on the surface + nout: np.ndarray + (3,) array of (x, y, z) coordinates of outward unit vector + e1: np.ndarray + (3,) array of (x, y, z) coordinates of first unit vector + e2: np.ndarray + (3,) array of (x, y, z) coordinates of second unit vector + + """ if np.allclose([theta, psi], [np.pi/2., 0.]): summit = self._dgeom['summit'] nout = self._dgeom['nout'] diff --git a/tofu/version.py b/tofu/version.py index ab0e46b95..57829d158 100644 --- a/tofu/version.py +++ b/tofu/version.py @@ -1,2 +1,2 @@ # Do not edit, pipeline versioning governed by git tags! -__version__ = '1.4.2-a5' +__version__ = '1.4.2-a5-2-g6065092' From 87899e8f054e8c82c4fa8c40d4fd0107bd3f4aea Mon Sep 17 00:00:00 2001 From: VEZINET Didier Date: Tue, 3 Dec 2019 14:09:07 +0100 Subject: [PATCH 02/27] [Issue202] Renamed CrystBragg.get_approx_detect() and started introducing ddist, dtheta, dpsi to optimize position --- tofu/geom/_core_optics.py | 68 +++++++++++++++++++++++---------------- tofu/geom/_plot_optics.py | 2 +- tofu/version.py | 2 +- 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/tofu/geom/_core_optics.py b/tofu/geom/_core_optics.py index 1c33654bd..7db519d73 100644 --- a/tofu/geom/_core_optics.py +++ b/tofu/geom/_core_optics.py @@ -857,8 +857,9 @@ def get_lamb_from_bragg(self, bragg, n=None): return _comp_optics.get_lamb_from_bragg(np.atleast_1d(bragg), self._dmat['d'], n=n) - def get_approx_detector_frame(self, bragg=None, lamb=None, - rcurve=None, n=None, plot=False): + def get_detector_frame(self, bragg=None, lamb=None, + rcurve=None, n=None, + dtheta=None, dpsi=None, ddist=None, plot=False): """ Return approximate ideal detector geometry Assumes infinitesimal and ideal crystal @@ -894,14 +895,25 @@ def get_approx_detector_frame(self, bragg=None, lamb=None, func = _comp_optics.get_approx_detector_rel det_dist, det_nout_rel, det_ei_rel = func(rcurve, bragg) + # Apply small corrections + if dtheta is None: + dtheta = 0. + if dpsi is None: + dpsi = 0. + if ddist is None: + ddist = 0. + det_dist += ddist + # Deduce absolute position in (x, y, z) - func = _comp_optics.get_det_abs_from_rel - det_cent, det_nout, det_ei, det_ej = func(det_dist, - det_nout_rel, det_ei_rel, - self._dgeom['summit'], - self._dgeom['nout'], - self._dgeom['e1'], - self._dgeom['e2']) + det_cent, det_nout, det_ei, det_ej = _comp_optics.get_det_abs_from_rel( + det_dist, det_nout_rel, det_ei_rel, + self._dgeom['summit'], + self._dgeom['nout'], self._dgeom['e1'], self._dgeom['e2']) + + if dtheta != 0. or dpsi != 0.: + det_nout = (np.cos(dpsi)*np.cos(dtheta)*det_nout + + np.cos(dpsi)*np.sin(dtheta)*det_ej + + np.sin(dpsi)*det_ei) if plot: dax = self.plot() @@ -1039,8 +1051,8 @@ def calc_phibragg_from_xixj(self, xi, xj, n=None, lc = [det_cent is None, det_ei is None, det_ej is None] assert all(lc) or not any(lc) if all(lc): - func = self.get_approx_detector_frame - det_cent, _, det_ei, det_ej = func(lamb=self._DEFLAMB) + det_cent, _, det_ei, det_ej = self.get_approx_detector_frame( + lamb=self._DEFLAMB) # Get local summit nout, e1, e2 if non-centered if theta is None: @@ -1050,17 +1062,17 @@ def calc_phibragg_from_xixj(self, xi, xj, n=None, summit, nout, e1, e2 = self.get_local_noute1e2(theta, psi) # Compute - func = _comp_optics.calc_braggphi_from_xixj - bragg, phi = func(xii, xjj, det_cent, det_ei, det_ej, - summit, -nout, e1, e2) + bragg, phi = _comp_optics.calc_braggphi_from_xixj( + xii, xjj, det_cent, det_ei, det_ej, + summit, -nout, e1, e2) if plot != False: - func = _plot_optics.CrystalBragg_plot_braggangle_from_xixj - lax = func(xi=xii, xj=xjj, - ax=ax, plot=plot, - bragg=bragg * 180./np.pi, - angle=phi * 180./np.pi, - braggunits='deg', angunits='deg', **kwdargs) + lax = _plot_optics.CrystalBragg_plot_braggangle_from_xixj( + xi=xii, xj=xjj, + ax=ax, plot=plot, + bragg=bragg * 180./np.pi, + angle=phi * 180./np.pi, + braggunits='deg', angunits='deg', **kwdargs) return bragg, phi def plot_johannerror(self, lamb=None): @@ -1078,10 +1090,10 @@ def plot_data_vs_lambphi(self, xi=None, xj=None, data=None, nxj = xj.size if xj is not None else np.unique(xjj).size # Compute lamb / phi - func = self.calc_phibragg_from_xixj - bragg, phi = func(xii, xjj, n=n, - det_cent=det_cent, det_ei=det_ei, det_ej=det_ej, - theta=theta, psi=psi, plot=False) + bragg, phi = self.calc_phibragg_from_xixj( + xii, xjj, n=n, + det_cent=det_cent, det_ei=det_ei, det_ej=det_ej, + theta=theta, psi=psi, plot=False) assert bragg.shape == phi.shape == data.shape lamb = self.get_lamb_from_bragg(bragg, n=n) @@ -1092,10 +1104,10 @@ def plot_data_vs_lambphi(self, xi=None, xj=None, data=None, spect1d = np.array([np.nanmean(data[ind==ii]) for ii in np.unique(ind)]) # plot - func = _plot_optics.CrystalBragg_plot_data_vs_lambphi - ax = func(xi, xj, bragg, lamb, phi, data, - lambfit=lambfit, phifit=phifit, spect1d=spect1d, - cmap=cmap, vmin=vmin, vmax=vmax, fs=fs) + ax = _plot_optics.CrystalBragg_plot_data_vs_lambphi( + xi, xj, bragg, lamb, phi, data, + lambfit=lambfit, phifit=phifit, spect1d=spect1d, + cmap=cmap, vmin=vmin, vmax=vmax, fs=fs) return ax def plot_data_fit2d(self, xi=None, xj=None, data=None, mask=None, diff --git a/tofu/geom/_plot_optics.py b/tofu/geom/_plot_optics.py index 07e669218..26014763d 100644 --- a/tofu/geom/_plot_optics.py +++ b/tofu/geom/_plot_optics.py @@ -395,7 +395,7 @@ def CrystalBragg_plot_data_vs_lambphi(xi, xj, bragg, lamb, phi, data, ax0.contour(xi, xj, phi, 10, cmap=cmap, ls='--') ax1.imshow(data, extent=extent, aspect='equal', origin='lower', vmin=vmin, vmax=vmax) - axs1.plot(xi, np.sum(data, axis=0), c='k', ls='-') + axs1.plot(xi, np.nanmean(data, axis=0), c='k', ls='-') ax2.scatter(lamb.ravel(), phi.ravel(), c=data.ravel(), s=2, marker='s', edgecolors='None', cmap=cmap, vmin=vmin, vmax=vmax) diff --git a/tofu/version.py b/tofu/version.py index 57829d158..9685ed84b 100644 --- a/tofu/version.py +++ b/tofu/version.py @@ -1,2 +1,2 @@ # Do not edit, pipeline versioning governed by git tags! -__version__ = '1.4.2-a5-2-g6065092' +__version__ = '1.4.2-a5-3-g860996b' From efbf5ffc8668a148e722ea5aaa7d8d1f700c1e85 Mon Sep 17 00:00:00 2001 From: VEZINET Didier Date: Tue, 3 Dec 2019 18:16:38 +0100 Subject: [PATCH 03/27] [Issue202] Advanced approx_detect() with di, dj, tilt add vertical sum to plot and mask, and optional dlines --- tofu/geom/_comp_optics.py | 39 ++++++++++++++++++++++++++++--- tofu/geom/_core_optics.py | 45 ++++++++++++++++-------------------- tofu/geom/_plot_optics.py | 48 +++++++++++++++++++++++++++++++++++---- tofu/version.py | 2 +- 4 files changed, 99 insertions(+), 35 deletions(-) diff --git a/tofu/geom/_comp_optics.py b/tofu/geom/_comp_optics.py index 4917835b5..4a81743cb 100644 --- a/tofu/geom/_comp_optics.py +++ b/tofu/geom/_comp_optics.py @@ -117,14 +117,47 @@ def get_approx_detector_rel(rcurve, bragg): return det_dist, det_nout_rel, det_ei_rel def get_det_abs_from_rel(det_dist, det_nout_rel, det_ei_rel, - summit, nout, e1, e2): + summit, nout, e1, e2, + ddist=None, di=None, dj=None, + dtheta=None, dpsi=None, tilt=None): + # Reference det_nout = (det_nout_rel[0]*nout + det_nout_rel[1]*e1 + det_nout_rel[2]*e2) det_ei = (det_ei_rel[0]*nout + det_ei_rel[1]*e1 + det_ei_rel[2]*e2) det_ej = np.cross(det_nout, det_ei) - det_cent = summit - det_dist*det_nout - return det_cent, det_nout, det_ei, det_ej + + # Apply translation of center (ddist, di, dj) + if ddist is None: + ddist = 0. + if di is None: + di = 0. + if dj is None: + dj = 0. + det_dist += ddist + det_cent = summit - det_dist*det_nout + di*det_ei + dj*det_ej + + # Apply angles on unit vectors + if dtheta is None: + dtheta = 0. + if dpsi is None: + dpsi = 0. + if tilt is None: + tilt = 0. + + # dtheta and dpsi + det_nout2 = ((np.cos(dpsi)*det_nout + + np.sin(dpsi)*det_ei)*np.cos(dtheta) + + np.sin(dtheta)*det_ej) + det_ei2 = (np.cos(dpsi)*det_ei - np.sin(dpsi)*det_nout) + det_ej2 = np.cross(det_nout2, det_ei2) + + # tilt + det_ei3 = np.cos(tilt)*det_ei2 + np.sin(tilt)*det_ej2 + det_ej3 = np.cross(det_nout2, det_ei3) + + + return det_cent, det_nout2, det_ei3, det_ej3 # ############################################### diff --git a/tofu/geom/_core_optics.py b/tofu/geom/_core_optics.py index 7db519d73..9957f9bdd 100644 --- a/tofu/geom/_core_optics.py +++ b/tofu/geom/_core_optics.py @@ -824,7 +824,8 @@ def get_Rays_envelop(self, # ----------------- def plot(self, lax=None, proj=None, res=None, element=None, - color=None, + color=None, det_cent=None, + det_nout=None, det_ei=None, det_ej=None, dP=None, dI=None, dBs=None, dBv=None, dVect=None, dIHor=None, dBsHor=None, dBvHor=None, dleg=None, @@ -857,9 +858,10 @@ def get_lamb_from_bragg(self, bragg, n=None): return _comp_optics.get_lamb_from_bragg(np.atleast_1d(bragg), self._dmat['d'], n=n) - def get_detector_frame(self, bragg=None, lamb=None, - rcurve=None, n=None, - dtheta=None, dpsi=None, ddist=None, plot=False): + def get_detector_approx(self, bragg=None, lamb=None, + rcurve=None, n=None, + ddist=None, di=None, dj=None, + dtheta=None, dpsi=None, tilt=None, plot=False): """ Return approximate ideal detector geometry Assumes infinitesimal and ideal crystal @@ -895,25 +897,13 @@ def get_detector_frame(self, bragg=None, lamb=None, func = _comp_optics.get_approx_detector_rel det_dist, det_nout_rel, det_ei_rel = func(rcurve, bragg) - # Apply small corrections - if dtheta is None: - dtheta = 0. - if dpsi is None: - dpsi = 0. - if ddist is None: - ddist = 0. - det_dist += ddist - # Deduce absolute position in (x, y, z) det_cent, det_nout, det_ei, det_ej = _comp_optics.get_det_abs_from_rel( det_dist, det_nout_rel, det_ei_rel, self._dgeom['summit'], - self._dgeom['nout'], self._dgeom['e1'], self._dgeom['e2']) - - if dtheta != 0. or dpsi != 0.: - det_nout = (np.cos(dpsi)*np.cos(dtheta)*det_nout - + np.cos(dpsi)*np.sin(dtheta)*det_ej - + np.sin(dpsi)*det_ei) + self._dgeom['nout'], self._dgeom['e1'], self._dgeom['e2'], + ddist=ddist, di=di, dj=dj, + dtheta=dtheta, dpsi=dpsi, tilt=tilt) if plot: dax = self.plot() @@ -1010,7 +1000,7 @@ def calc_xixj_from_phibragg(self, phi=None, det_ei is None, det_ej is None] assert all(lc) or not any(lc) if all(lc): - func = self.get_approx_detector_frame + func = self.get_detector_approx det_cent, det_nout, det_ei, det_ej = func(lamb=self._DEFLAMB) # Get local summit nout, e1, e2 if non-centered @@ -1035,6 +1025,7 @@ def calc_xixj_from_phibragg(self, phi=None, def _checkformat_xixj(xi, xj): xi = np.atleast_1d(xi) xj = np.atleast_1d(xj) + if xi.shape == xj.shape: return xi, xj, (xi, xj) else: @@ -1051,7 +1042,7 @@ def calc_phibragg_from_xixj(self, xi, xj, n=None, lc = [det_cent is None, det_ei is None, det_ej is None] assert all(lc) or not any(lc) if all(lc): - det_cent, _, det_ei, det_ej = self.get_approx_detector_frame( + det_cent, _, det_ei, det_ej = self.get_detector_approx( lamb=self._DEFLAMB) # Get local summit nout, e1, e2 if non-centered @@ -1078,11 +1069,11 @@ def calc_phibragg_from_xixj(self, xi, xj, n=None, def plot_johannerror(self, lamb=None): raise NotImplementedError - def plot_data_vs_lambphi(self, xi=None, xj=None, data=None, - det_cent=None, det_ei=None, det_ej=None, - theta=None, psi=None, n=None, - plot=True, fs=None, - cmap=None, vmin=None, vmax=None): + def plot_data_vs_lambphi(self, xi=None, xj=None, data=None, mask=None, + det_cent=None, det_ei=None, det_ej=None, + theta=None, psi=None, n=None, + plot=True, fs=None, + cmap=None, vmin=None, vmax=None): # Check / format inputs assert data is not None xi, xj, (xii, xjj) = self._checkformat_xixj(xi, xj) @@ -1101,6 +1092,8 @@ def plot_data_vs_lambphi(self, xi=None, xj=None, data=None, lambfit, phifit = _comp_optics.get_lambphifit(lamb, phi, nxi, nxj) lambfitbins = 0.5*(lambfit[1:] + lambfit[:-1]) ind = np.digitize(lamb, lambfitbins) + if mask is not None: + data[~mask] = np.nan spect1d = np.array([np.nanmean(data[ind==ii]) for ii in np.unique(ind)]) # plot diff --git a/tofu/geom/_plot_optics.py b/tofu/geom/_plot_optics.py index 26014763d..574e8e406 100644 --- a/tofu/geom/_plot_optics.py +++ b/tofu/geom/_plot_optics.py @@ -93,6 +93,7 @@ def _check_projdax_mpl(dax=None, proj=None, fs=None, wintit=None): def CrystalBragg_plot(cryst, lax=None, proj=None, res=None, element=None, color=None, dP=None, + det_cent=None, det_nout=None, det_ei=None, det_ej=None, dI=None, dBs=None, dBv=None, dVect=None, dIHor=None, dBsHor=None, dBvHor=None, dleg=None, indices=False, @@ -118,8 +119,10 @@ def CrystalBragg_plot(cryst, lax=None, proj=None, res=None, element=None, # Temporary matplotlib issue dleg = None else: - dax = _CrystalBragg_plot_crosshor(cryst, proj=proj, res=res, dax=lax, element=element, - color=color) + dax = _CrystalBragg_plot_crosshor(cryst, proj=proj, res=res, dax=lax, + element=element, color=color, + det_cent=det_cent, det_nout=det_nout, + det_ei=det_ei, det_ej=det_ej) # recompute the ax.dataLim ax0 = None @@ -139,7 +142,10 @@ def CrystalBragg_plot(cryst, lax=None, proj=None, res=None, element=None, ax0.figure.canvas.draw() return dax -def _CrystalBragg_plot_crosshor(cryst, proj=None, dax=None, element=None, res=None, +def _CrystalBragg_plot_crosshor(cryst, proj=None, dax=None, + element=None, res=None, + det_cent=None, det_nout=None, + det_ei=None, det_ej=None, Pdict=_def.TorPd, Idict=_def.TorId, Bsdict=_def.TorBsd, Bvdict=_def.TorBvd, Vdict=_def.TorVind, color=None, ms=None, quiver_cmap=None, @@ -232,8 +238,10 @@ def _CrystalBragg_plot_crosshor(cryst, proj=None, dax=None, element=None, res=No p0 = np.repeat(summ[:,None], 3, axis=1) v = np.concatenate((nin[:, None], e1[:, None], e2[:, None]), axis=1) if dax['cross'] is not None: - dax['cross'].quiver(np.hypot(p0[0,:], p0[1,:]), p0[2,:], - np.hypot(v[0,:], v[1,:]), v[2,:], + pr = np.hypot(p0[0,:], p0[1,:]) + vr = np.hypot(p0[0,:]+v[0,:], p0[1,:]+v[1,:]) - pr + dax['cross'].quiver(pr, p0[2,:], + vr, v[2,:], np.r_[0., 0.5, 1.], cmap=quiver_cmap, angles='xy', scale_units='xy', label=cryst.Id.NameLTX+" unit vect", **Vdict) @@ -244,6 +252,36 @@ def _CrystalBragg_plot_crosshor(cryst, proj=None, dax=None, element=None, res=No angles='xy', scale_units='xy', label=cryst.Id.NameLTX+" unit vect", **Vdict) + # Detector + sc = None + if det_cent is not None: + if dax['cross'] is not None: + dax['cross'].plot(np.hypot(det_cent[0], det_cent[1]), det_cent[2], + marker='x', ms=ms, c=color, label="det_cent") + if dax['hor'] is not None: + dax['hor'].plot(det_cent[0], det_cent[1], + marker='x', ms=ms, c=color, label="det_cent") + + if det_nout is not None: + assert det_ei is not None and det_ej is not None + p0 = np.repeat(det_cent[:, None], 3, axis=1) + v = np.concatenate((det_nout[:, None], det_ei[:, None], + det_ej[:, None]), axis=1) + if dax['cross'] is not None: + pr = np.hypot(p0[0,:], p0[1,:]) + vr = np.hypot(p0[0,:]+v[0,:], p0[1,:]+v[1,:]) - pr + dax['cross'].quiver(pr, p0[2,:], + vr, v[2,:], + np.r_[0., 0.5, 1.], cmap=quiver_cmap, + angles='xy', scale_units='xy', + label="det unit vect", **Vdict) + if dax['hor'] is not None: + dax['hor'].quiver(p0[0,:], p0[1,:], + v[0,:], v[1,:], + np.r_[0., 0.5, 1.], cmap=quiver_cmap, + angles='xy', scale_units='xy', + label="det unit vect", **Vdict) + return dax diff --git a/tofu/version.py b/tofu/version.py index 9685ed84b..432f412b3 100644 --- a/tofu/version.py +++ b/tofu/version.py @@ -1,2 +1,2 @@ # Do not edit, pipeline versioning governed by git tags! -__version__ = '1.4.2-a5-3-g860996b' +__version__ = '1.4.2-a5-4-g87899e8' From 413be477bc685334aca52a88e31c93c4ab3cd59d Mon Sep 17 00:00:00 2001 From: VEZINET Didier Date: Wed, 4 Dec 2019 14:29:36 +0100 Subject: [PATCH 04/27] [Issue202] Added vertsum1d to CrystBragg.plot_data_vs_philamb(), tunable nphifit and nlambfit, started adding magaxis phi computation => notes --- tofu/geom/_core_optics.py | 34 +++++++++++++++++++++++++++++++--- tofu/geom/_plot_optics.py | 17 ++++++++++++++--- tofu/version.py | 2 +- 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/tofu/geom/_core_optics.py b/tofu/geom/_core_optics.py index 9957f9bdd..687ebe515 100644 --- a/tofu/geom/_core_optics.py +++ b/tofu/geom/_core_optics.py @@ -840,6 +840,19 @@ def plot(self, lax=None, proj=None, res=None, element=None, # methods for generic first-approx # ----------------- + def get_phi_from_magaxis_summit(self, r, z, lamb=None, bragg=None, n=None): + # Check / format input + r = np.atleast_1d(r) + z = np.atleast_1d(z) + assert r.shape == z.shape + bragg = self._checkformat_bragglamb(bragg=bragg, lamb=lamb, n=n) + + # Compute phi + + return phi + + + def get_bragg_from_lamb(self, lamb, n=None): """ Braggs' law: n*lamb = 2dsin(bragg) """ if self._dmat['d'] is None: @@ -1072,6 +1085,8 @@ def plot_johannerror(self, lamb=None): def plot_data_vs_lambphi(self, xi=None, xj=None, data=None, mask=None, det_cent=None, det_ei=None, det_ej=None, theta=None, psi=None, n=None, + nlambfit=None, nphifit=None, + phiref=None, magaxis=None, plot=True, fs=None, cmap=None, vmin=None, vmax=None): # Check / format inputs @@ -1089,17 +1104,30 @@ def plot_data_vs_lambphi(self, xi=None, xj=None, data=None, mask=None, lamb = self.get_lamb_from_bragg(bragg, n=n) # Compute lambfit / phifit and spectrum1d - lambfit, phifit = _comp_optics.get_lambphifit(lamb, phi, nxi, nxj) - lambfitbins = 0.5*(lambfit[1:] + lambfit[:-1]) - ind = np.digitize(lamb, lambfitbins) if mask is not None: data[~mask] = np.nan + if nlambfit is None: + nlambfit = nxi + if nphifit is None: + nphifit = nxj + lambfit, phifit = _comp_optics.get_lambphifit(lamb, phi, + nlambfit, nphifit) + lambfitbins = 0.5*(lambfit[1:] + lambfit[:-1]) + ind = np.digitize(lamb, lambfitbins) spect1d = np.array([np.nanmean(data[ind==ii]) for ii in np.unique(ind)]) + phifitbins = 0.5*(phifit[1:] + phifit[:-1]) + ind = np.digitize(phi, phifitbins) + vertsum1d = np.array([np.nanmean(data[ind==ii]) for ii in np.unique(ind)]) + + # Get phiref from mag axis + if phiref is None and magaxis is not None: + phiref = None # plot ax = _plot_optics.CrystalBragg_plot_data_vs_lambphi( xi, xj, bragg, lamb, phi, data, lambfit=lambfit, phifit=phifit, spect1d=spect1d, + vertsum1d=vertsum1d, phiref=phiref, cmap=cmap, vmin=vmin, vmax=vmax, fs=fs) return ax diff --git a/tofu/geom/_plot_optics.py b/tofu/geom/_plot_optics.py index 574e8e406..69c28e047 100644 --- a/tofu/geom/_plot_optics.py +++ b/tofu/geom/_plot_optics.py @@ -381,7 +381,9 @@ def CrystalBragg_plot_braggangle_from_xixj(xi=None, xj=None, def CrystalBragg_plot_data_vs_lambphi(xi, xj, bragg, lamb, phi, data, - lambfit=None, phifit=None, spect1d=None, + lambfit=None, phifit=None, + spect1d=None, vertsum1d=None, + phiref=None, cmap=None, vmin=None, vmax=None, fs=None, dmargin=None, angunits='deg'): @@ -402,6 +404,8 @@ def CrystalBragg_plot_data_vs_lambphi(xi, xj, bragg, lamb, phi, data, bragg = bragg*180./np.pi phi = phi*180./np.pi phifit = phifit*180./np.pi + if phiref is not None: + phiref = 180*phiref/np.pi # pre-compute @@ -415,19 +419,23 @@ def CrystalBragg_plot_data_vs_lambphi(xi, xj, bragg, lamb, phi, data, # ------------ fig = fig = plt.figure(figsize=fs) - gs = gridspec.GridSpec(4, 3, **dmargin) + gs = gridspec.GridSpec(4, 4, **dmargin) ax0 = fig.add_subplot(gs[:3, 0], aspect='equal', adjustable='datalim') ax1 = fig.add_subplot(gs[:3, 1], aspect='equal', adjustable='datalim', sharex=ax0, sharey=ax0) axs1 = fig.add_subplot(gs[3, 1], sharex=ax0) ax2 = fig.add_subplot(gs[:3, 2]) axs2 = fig.add_subplot(gs[3, 2], sharex=ax2, sharey=axs1) + ax3 = fig.add_subplot(gs[:3, 3], sharey=ax2) ax0.set_title('Coordinates transform') ax1.set_title('Camera image') ax2.set_title('Camera image transformed') - ax0.set_ylabel(r'incidence angle ($deg$)') + ax2.set_ylabel(r'incidence angle ($deg$)') + ax2.set_xlabel(r'$\lambda$ ($m$)') + axs2.set_xlabel(r'$\lambda$ ($m$)') + ax3.set_ylabel(r'incidence angle ($deg$)') ax0.contour(xi, xj, bragg, 10, cmap=cmap) ax0.contour(xi, xj, phi, 10, cmap=cmap, ls='--') @@ -438,6 +446,9 @@ def CrystalBragg_plot_data_vs_lambphi(xi, xj, bragg, lamb, phi, data, marker='s', edgecolors='None', cmap=cmap, vmin=vmin, vmax=vmax) axs2.plot(lambfit, spect1d, c='k', ls='-') + ax3.plot(vertsum1d, phifit, c='k', ls='-') + if phiref is not None: + ax3.axhline(phiref, c='k', ls='--') ax2.set_xlim(extent2[0], extent2[1]) ax2.set_ylim(extent2[2], extent2[3]) diff --git a/tofu/version.py b/tofu/version.py index 432f412b3..a6dc38a33 100644 --- a/tofu/version.py +++ b/tofu/version.py @@ -1,2 +1,2 @@ # Do not edit, pipeline versioning governed by git tags! -__version__ = '1.4.2-a5-4-g87899e8' +__version__ = '1.4.2-a5-5-gefbf5ff' From 7b9be6cdf891a41e0b74030d1b0c5747a3510cac Mon Sep 17 00:00:00 2001 From: VEZINET Didier Date: Wed, 4 Dec 2019 18:09:25 +0100 Subject: [PATCH 05/27] [Issue202] Computing cone intersection with circle too cumbersome => discretize circle and select in interval of interest --- .../SpectroX2D_ConeCircleMagAxis.pdf | Bin 0 -> 71783 bytes .../SpectroX2D_ConeCircleMagAxis.tex | 133 ++++++++++++++++++ tofu/version.py | 2 +- 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 Notes_Upgrades/SpectroX2D/SpectroX2D_ConeCircleMagAxis.pdf create mode 100644 Notes_Upgrades/SpectroX2D/SpectroX2D_ConeCircleMagAxis.tex diff --git a/Notes_Upgrades/SpectroX2D/SpectroX2D_ConeCircleMagAxis.pdf b/Notes_Upgrades/SpectroX2D/SpectroX2D_ConeCircleMagAxis.pdf new file mode 100644 index 0000000000000000000000000000000000000000..136140d9c72c9a9829e046150e85b643dc6c48b2 GIT binary patch literal 71783 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
  • 9C%%5XD0l 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 Date: Fri, 6 Dec 2019 16:07:39 +0100 Subject: [PATCH 06/27] [Issue202] Added magnetic axis to plot => investigate rhotn transform ? --- tofu/geom/_core_optics.py | 59 +++++++++++++++++++++++++++++++++------ tofu/geom/_plot_optics.py | 11 ++++---- tofu/version.py | 2 +- 3 files changed, 58 insertions(+), 14 deletions(-) diff --git a/tofu/geom/_core_optics.py b/tofu/geom/_core_optics.py index 687ebe515..9e81ce040 100644 --- a/tofu/geom/_core_optics.py +++ b/tofu/geom/_core_optics.py @@ -1079,6 +1079,32 @@ def calc_phibragg_from_xixj(self, xi, xj, n=None, braggunits='deg', angunits='deg', **kwdargs) return bragg, phi + def calc_phibragg_from_pts(self, pts, n=None): + """ Return the bragg angle and phi of pts from crystal summit + + The pts are provided as a (x, y, z) coordinates array + The bragg angle and phi are computed from the crystal's summit + + """ + # Check / format inputs + pts = np.asarray(pts) + assert pts.ndim in [1, 2] + assert 3 in pts.shape + if pts.ndim == 1: + pts = pts[:, None] + if pts.shape[0] != 3: + pts = pts.T + + # Compute + vect = pts - self._dgeom['summit'][:, None] + vect = vect / np.sqrt(np.sum(vect**2, axis=0)) + bragg = np.pi/2 - np.arccos(np.sum(vect*self._dgeom['nin'][:, None], axis=0)) + v1 = np.sum(vect*self._dgeom['e1'][:, None], axis=0) + v2 = np.sum(vect*self._dgeom['e2'][:, None], axis=0) + phi = np.arctan2(v2, v1) + return bragg, phi + + def plot_johannerror(self, lamb=None): raise NotImplementedError @@ -1086,7 +1112,7 @@ def plot_data_vs_lambphi(self, xi=None, xj=None, data=None, mask=None, det_cent=None, det_ei=None, det_ej=None, theta=None, psi=None, n=None, nlambfit=None, nphifit=None, - phiref=None, magaxis=None, + magaxis=None, npaxis=None, plot=True, fs=None, cmap=None, vmin=None, vmax=None): # Check / format inputs @@ -1120,15 +1146,32 @@ def plot_data_vs_lambphi(self, xi=None, xj=None, data=None, mask=None, vertsum1d = np.array([np.nanmean(data[ind==ii]) for ii in np.unique(ind)]) # Get phiref from mag axis - if phiref is None and magaxis is not None: - phiref = None + lambax, phiax = None, None + if magaxis is not None: + if npaxis is None: + npaxis = 1000 + thetacryst = np.arctan2(self._dgeom['summit'][1], + self._dgeom['summit'][0]) + thetaax = thetacryst + np.pi/2*np.linspace(-1, 1, npaxis) + pts = np.array([magaxis[0]*np.cos(thetaax), + magaxis[0]*np.sin(thetaax), + np.full((npaxis,), magaxis[1])]) + braggax, phiax = self.calc_phibragg_from_pts(pts) + lambax = self.get_lamb_from_bragg(braggax) + phiax = np.arctan2(np.sin(phiax-np.pi), np.cos(phiax-np.pi)) + ind = ((lambax >= lambfit[0]) & (lambax <= lambfit[-1]) + & (phiax >= phifit[0]) & (phiax <= phifit[-1])) + lambax, phiax = lambax[ind], phiax[ind] + ind = np.argsort(lambax) + lambax, phiax = lambax[ind], phiax[ind] # plot - ax = _plot_optics.CrystalBragg_plot_data_vs_lambphi( - xi, xj, bragg, lamb, phi, data, - lambfit=lambfit, phifit=phifit, spect1d=spect1d, - vertsum1d=vertsum1d, phiref=phiref, - cmap=cmap, vmin=vmin, vmax=vmax, fs=fs) + if plot: + ax = _plot_optics.CrystalBragg_plot_data_vs_lambphi( + xi, xj, bragg, lamb, phi, data, + lambfit=lambfit, phifit=phifit, spect1d=spect1d, + vertsum1d=vertsum1d, lambax=lambax, phiax=phiax, + cmap=cmap, vmin=vmin, vmax=vmax, fs=fs) return ax def plot_data_fit2d(self, xi=None, xj=None, data=None, mask=None, diff --git a/tofu/geom/_plot_optics.py b/tofu/geom/_plot_optics.py index 69c28e047..e9e0faef2 100644 --- a/tofu/geom/_plot_optics.py +++ b/tofu/geom/_plot_optics.py @@ -383,7 +383,7 @@ def CrystalBragg_plot_braggangle_from_xixj(xi=None, xj=None, def CrystalBragg_plot_data_vs_lambphi(xi, xj, bragg, lamb, phi, data, lambfit=None, phifit=None, spect1d=None, vertsum1d=None, - phiref=None, + lambax=None, phiax=None, cmap=None, vmin=None, vmax=None, fs=None, dmargin=None, angunits='deg'): @@ -404,8 +404,9 @@ def CrystalBragg_plot_data_vs_lambphi(xi, xj, bragg, lamb, phi, data, bragg = bragg*180./np.pi phi = phi*180./np.pi phifit = phifit*180./np.pi - if phiref is not None: - phiref = 180*phiref/np.pi + if phiax is not None: + phiax = 180*phiax/np.pi + # pre-compute @@ -447,8 +448,8 @@ def CrystalBragg_plot_data_vs_lambphi(xi, xj, bragg, lamb, phi, data, cmap=cmap, vmin=vmin, vmax=vmax) axs2.plot(lambfit, spect1d, c='k', ls='-') ax3.plot(vertsum1d, phifit, c='k', ls='-') - if phiref is not None: - ax3.axhline(phiref, c='k', ls='--') + if phiax is not None: + ax2.plot(lambax, phiax, c='r', ls='-', lw=1.) ax2.set_xlim(extent2[0], extent2[1]) ax2.set_ylim(extent2[2], extent2[3]) diff --git a/tofu/version.py b/tofu/version.py index 3c77326e5..d75255f8a 100644 --- a/tofu/version.py +++ b/tofu/version.py @@ -1,2 +1,2 @@ # Do not edit, pipeline versioning governed by git tags! -__version__ = '1.4.2-a5-15-gc472511' +__version__ = '1.4.2-a5-16-g7b9be6c' From b6ed4fc9adb224bc55a4870f2a985b7399eb865c Mon Sep 17 00:00:00 2001 From: VEZINET Didier Date: Mon, 9 Dec 2019 18:13:51 +0100 Subject: [PATCH 07/27] [Issue202] CrystBragg.calc_johannerror() operational with plot, but Detector not tangential to Rowland circle ! needs to be fixed in _get_detect_approx() --- tofu/geom/_comp_optics.py | 14 +++++++-- tofu/geom/_core_optics.py | 63 +++++++++++++++++++++++++++++++++++-- tofu/geom/_plot_optics.py | 65 +++++++++++++++++++++++++++++++++++++++ tofu/version.py | 2 +- 4 files changed, 137 insertions(+), 7 deletions(-) diff --git a/tofu/geom/_comp_optics.py b/tofu/geom/_comp_optics.py index 4a81743cb..4a8b0d956 100644 --- a/tofu/geom/_comp_optics.py +++ b/tofu/geom/_comp_optics.py @@ -106,14 +106,22 @@ def get_lamb_from_bragg(bragg, d, n=None): # Approximate solution # ############################################### -def get_approx_detector_rel(rcurve, bragg): +def get_approx_detector_rel(rcurve, bragg, tangent_to_rowland=None): + + if tangent_to_rowland is None: + tangent_to_rowland = True # distance crystal - det_center det_dist = rcurve*np.sin(bragg) # det_nout and det_e1 in (nout, e1, e2) (det_e2 = e2) - det_nout_rel = np.r_[np.sin(bragg), -np.cos(bragg), 0.] - det_ei_rel = np.r_[np.cos(bragg), np.sin(bragg), 0] + if tangent_to_rowland: + # TBF !!!!!!!!!!!!!!! + det_nout_rel = p.r_[np.sin(bragg)] + det_ei_rel = None + else: + det_nout_rel = np.r_[np.sin(bragg), -np.cos(bragg), 0.] + det_ei_rel = np.r_[np.cos(bragg), np.sin(bragg), 0] return det_dist, det_nout_rel, det_ei_rel def get_det_abs_from_rel(det_dist, det_nout_rel, det_ei_rel, diff --git a/tofu/geom/_core_optics.py b/tofu/geom/_core_optics.py index 9e81ce040..e5ab9da08 100644 --- a/tofu/geom/_core_optics.py +++ b/tofu/geom/_core_optics.py @@ -874,7 +874,8 @@ def get_lamb_from_bragg(self, bragg, n=None): def get_detector_approx(self, bragg=None, lamb=None, rcurve=None, n=None, ddist=None, di=None, dj=None, - dtheta=None, dpsi=None, tilt=None, plot=False): + dtheta=None, dpsi=None, tilt=None, + tangent_to_rowland=None, plot=False): """ Return approximate ideal detector geometry Assumes infinitesimal and ideal crystal @@ -1105,8 +1106,64 @@ def calc_phibragg_from_pts(self, pts, n=None): return bragg, phi - def plot_johannerror(self, lamb=None): - raise NotImplementedError + def calc_johannerror(self, xi=None, xj=None, err=None, + det_cent=None, det_ei=None, det_ej=None, n=None, + lpsi=None, ltheta=None, + plot=True, fs=None, cmap=None, vmin=None, vmax=None): + """ Plot the johann error + + The johann error is the error (scattering) induced by defocalization + due to finite crystal dimensions + There is a johann error on wavelength (lamb => loss of spectral + resolution) and on directionality (phi) + If provided, lpsi and ltheta are taken as normalized variations with + respect to the crystal summit and to its extenthalf. + Typical values are: + - lpsi = [-1, 1, 1, -1] + - ltheta = [-1, -1, 1, 1] + They must have the same len() + + """ + + + # Check / format inputs + xi, xj, (xii, xjj) = self._checkformat_xixj(xi, xj) + nxi = xi.size if xi is not None else np.unique(xii).size + nxj = xj.size if xj is not None else np.unique(xjj).size + + # Compute lamb / phi + bragg, phi = self.calc_phibragg_from_xixj( + xii, xjj, n=n, + det_cent=det_cent, det_ei=det_ei, det_ej=det_ej, + theta=None, psi=None, plot=False) + assert bragg.shape == phi.shape + lamb = self.get_lamb_from_bragg(bragg, n=n) + + if lpsi is None: + lpsi = self._dgeom['extenthalf'][0]*np.r_[-1., 1., 1., -1.] + else: + lpsi = self._dgeom['extenthalf'][0]*np.r_[lpsi] + if ltheta is None: + ltheta = np.pi/2 + self._dgeom['extenthalf'][1]*np.r_[-1., -1., 1., 1.] + else: + ltheta = np.pi/2 + self._dgeom['extenthalf'][1]*np.r_[ltheta] + npsi = lpsi.size + assert npsi == ltheta.size + lamberr = np.full(tuple(np.r_[npsi, lamb.shape]), np.nan) + phierr = np.full(lamberr.shape, np.nan) + for ii in range(len(ltheta)): + bragg, phierr[ii, ...] = self.calc_phibragg_from_xixj( + xii, xjj, n=n, + det_cent=det_cent, det_ei=det_ei, det_ej=det_ej, + theta=ltheta[ii], psi=lpsi[ii], plot=False) + lamberr[ii, ...] = self.get_lamb_from_bragg(bragg, n=n) + err_lamb = np.nanmax(np.abs(lamb[None, ...] - lamberr), axis=0) + err_phi = np.nanmax(np.abs(phi[None, ...] - phierr), axis=0) + if plot is True: + ax = _plot_optics.CrystalBragg_plot_johannerror( + xi, xj, lamb, phi, err_lamb, err_phi, err=err, + cmap=cmap, vmin=vmin, vmax=vmax, fs=fs) + return err_lamb, err_phi def plot_data_vs_lambphi(self, xi=None, xj=None, data=None, mask=None, det_cent=None, det_ei=None, det_ej=None, diff --git a/tofu/geom/_plot_optics.py b/tofu/geom/_plot_optics.py index e9e0faef2..ba97a89bb 100644 --- a/tofu/geom/_plot_optics.py +++ b/tofu/geom/_plot_optics.py @@ -380,6 +380,71 @@ def CrystalBragg_plot_braggangle_from_xixj(xi=None, xj=None, return ax +def CrystalBragg_plot_johannerror(xi, xj, lamb, phi, err_lamb, err_phi, + cmap=None, vmin=None, vmax=None, + fs=None, dmargin=None, + angunits='deg', err=None): + + # Check inputs + # ------------ + + if fs is None: + fs = (14, 8) + if cmap is None: + cmap = plt.cm.viridis + if dmargin is None: + dmargin = {'left':0.03, 'right':0.99, + 'bottom':0.05, 'top':0.92, + 'wspace':None, 'hspace':0.4} + assert angunits in ['deg', 'rad'] + if angunits == 'deg': + # bragg = bragg*180./np.pi + phi = phi*180./np.pi + err_phi = err_phi*180./np.pi + + if err is None: + err = 'abs' + if err == 'rel': + err_lamb = 100.*err_lamb / (np.nanmax(lamb) - np.nanmin(lamb)) + err_phi = 100.*err_phi / (np.nanmax(phi) - np.nanmin(phi)) + + + # pre-compute + # ------------ + + # extent + extent = (xi.min(), xi.max(), xj.min(), xj.max()) + + # Plot + # ------------ + + fig = fig = plt.figure(figsize=fs) + gs = gridspec.GridSpec(1, 3, **dmargin) + ax0 = fig.add_subplot(gs[0, 0], aspect='equal', adjustable='datalim') + ax1 = fig.add_subplot(gs[0, 1], aspect='equal', adjustable='datalim', + sharex=ax0, sharey=ax0) + ax2 = fig.add_subplot(gs[0, 2], aspect='equal', adjustable='datalim', + sharex=ax0, sharey=ax0) + + ax0.set_title('Iso-lamb and iso-phi at crystal summit') + ax1.set_title('Focalization error on lamb') + ax2.set_title('Focalization error on phi') + + ax0.contour(xi, xj, lamb, 10, cmap=cmap) + ax0.contour(xi, xj, phi, 10, cmap=cmap, ls='--') + imlamb = ax1.imshow(err_lamb, extent=extent, aspect='equal', + origin='lower', interpolation='nearest', + vmin=vmin, vmax=vmax) + imphi = ax2.imshow(err_phi, extent=extent, aspect='equal', + origin='lower', interpolation='nearest', + vmin=vmin, vmax=vmax) + + plt.colorbar(imlamb, ax=ax1) + plt.colorbar(imphi, ax=ax2) + + return [ax0, ax1, ax2] + + def CrystalBragg_plot_data_vs_lambphi(xi, xj, bragg, lamb, phi, data, lambfit=None, phifit=None, spect1d=None, vertsum1d=None, diff --git a/tofu/version.py b/tofu/version.py index d75255f8a..913c1a02b 100644 --- a/tofu/version.py +++ b/tofu/version.py @@ -1,2 +1,2 @@ # Do not edit, pipeline versioning governed by git tags! -__version__ = '1.4.2-a5-16-g7b9be6c' +__version__ = '1.4.2-a5-17-g072d163' From 9ce7828cdeebde6c63fee480613a090301339469 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 9 Dec 2019 22:40:45 +0100 Subject: [PATCH 08/27] [Issue202] Fixed tanget_to_rowland, and more informative exception messages --- tofu/geom/_comp_optics.py | 21 ++++++++++++--------- tofu/geom/_core_optics.py | 29 +++++++++++++++++++++-------- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/tofu/geom/_comp_optics.py b/tofu/geom/_comp_optics.py index 4a8b0d956..fa88405dc 100644 --- a/tofu/geom/_comp_optics.py +++ b/tofu/geom/_comp_optics.py @@ -115,16 +115,17 @@ def get_approx_detector_rel(rcurve, bragg, tangent_to_rowland=None): det_dist = rcurve*np.sin(bragg) # det_nout and det_e1 in (nout, e1, e2) (det_e2 = e2) + n_crystdet_rel = np.r_[-np.sin(bragg), np.cos(bragg), 0.] if tangent_to_rowland: - # TBF !!!!!!!!!!!!!!! - det_nout_rel = p.r_[np.sin(bragg)] - det_ei_rel = None + bragg2 = 2.*bragg + det_nout_rel = np.r_[-np.cos(bragg2), -np.sin(bragg2), 0.] + det_ei_rel = np.r_[np.sin(bragg2), -np.cos(bragg2), 0.] else: - det_nout_rel = np.r_[np.sin(bragg), -np.cos(bragg), 0.] + det_nout_rel = -n_crystdet_rel det_ei_rel = np.r_[np.cos(bragg), np.sin(bragg), 0] - return det_dist, det_nout_rel, det_ei_rel + return det_dist, n_crystdet_rel, det_nout_rel, det_ei_rel -def get_det_abs_from_rel(det_dist, det_nout_rel, det_ei_rel, +def get_det_abs_from_rel(det_dist, n_crystdet_rel, det_nout_rel, det_ei_rel, summit, nout, e1, e2, ddist=None, di=None, dj=None, dtheta=None, dpsi=None, tilt=None): @@ -143,9 +144,12 @@ def get_det_abs_from_rel(det_dist, det_nout_rel, det_ei_rel, if dj is None: dj = 0. det_dist += ddist - det_cent = summit - det_dist*det_nout + di*det_ei + dj*det_ej - # Apply angles on unit vectors + n_crystdet = (n_crystdet_rel[0]*nout + + n_crystdet_rel[1]*e1 + n_crystdet_rel[2]*e2) + det_cent = summit + det_dist*n_crystdet + di*det_ei + dj*det_ej + + # Apply angles on unit vectors with respect to themselves if dtheta is None: dtheta = 0. if dpsi is None: @@ -164,7 +168,6 @@ def get_det_abs_from_rel(det_dist, det_nout_rel, det_ei_rel, det_ei3 = np.cos(tilt)*det_ei2 + np.sin(tilt)*det_ej2 det_ej3 = np.cross(det_nout2, det_ei3) - return det_cent, det_nout2, det_ei3, det_ej3 diff --git a/tofu/geom/_core_optics.py b/tofu/geom/_core_optics.py index e5ab9da08..8a48cd824 100644 --- a/tofu/geom/_core_optics.py +++ b/tofu/geom/_core_optics.py @@ -292,17 +292,17 @@ def _checkformat_dmat(cls, dmat=None): assert all([ss in lkok for ss in dmat.keys()]) for kk in cls._ddef['dmat'].keys(): dmat[kk] = dmat.get(kk, cls._ddef['dmat'][kk]) - if dmat['d'] is not None: + if dmat.get('d') is not None: dmat['d'] = float(dmat['d']) - if dmat['formula'] is not None: + if dmat.get('formula') is not None: assert isinstance(dmat['formula'], str) - if dmat['density'] is not None: + if dmat.get('density') is not None: dmat['density'] = float(dmat['density']) - if dmat['lengths'] is not None: + if dmat.get('lengths') is not None: dmat['lengths'] = np.atleast_1d(dmat['lengths']).ravel() - if dmat['angles'] is not None: + if dmat.get('angles') is not None: dmat['angles'] = np.atleast_1d(dmat['angles']).ravel() - if dmat['cut'] is not None: + if dmat.get('cut') is not None: dmat['cut'] = np.atleast_1d(dmat['cut']).ravel().astype(int) assert dmat['cut'].size <= 4 return dmat @@ -906,14 +906,27 @@ def get_detector_approx(self, bragg=None, lamb=None, if rcurve is None: rcurve = self._dgeom['rcurve'] bragg = self._checkformat_bragglamb(bragg=bragg, lamb=lamb, n=n) + if np.all(np.isnan(bragg)): + msg = ("There is no available bragg angle!\n" + + " => Check the vlue of self.dmat['d'] vs lamb") + raise Exception(msg) + + lf = ['summit', 'nout', 'e1', 'e2'] + lc = [rcurve is None] + [self._dgeom[kk] is None for kk in lf] + if any(lc): + msg = ("Some missing fields in dgeom for computation:" + + "\n\t-" + "\n\t-".join(['rcurve'] + lf)) + raise Exception(msg) # Compute crystal-centered parameters in (nout, e1, e2) func = _comp_optics.get_approx_detector_rel - det_dist, det_nout_rel, det_ei_rel = func(rcurve, bragg) + (det_dist, n_crystdet_rel, + det_nout_rel, det_ei_rel) = _comp_optics.get_approx_detector_rel( + rcurve, bragg, tangent_to_rowland=tangent_to_rowland) # Deduce absolute position in (x, y, z) det_cent, det_nout, det_ei, det_ej = _comp_optics.get_det_abs_from_rel( - det_dist, det_nout_rel, det_ei_rel, + det_dist, n_crystdet_rel, det_nout_rel, det_ei_rel, self._dgeom['summit'], self._dgeom['nout'], self._dgeom['e1'], self._dgeom['e2'], ddist=ddist, di=di, dj=dj, From 063c16509e7368a50b6c5d84b48372b8397b9f56 Mon Sep 17 00:00:00 2001 From: VEZINET Didier Date: Tue, 10 Dec 2019 08:42:23 +0100 Subject: [PATCH 09/27] [Issue202] Plotted images for ITER PDR, implemented tit / wintit in CrystalBragg_plot_johannerror() --- tofu/geom/_core_optics.py | 13 +++++++++---- tofu/geom/_plot_optics.py | 23 ++++++++++++++++++----- tofu/version.py | 2 +- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/tofu/geom/_core_optics.py b/tofu/geom/_core_optics.py index 8a48cd824..7000268f4 100644 --- a/tofu/geom/_core_optics.py +++ b/tofu/geom/_core_optics.py @@ -1122,7 +1122,8 @@ def calc_phibragg_from_pts(self, pts, n=None): def calc_johannerror(self, xi=None, xj=None, err=None, det_cent=None, det_ei=None, det_ej=None, n=None, lpsi=None, ltheta=None, - plot=True, fs=None, cmap=None, vmin=None, vmax=None): + plot=True, fs=None, cmap=None, + vmin=None, vmax=None, tit=None, wintit=None): """ Plot the johann error The johann error is the error (scattering) induced by defocalization @@ -1153,11 +1154,15 @@ def calc_johannerror(self, xi=None, xj=None, err=None, lamb = self.get_lamb_from_bragg(bragg, n=n) if lpsi is None: - lpsi = self._dgeom['extenthalf'][0]*np.r_[-1., 1., 1., -1.] + lpsi = self._dgeom['extenthalf'][0]*np.r_[-1., 0., 1., 1., + 1., 0., -1, -1] else: lpsi = self._dgeom['extenthalf'][0]*np.r_[lpsi] if ltheta is None: - ltheta = np.pi/2 + self._dgeom['extenthalf'][1]*np.r_[-1., -1., 1., 1.] + ltheta = np.pi/2 + self._dgeom['extenthalf'][1]*np.r_[-1., -1., + -1., 0., + 1., 1., + 1., 0.] else: ltheta = np.pi/2 + self._dgeom['extenthalf'][1]*np.r_[ltheta] npsi = lpsi.size @@ -1175,7 +1180,7 @@ def calc_johannerror(self, xi=None, xj=None, err=None, if plot is True: ax = _plot_optics.CrystalBragg_plot_johannerror( xi, xj, lamb, phi, err_lamb, err_phi, err=err, - cmap=cmap, vmin=vmin, vmax=vmax, fs=fs) + cmap=cmap, vmin=vmin, vmax=vmax, fs=fs, tit=tit, wintit=wintit) return err_lamb, err_phi def plot_data_vs_lambphi(self, xi=None, xj=None, data=None, mask=None, diff --git a/tofu/geom/_plot_optics.py b/tofu/geom/_plot_optics.py index ba97a89bb..936eb8966 100644 --- a/tofu/geom/_plot_optics.py +++ b/tofu/geom/_plot_optics.py @@ -382,7 +382,7 @@ def CrystalBragg_plot_braggangle_from_xixj(xi=None, xj=None, def CrystalBragg_plot_johannerror(xi, xj, lamb, phi, err_lamb, err_phi, cmap=None, vmin=None, vmax=None, - fs=None, dmargin=None, + fs=None, dmargin=None, wintit=None, tit=None, angunits='deg', err=None): # Check inputs @@ -393,8 +393,8 @@ def CrystalBragg_plot_johannerror(xi, xj, lamb, phi, err_lamb, err_phi, if cmap is None: cmap = plt.cm.viridis if dmargin is None: - dmargin = {'left':0.03, 'right':0.99, - 'bottom':0.05, 'top':0.92, + dmargin = {'left':0.05, 'right':0.99, + 'bottom':0.06, 'top':0.92, 'wspace':None, 'hspace':0.4} assert angunits in ['deg', 'rad'] if angunits == 'deg': @@ -407,7 +407,16 @@ def CrystalBragg_plot_johannerror(xi, xj, lamb, phi, err_lamb, err_phi, if err == 'rel': err_lamb = 100.*err_lamb / (np.nanmax(lamb) - np.nanmin(lamb)) err_phi = 100.*err_phi / (np.nanmax(phi) - np.nanmin(phi)) + err_lamb_units = '%' + err_phi_units = '%' + else: + err_lamb_units = 'm' + err_phi_units = angunits + if wintit is None: + wintit = _WINTIT + if tit is None: + tit = False # pre-compute # ------------ @@ -427,8 +436,8 @@ def CrystalBragg_plot_johannerror(xi, xj, lamb, phi, err_lamb, err_phi, sharex=ax0, sharey=ax0) ax0.set_title('Iso-lamb and iso-phi at crystal summit') - ax1.set_title('Focalization error on lamb') - ax2.set_title('Focalization error on phi') + ax1.set_title('Focalization error on lamb ({})'.format(err_lamb_units)) + ax2.set_title('Focalization error on phi ({})'.format(err_phi_units)) ax0.contour(xi, xj, lamb, 10, cmap=cmap) ax0.contour(xi, xj, phi, 10, cmap=cmap, ls='--') @@ -441,6 +450,10 @@ def CrystalBragg_plot_johannerror(xi, xj, lamb, phi, err_lamb, err_phi, plt.colorbar(imlamb, ax=ax1) plt.colorbar(imphi, ax=ax2) + if wintit is not False: + fig.canvas.set_window_title(wintit) + if tit is not False: + fig.suptitle(tit, size=14, weight='bold') return [ax0, ax1, ax2] diff --git a/tofu/version.py b/tofu/version.py index 913c1a02b..4d5b0289a 100644 --- a/tofu/version.py +++ b/tofu/version.py @@ -1,2 +1,2 @@ # Do not edit, pipeline versioning governed by git tags! -__version__ = '1.4.2-a5-17-g072d163' +__version__ = '1.4.2-a5-19-g9ce7828' From ebe97e311b2f660b2a48f2e7f8e6077ca12aed5d Mon Sep 17 00:00:00 2001 From: root Date: Wed, 11 Dec 2019 00:25:33 +0100 Subject: [PATCH 10/27] [Issue202] Added rockingcurve to dbragg and plot_rockingcurve() to be finished --- tofu/geom/_core_optics.py | 24 +++++++++++++++++++++++- tofu/geom/_plot_optics.py | 27 +++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/tofu/geom/_core_optics.py b/tofu/geom/_core_optics.py index 7000268f4..aca8c62bd 100644 --- a/tofu/geom/_core_optics.py +++ b/tofu/geom/_core_optics.py @@ -312,12 +312,19 @@ def _checkformat_dbragg(cls, dbragg=None): if dbragg is None: return assert isinstance(dbragg, dict) - lkok = ['angle'] + lkok = cls._get_keys_dbragg() assert all([isinstance(ss, str) for ss in dbragg.keys()]) assert all([ss in lkok for ss in dbragg.keys()]) for kk in cls._ddef['dbragg'].keys(): dbragg[kk] = dbragg.get(kk, cls._ddef['dbragg'][kk]) + if dbragg.get('rockingcurve') is not None: + assert isinstance(dbragg['rockingcurve'], dict) + drock = dbragg['rockingcurve'] + if drock.get('sigma') is not None: + dbragg['rockingcurve']['sigma'] = float(drock['sigma']) + dbragg['rockingcurve']['deltad'] = float(drock.get('deltad', 0.)) + dbragg['rockingcurve']['Rmax'] = float(drock.get('Rmax', 1.)) return dbragg @classmethod @@ -570,6 +577,13 @@ def summit(self): def center(self): return self._dgeom['center'] + @property + def rockingcurve(self): + if self._bragg.get('rockingcurve') is not None: + if self._dbragg['rocingcurve'].get('sigma') is not None: + return self._dbragg['rockingcurve'] + raise Exception("rockingcurve was not set!") + # ----------------- # methods for color # ----------------- @@ -692,6 +706,14 @@ def move(self, kind=None, **kwdargs): if kind == 'rotate': self._rotate(**kwdargs) + # ----------------- + # methods for rocking curve + # ----------------- + + def plot_rockingcurve(self): + drock = self.rockingcurve + return _plot.CrystalBragg_plot_rockingcurve(drock) + # ----------------- # methods for surface and contour sampling # ----------------- diff --git a/tofu/geom/_plot_optics.py b/tofu/geom/_plot_optics.py index 936eb8966..96d56da00 100644 --- a/tofu/geom/_plot_optics.py +++ b/tofu/geom/_plot_optics.py @@ -285,6 +285,33 @@ def _CrystalBragg_plot_crosshor(cryst, proj=None, dax=None, return dax +# ################################################################# +# ################################################################# +# Rocking curve plot +# ################################################################# +# ################################################################# + +def CrystalBragg_plot_rockingcurve(Rmax=None, sigma=None, + bragg=None, delta_bragg=None, npts=None): + + # Prepare + def func(angle, d=d, deltad=drock['deltad'], + Rmax=drock['Rmax'], sigma=drock['sigma']): + return Rmax*((sigma**2/(((angle - (bragg+delta_bragg))**2 + sigma**2)/(sigma*np.pi)) + + if npts is None: + npts = 1000 + angle = bragg + delta_bragg + 3.*sigma*np.linspace(-1, 1, npts) + curve = func(angle) + + # Plot + fig = plt.figure() + ax = fig.add_axes([0.1, 0.1, 0.8, 0.8]) + ax.plot(angle, curve, ls='-', lw=1., c='k') + ax.axvline(bragg, ls='--', lw=1, c='k') + return ax + + # ################################################################# # ################################################################# # Bragg diffraction plot From fb0ea5e63d59d8d14a90be31e522592db22ae155 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 11 Dec 2019 11:43:13 +0100 Subject: [PATCH 11/27] [Issue202] Added get_rockingcurve_func(), TODO: add possibility of tabulated rocking curve --- tofu/geom/_core_optics.py | 20 ++++++++++++++++++-- tofu/geom/_plot_optics.py | 9 +++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/tofu/geom/_core_optics.py b/tofu/geom/_core_optics.py index aca8c62bd..1de24b890 100644 --- a/tofu/geom/_core_optics.py +++ b/tofu/geom/_core_optics.py @@ -710,9 +710,25 @@ def move(self, kind=None, **kwdargs): # methods for rocking curve # ----------------- - def plot_rockingcurve(self): + def get_rockingcurve_func(self, lamb=None, bragg=None, n=None): drock = self.rockingcurve - return _plot.CrystalBragg_plot_rockingcurve(drock) + bragg = self._checkformat_bragglamb(bragg=bragg, lamb=lamb, n=n) + delta_bragg = self. - bragg + def func(angle, d=d, delta_bragg=delta_bragg, + Rmax=drock['Rmax'], sigma=drock['sigma']): + core = (sigma**2/(((angle - (bragg+delta_bragg))**2 + sigma**2) + if Rmax is None: + return core/(sigma*np.pi) + else: + return Rmax*core + return func + + def plot_rockingcurve(self, lamb=None, bragg=None, + fs=None, ax=None): + drock = self.rockingcurve + bragg = self._checkformat_bragglamb(bragg=bragg, lamb=lamb, n=n) + func = self.get_rockingcurve_func(bragg=bragg, n=n) + return _plot.CrystalBragg_plot_rockingcurve(func, fs=fs, ax=ax) # ----------------- # methods for surface and contour sampling diff --git a/tofu/geom/_plot_optics.py b/tofu/geom/_plot_optics.py index 96d56da00..4f7942530 100644 --- a/tofu/geom/_plot_optics.py +++ b/tofu/geom/_plot_optics.py @@ -295,18 +295,15 @@ def CrystalBragg_plot_rockingcurve(Rmax=None, sigma=None, bragg=None, delta_bragg=None, npts=None): # Prepare - def func(angle, d=d, deltad=drock['deltad'], - Rmax=drock['Rmax'], sigma=drock['sigma']): - return Rmax*((sigma**2/(((angle - (bragg+delta_bragg))**2 + sigma**2)/(sigma*np.pi)) - if npts is None: npts = 1000 angle = bragg + delta_bragg + 3.*sigma*np.linspace(-1, 1, npts) curve = func(angle) # Plot - fig = plt.figure() - ax = fig.add_axes([0.1, 0.1, 0.8, 0.8]) + if ax is None: + fig = plt.figure() + ax = fig.add_axes([0.1, 0.1, 0.8, 0.8]) ax.plot(angle, curve, ls='-', lw=1., c='k') ax.axvline(bragg, ls='--', lw=1, c='k') return ax From 5fe6eee411cb8148c9aeabb665ec53bba85499d9 Mon Sep 17 00:00:00 2001 From: VEZINET Didier Date: Wed, 11 Dec 2019 18:16:36 +0100 Subject: [PATCH 12/27] [Issue202] Rocking curve more elaborate, now allows for tabulated data, with source, consider lamb-dependent tab? saved in cryst --- tofu/geom/_core_optics.py | 45 ++++++++++++++++++++++++++++----------- tofu/version.py | 2 +- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/tofu/geom/_core_optics.py b/tofu/geom/_core_optics.py index 1de24b890..e3cca59af 100644 --- a/tofu/geom/_core_optics.py +++ b/tofu/geom/_core_optics.py @@ -321,10 +321,31 @@ def _checkformat_dbragg(cls, dbragg=None): if dbragg.get('rockingcurve') is not None: assert isinstance(dbragg['rockingcurve'], dict) drock = dbragg['rockingcurve'] - if drock.get('sigma') is not None: - dbragg['rockingcurve']['sigma'] = float(drock['sigma']) - dbragg['rockingcurve']['deltad'] = float(drock.get('deltad', 0.)) - dbragg['rockingcurve']['Rmax'] = float(drock.get('Rmax', 1.)) + try: + if drock.get('sigma') is not None: + dbragg['rockingcurve']['sigma'] = float(drock['sigma']) + dbragg['rockingcurve']['deltad'] = float(drock.get('deltad', 0.)) + dbragg['rockingcurve']['Rmax'] = float(drock.get('Rmax', 1.)) + dbragg['rockingcurve']['type'] = 'lorentz-log' + elif drock.get('angle') is not None: + dbragg['rockingcurve']['angle'] = np.r_[drock['angle']] + dbragg['rockingcurve']['value'] = np.r_[drock['value']] + dbragg['rockingcurve']['type'] = 'tabulated' + if drock.get('source') is None: + msg = "Unknonw source for the tabulated rocking curve!" + warnings.warn(msg) + dbragg['rockingcurve']['source'] = drock.get('source') + except Exception as err: + msg = ("Provide the rocking curve as a dict with either:\n" + + "\t- parameters of a lorentzian in log10:\n" + + "\t\t{'sigma': float,\n" + + "\t\t 'deltad': float,\n" + + "\t\t 'Rmax': float}\n" + + "\t- tabulated (angle, value), with source (url...)" + + "\t\t{'angle': np.ndarray,\n" + + "\t\t 'value': np.ndarray,\n" + + "\t\t 'source': str}") + raise Exception(msg) return dbragg @classmethod @@ -411,7 +432,7 @@ def set_dmat(self, dmat=None): dmat = self._checkformat_dmat(dmat) self._dmat = dmat - def _set_dbragg(self, dbragg=None): + def set_dbragg(self, dbragg=None): dbragg = self._checkformat_dbragg(dbragg) self._dbragg = dbragg @@ -713,14 +734,14 @@ def move(self, kind=None, **kwdargs): def get_rockingcurve_func(self, lamb=None, bragg=None, n=None): drock = self.rockingcurve bragg = self._checkformat_bragglamb(bragg=bragg, lamb=lamb, n=n) - delta_bragg = self. - bragg + delta_bragg = None def func(angle, d=d, delta_bragg=delta_bragg, - Rmax=drock['Rmax'], sigma=drock['sigma']): - core = (sigma**2/(((angle - (bragg+delta_bragg))**2 + sigma**2) - if Rmax is None: - return core/(sigma*np.pi) - else: - return Rmax*core + Rmax=drock['Rmax'], sigma=drock['sigma']): + core = sigma**2/((angle - (bragg+delta_bragg))**2 + sigma**2) + if Rmax is None: + return core/(sigma*np.pi) + else: + return Rmax*core return func def plot_rockingcurve(self, lamb=None, bragg=None, diff --git a/tofu/version.py b/tofu/version.py index 4d5b0289a..bb5bab542 100644 --- a/tofu/version.py +++ b/tofu/version.py @@ -1,2 +1,2 @@ # Do not edit, pipeline versioning governed by git tags! -__version__ = '1.4.2-a5-19-g9ce7828' +__version__ = '1.4.2-a5-22-gfb0ea5e' From dfd201172de369d53ca2feee2bd84b879e6ebb30 Mon Sep 17 00:00:00 2001 From: VEZINET Didier Date: Wed, 11 Dec 2019 18:18:32 +0100 Subject: [PATCH 13/27] [Issue202] Added input files for tests and develeping, to be deleted before PR --- ...CS_ArXVII_sh00000_Vers1.4.1-174-g453d6a3.npz | Bin 0 -> 46371 bytes tofu/version.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 TFG_CrystalBragg_ExpWEST_DgXICS_ArXVII_sh00000_Vers1.4.1-174-g453d6a3.npz diff --git a/TFG_CrystalBragg_ExpWEST_DgXICS_ArXVII_sh00000_Vers1.4.1-174-g453d6a3.npz b/TFG_CrystalBragg_ExpWEST_DgXICS_ArXVII_sh00000_Vers1.4.1-174-g453d6a3.npz new file mode 100644 index 0000000000000000000000000000000000000000..57f982f97fe1d656264c0b41a0b2edee6d81ec98 GIT binary patch literal 46371 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 literal 0 HcmV?d00001 diff --git a/tofu/version.py b/tofu/version.py index bb5bab542..eb4b78d2f 100644 --- a/tofu/version.py +++ b/tofu/version.py @@ -1,2 +1,2 @@ # Do not edit, pipeline versioning governed by git tags! -__version__ = '1.4.2-a5-22-gfb0ea5e' +__version__ = '1.4.2-a5-23-g5fe6eee' From 7d3011e7a2ff9e036663a0b9c5090258198c24bc Mon Sep 17 00:00:00 2001 From: VEZINET Didier Date: Thu, 12 Dec 2019 10:53:15 +0100 Subject: [PATCH 14/27] [Issue202] CrystBragg.get_summary() now displays rocking curve type --- tofu/geom/_core_optics.py | 11 ++++++++--- tofu/version.py | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/tofu/geom/_core_optics.py b/tofu/geom/_core_optics.py index e3cca59af..5d3f985b7 100644 --- a/tofu/geom/_core_optics.py +++ b/tofu/geom/_core_optics.py @@ -600,8 +600,8 @@ def center(self): @property def rockingcurve(self): - if self._bragg.get('rockingcurve') is not None: - if self._dbragg['rocingcurve'].get('sigma') is not None: + if self._dbragg.get('rockingcurve') is not None: + if self._dbragg['rockingcurve'].get('type') is not None: return self._dbragg['rockingcurve'] raise Exception("rockingcurve was not set!") @@ -626,11 +626,16 @@ def get_summary(self, sep=' ', line='-', just='l', # ----------------------- # Build material col0 = ['formula', 'symmetry', 'cut', 'density', - 'd (A)', 'bragg(%s A) (deg)'%str(self._DEFLAMB)] + 'd (A)', 'bragg(%s A) (deg)'%str(self._DEFLAMB), 'rocking curve'] ar0 = [self._dmat['formula'], self._dmat['symmetry'], str(self._dmat['cut']), str(self._dmat['density']), '{0:5.3f}'.format(self._dmat['d']*1.e10), str(self.get_bragg_from_lamb(self._DEFLAMB)[0]*180./np.pi)] + try: + ar0.append(self.rockingcurve['type']) + except Exception as err: + ar0.append('None') + # ----------------------- # Build geometry diff --git a/tofu/version.py b/tofu/version.py index d8838e0cb..5dedecfc4 100644 --- a/tofu/version.py +++ b/tofu/version.py @@ -1,2 +1,2 @@ # Do not edit, pipeline versioning governed by git tags! -__version__ = '1.4.2-a5-24-gdfd2011' +__version__ = '1.4.2-a5-35-gebc5fa7' From f44434fc3ef58b311befc347c3c227f9c0db7b2f Mon Sep 17 00:00:00 2001 From: VEZINET Didier Date: Thu, 12 Dec 2019 18:16:49 +0100 Subject: [PATCH 15/27] [Issue202] Both tabulated-1d and 2d rocking curves possible, finish plotting routines and get 2d data for Quartz crystal --- ..._ArXVII_sh00000_Vers1.4.1-174-g453d6a3.npz | Bin 46371 -> 47307 bytes tofu/geom/_core_optics.py | 81 ++++++++++++++---- tofu/version.py | 2 +- 3 files changed, 67 insertions(+), 16 deletions(-) diff --git a/TFG_CrystalBragg_ExpWEST_DgXICS_ArXVII_sh00000_Vers1.4.1-174-g453d6a3.npz b/TFG_CrystalBragg_ExpWEST_DgXICS_ArXVII_sh00000_Vers1.4.1-174-g453d6a3.npz index 57f982f97fe1d656264c0b41a0b2edee6d81ec98..1620a6b6c4e939677de17363b5d1e0f1304fd571 100644 GIT binary patch delta 5509 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 delta 4995 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 h9DABgjom>2Jn=M3U0G!`PxcW%_sl31`< 0: + msg = ("Unauthorized keys in dbrag['rockingcurve']:\n" + + "\t-" + "\n\t-".join(lkout)) + raise Exception(msg) try: if drock.get('sigma') is not None: dbragg['rockingcurve']['sigma'] = float(drock['sigma']) dbragg['rockingcurve']['deltad'] = float(drock.get('deltad', 0.)) dbragg['rockingcurve']['Rmax'] = float(drock.get('Rmax', 1.)) dbragg['rockingcurve']['type'] = 'lorentz-log' - elif drock.get('angle') is not None: - dbragg['rockingcurve']['angle'] = np.r_[drock['angle']] - dbragg['rockingcurve']['value'] = np.r_[drock['value']] - dbragg['rockingcurve']['type'] = 'tabulated' + elif drock.get('dangle') is not None: + c2d = (drock.get('lamb') is not None + and drock.get('value').ndim == 2) + if c2d: + if drock['value'].shape != (drock['dangle'].size, + drock['lamb'].size): + msg = ("Tabulated 2d rocking curve should be:\n" + + "\tshape = (dangle.size, lamb.size)") + raise Exception(msg) + dbragg['rockingcurve']['dangle'] = np.r_[drock['dangle']] + dbragg['rockingcurve']['lamb'] = np.r_[drock['lamb']] + dbragg['rockingcurve']['value'] = drock['value'] + dbragg['rockingcurve']['type'] = 'tabulated-2d' + else: + if drock.get('lamb') is None: + msg = ("Please also specify the lamb for which the " + + "rocking curve was tabulated!") + raise Exception(msg) + dbragg['rockingcurve']['lamb'] = float(drock['lamb']) + dbragg['rockingcurve']['dangle'] = np.r_[drock['dangle']] + dbragg['rockingcurve']['value'] = np.r_[drock['value']] + dbragg['rockingcurve']['type'] = 'tabulated-1d' if drock.get('source') is None: msg = "Unknonw source for the tabulated rocking curve!" warnings.warn(msg) @@ -341,8 +367,8 @@ def _checkformat_dbragg(cls, dbragg=None): + "\t\t{'sigma': float,\n" + "\t\t 'deltad': float,\n" + "\t\t 'Rmax': float}\n" - + "\t- tabulated (angle, value), with source (url...)" - + "\t\t{'angle': np.ndarray,\n" + + "\t- tabulated (dangle, value), with source (url...)" + + "\t\t{'dangle': np.ndarray,\n" + "\t\t 'value': np.ndarray,\n" + "\t\t 'source': str}") raise Exception(msg) @@ -738,15 +764,40 @@ def move(self, kind=None, **kwdargs): def get_rockingcurve_func(self, lamb=None, bragg=None, n=None): drock = self.rockingcurve - bragg = self._checkformat_bragglamb(bragg=bragg, lamb=lamb, n=n) - delta_bragg = None - def func(angle, d=d, delta_bragg=delta_bragg, - Rmax=drock['Rmax'], sigma=drock['sigma']): - core = sigma**2/((angle - (bragg+delta_bragg))**2 + sigma**2) - if Rmax is None: - return core/(sigma*np.pi) - else: - return Rmax*core + if drock['type'] == 'tabulated-1d': + if lamb is not None and lamb != drock['lamb']: + msg = ("rocking curve was tabulated only for:\n" + + "\tlamb = {} m\n".format(lamb) + + " => Please let lamb=None") + raise Exception(msg) + bragg = self._checkformat_bragglamb(lamb=drock['lamb'], n=n) + func = scpinterp.interp1d(drock['dangle']+bragg, drock['value'], + kind='linear', bounds_error=False, + fill_value=0, assume_sorted=True) + + elif drock['type'] == 'tabulated-2d': + bragg = self._checkformat_bragglamb(bragg=bragg, lamb=lamb, n=n) + lamb = self.get_lamb_from_bragg(bragg) + lmin, lmax = drock['lamb'].min(), drock['lamb'].max() + if lamb is None or lamb < lmin or lamb > lmax: + msg = ("rocking curve was tabulated only in interval:\n" + + "\tlamb in [{}; {}] m\n".format(lmin, lmax) + + " => Please set lamb accordingly") + raise Exception(msg) + bragg = self._checkformat_bragglamb(lamb=lamb, n=n) + def func(angle, lamb=lamb, bragg=bragg, drock=drock): + return scpinterp.interp2d(drock['dangle']+bragg, drock['lamb'], + drock['value'], kind='linear', + bounds_error=False, fill_value=0, + assume_sorted=True)(angle, lamb) + else: + def func(angle, d=d, delta_bragg=delta_bragg, + Rmax=drock['Rmax'], sigma=drock['sigma']): + core = sigma**2/((angle - (bragg+delta_bragg))**2 + sigma**2) + if Rmax is None: + return core/(sigma*np.pi) + else: + return Rmax*core return func def plot_rockingcurve(self, lamb=None, bragg=None, diff --git a/tofu/version.py b/tofu/version.py index 5dedecfc4..e6a5a9db5 100644 --- a/tofu/version.py +++ b/tofu/version.py @@ -1,2 +1,2 @@ # Do not edit, pipeline versioning governed by git tags! -__version__ = '1.4.2-a5-35-gebc5fa7' +__version__ = '1.4.2-a5-36-g7d3011e' From b2beda9c6fa19a29cadbfbf8353184c73729d8f5 Mon Sep 17 00:00:00 2001 From: VEZINET Didier Date: Fri, 13 Dec 2019 17:45:20 +0100 Subject: [PATCH 16/27] [Issue202] Johann error now also visible with plot_line_tracing() for a single lambda, and computation from plasma points started with calc_psidthetaphi_from_pts_lamb() --- tofu/geom/_comp_optics.py | 52 +++++++++- tofu/geom/_core_optics.py | 199 +++++++++++++++++++++++++++++++++++--- tofu/geom/_plot_optics.py | 53 ++++++++++ tofu/version.py | 2 +- 4 files changed, 288 insertions(+), 18 deletions(-) diff --git a/tofu/geom/_comp_optics.py b/tofu/geom/_comp_optics.py index fa88405dc..4c1d2200d 100644 --- a/tofu/geom/_comp_optics.py +++ b/tofu/geom/_comp_optics.py @@ -146,7 +146,7 @@ def get_det_abs_from_rel(det_dist, n_crystdet_rel, det_nout_rel, det_ei_rel, det_dist += ddist n_crystdet = (n_crystdet_rel[0]*nout - + n_crystdet_rel[1]*e1 + n_crystdet_rel[2]*e2) + + n_crystdet_rel[1]*e1 + n_crystdet_rel[2]*e2) det_cent = summit + det_dist*n_crystdet + di*det_ei + dj*det_ej # Apply angles on unit vectors with respect to themselves @@ -244,3 +244,53 @@ def get_lambphifit(lamb, phi, nxi, nxj): phiD = phi.max() - phi.min() phifit = phi.min() + phiD*np.linspace(0, 1, nxj) return lambfit, phifit + + +# ############################################### +# From plasma pts +# ############################################### + +def calc_psidthetaphi_from_pts_lamb(pts, bragg, , nlamb, npts, ntheta): + + scaPCem = np.full((nlamb, npts), np.nan) + psi = np.full((nlamb, npts, ntheta), np.nan) + dtheta = np.full((nlamb, npts, ntheta), np.nan) + phi = np.full((nlamb, npts, ntheta), np.nan) + + # Get to scalar product + PC = C[:, None] - pts + PCnorm2 = np.sum(PC**2, axis=0)**2 + cos2 = np.cos(bragg)**2 + deltaon4 = (*R**2*cos2[:, None]**2 + - *(R**2*cos2[:, None] + - PCnorm2[None, :]*np.sin(bragg)[:, None]**2)) + + # Get two relevant solutions + ind = deltaon4 >= 0. + PCnorm = np.tile(np.sqrt(PCnorm2), (nlamb, 1))[ind] + sol1 = -R*cos2 - np.sqrt(deltaon4[ind]) + sol2 = -R*cos2 + np.sqrt(deltaon4[ind]) + ind1 = (np.abs(sol1) <= PCnorm) & (sol1 >= -R) + ind2 = (np.abs(sol2) <= PCnorm) & (sol2 >= -R) + assert not np.any(ind1 & ind2) + sol1 = sol1[ind1] + sol2 = sol2[ind2] + indn = ind.nonzero() + ind1 = [indn[0][ind1], indn[1][ind1]] + ind2 = [indn[0][ind2], indn[1][ind2]] + scaPCem[ind1[0], ind1[1]] = sol1 + scaPCem[ind2[0], ind2[1]] = sol2 + ind = ~np.isnan(scaPCem) + + # Get equation on PCem + X = np.sum(PC*nout[:, None], axis=0) + Y = np.sum(PC*e1[:, None], axis=0) + Z = np.sum(PC*e2[:, None], axis=0) + XYnorm = np.sqrt(X**2 + Y**2) + psi = (np.arccos(XYnorm * (scaPCem[ind] - Z*np.sin(dtheta))/np.cos(dtheta)) + + np.arctan2(Y, X)) + psi = np.arctan2(np.sin(psi), np.cos(psi)) + + dtheta = extenthalf[1]*np.linspace(-1, 1, ntheta) + + return dtheta, psi, phi diff --git a/tofu/geom/_core_optics.py b/tofu/geom/_core_optics.py index 49fb960d8..4d39664e4 100644 --- a/tofu/geom/_core_optics.py +++ b/tofu/geom/_core_optics.py @@ -762,7 +762,7 @@ def move(self, kind=None, **kwdargs): # methods for rocking curve # ----------------- - def get_rockingcurve_func(self, lamb=None, bragg=None, n=None): + def get_rockingcurve_func(self, lamb=None, n=None): drock = self.rockingcurve if drock['type'] == 'tabulated-1d': if lamb is not None and lamb != drock['lamb']: @@ -776,8 +776,6 @@ def get_rockingcurve_func(self, lamb=None, bragg=None, n=None): fill_value=0, assume_sorted=True) elif drock['type'] == 'tabulated-2d': - bragg = self._checkformat_bragglamb(bragg=bragg, lamb=lamb, n=n) - lamb = self.get_lamb_from_bragg(bragg) lmin, lmax = drock['lamb'].min(), drock['lamb'].max() if lamb is None or lamb < lmin or lamb > lmax: msg = ("rocking curve was tabulated only in interval:\n" @@ -800,10 +798,9 @@ def func(angle, d=d, delta_bragg=delta_bragg, return Rmax*core return func - def plot_rockingcurve(self, lamb=None, bragg=None, + def plot_rockingcurve(self, lamb=None, fs=None, ax=None): drock = self.rockingcurve - bragg = self._checkformat_bragglamb(bragg=bragg, lamb=lamb, n=n) func = self.get_rockingcurve_func(bragg=bragg, n=n) return _plot.CrystalBragg_plot_rockingcurve(func, fs=fs, ax=ax) @@ -1233,6 +1230,83 @@ def calc_phibragg_from_pts(self, pts, n=None): phi = np.arctan2(v2, v1) return bragg, phi + def plot_line_tracing_on_det(self, lamb=None, n=None, + xi_bounds=None, xj_bounds=None, nphi=None, + det_cent=None, det_nout=None, + det_ei=None, det_ej=None, + johann=False, lpsi=None, ltheta=None, + rocking=False, fs=None, dmargin=None, + wintit=None, tit=None): + """ Visualize the de-focusing by ray-tracing of chosen lamb + """ + # Check / format inputs + if lamb is None: + lamb = self._DEFLAMB + lamb = np.atleast_1d(lamb).ravel() + nlamb = lamb.size + + det = np.array([[xi_bounds[0], xi_bounds[1], xi_bounds[1], + xi_bounds[0], xi_bounds[0]], + [xj_bounds[0], xj_bounds[0], xj_bounds[1], + xj_bounds[1], xj_bounds[0]]]) + + # Compute lamb / phi + _, phi = self.calc_phibragg_from_xixj( + det[0, :], det[1, :], n=n, + det_cent=det_cent, det_ei=det_ei, det_ej=det_ej, + theta=None, psi=None, plot=False) + phimin, phimax = np.nanmin(phi), np.nanmax(phi) + phimin, phimax = phimin-(phimax-phimin)/10, phimax+(phimax-phimin)/10 + del phi + + # Get reference ray-tracing + if nphi is None: + nphi = 300 + phi = np.linspace(phimin, phimax, nphi) + bragg = self._checkformat_bragglamb(lamb=lamb, n=n) + + xi = np.full((nlamb, nphi), np.nan) + xj = np.full((nlamb, nphi), np.nan) + for ll in range(nlamb): + xi[ll, :], xj[ll, :] = self.calc_xixj_from_phibragg( + bragg=bragg[ll], phi=phi, n=n, + det_cent=det_cent, det_nout=det_nout, + det_ei=det_ei, det_ej=det_ej, plot=False) + + # Get johann-error raytracing (multiple positions on crystal) + xi_err, xj_err = None, None + if johann and not rocking: + if lpsi is None or ltheta is None: + lpsi = np.linspace(-1., 1., 15) + ltheta = np.linspace(-1., 1., 15) + lpsi, ltheta = np.meshgrid(lpsi, ltheta) + lpsi = lpsi.ravel() + ltheta = ltheta.ravel() + lpsi = self._dgeom['extenthalf'][0]*np.r_[lpsi] + ltheta = np.pi/2 + self._dgeom['extenthalf'][1]*np.r_[ltheta] + npsi = lpsi.size + assert npsi == ltheta.size + + xi_err = np.full((nlamb, npsi*nphi), np.nan) + xj_err = np.full((nlamb, npsi*nphi), np.nan) + for l in range(nlamb): + for ii in range(npsi): + i0 = np.arange(ii*nphi, (ii+1)*nphi) + xi_err[l, i0], xj_err[l, i0] = self.calc_xixj_from_phibragg( + phi=phi, bragg=bragg[l], lamb=None, n=n, + theta=ltheta[ii], psi=lpsi[ii], + det_cent=det_cent, det_nout=det_nout, + det_ei=det_ei, det_ej=det_ej, plot=False) + + # Get rocking curve error + if rocking: + pass + + # Plot + ax = _plot_optics.CrystalBragg_plot_line_tracing_on_det( + lamb, xi, xj, xi_err, xj_err, det=det, + johann=johann, rocking=rocking, + fs=fs, dmargin=dmargin, wintit=wintit, tit=tit) def calc_johannerror(self, xi=None, xj=None, err=None, det_cent=None, det_ei=None, det_ej=None, n=None, @@ -1269,22 +1343,16 @@ def calc_johannerror(self, xi=None, xj=None, err=None, lamb = self.get_lamb_from_bragg(bragg, n=n) if lpsi is None: - lpsi = self._dgeom['extenthalf'][0]*np.r_[-1., 0., 1., 1., - 1., 0., -1, -1] - else: - lpsi = self._dgeom['extenthalf'][0]*np.r_[lpsi] + lpsi = np.r_[-1., 0., 1., 1., 1., 0., -1, -1] + lpsi = self._dgeom['extenthalf'][0]*np.r_[lpsi] if ltheta is None: - ltheta = np.pi/2 + self._dgeom['extenthalf'][1]*np.r_[-1., -1., - -1., 0., - 1., 1., - 1., 0.] - else: - ltheta = np.pi/2 + self._dgeom['extenthalf'][1]*np.r_[ltheta] + ltheta = np.r_[-1., -1., -1., 0., 1., 1., 1., 0.] + ltheta = np.pi/2 + self._dgeom['extenthalf'][1]*np.r_[ltheta] npsi = lpsi.size assert npsi == ltheta.size lamberr = np.full(tuple(np.r_[npsi, lamb.shape]), np.nan) phierr = np.full(lamberr.shape, np.nan) - for ii in range(len(ltheta)): + for ii in range(npsi): bragg, phierr[ii, ...] = self.calc_phibragg_from_xixj( xii, xjj, n=n, det_cent=det_cent, det_ei=det_ei, det_ej=det_ej, @@ -1298,6 +1366,105 @@ def calc_johannerror(self, xi=None, xj=None, err=None, cmap=cmap, vmin=vmin, vmax=vmax, fs=fs, tit=tit, wintit=wintit) return err_lamb, err_phi + def calc_braggphi_from_pts(self, pts): + pass + + def calc_thetapsi_from_lambpts(self, lamb=None, pts=None): + + # Check / Format inputs + pts = np.atleast_1d(pts) + if pts.ndim == 1: + pts = pts.reshape((3, 1)) + npts = pts.shape[1] + + lamb = np.r_[lamb] + nlamb = lamb.size + bragg = self. + + + dtheta, psi = _comp_optics.calc_psidthetaphi_from_pts_lamb() + + return dtheta, psi, phi, bragg + + + def plot_line_from_pts_on_det(self, lamb=None, pts=None,) + xi_bounds=None, xj_bounds=None, nphi=None, + det_cent=None, det_nout=None, + det_ei=None, det_ej=None, + johann=False, lpsi=None, ltheta=None, + rocking=False, fs=None, dmargin=None, + wintit=None, tit=None): + """ Visualize the de-focusing by ray-tracing of chosen lamb + """ + # Check / format inputs + if lamb is None: + lamb = self._DEFLAMB + lamb = np.atleast_1d(lamb).ravel() + nlamb = lamb.size + + det = np.array([[xi_bounds[0], xi_bounds[1], xi_bounds[1], + xi_bounds[0], xi_bounds[0]], + [xj_bounds[0], xj_bounds[0], xj_bounds[1], + xj_bounds[1], xj_bounds[0]]]) + + # Compute lamb / phi + _, phi = self.calc_phibragg_from_xixj( + det[0, :], det[1, :], n=n, + det_cent=det_cent, det_ei=det_ei, det_ej=det_ej, + theta=None, psi=None, plot=False) + phimin, phimax = np.nanmin(phi), np.nanmax(phi) + phimin, phimax = phimin-(phimax-phimin)/10, phimax+(phimax-phimin)/10 + del phi + + # Get reference ray-tracing + if nphi is None: + nphi = 300 + phi = np.linspace(phimin, phimax, nphi) + bragg = self._checkformat_bragglamb(lamb=lamb, n=n) + + xi = np.full((nlamb, nphi), np.nan) + xj = np.full((nlamb, nphi), np.nan) + for ll in range(nlamb): + xi[ll, :], xj[ll, :] = self.calc_xixj_from_phibragg( + bragg=bragg[ll], phi=phi, n=n, + det_cent=det_cent, det_nout=det_nout, + det_ei=det_ei, det_ej=det_ej, plot=False) + + # Get johann-error raytracing (multiple positions on crystal) + xi_err, xj_err = None, None + if johann and not rocking: + if lpsi is None or ltheta is None: + lpsi = np.linspace(-1., 1., 15) + ltheta = np.linspace(-1., 1., 15) + lpsi, ltheta = np.meshgrid(lpsi, ltheta) + lpsi = lpsi.ravel() + ltheta = ltheta.ravel() + lpsi = self._dgeom['extenthalf'][0]*np.r_[lpsi] + ltheta = np.pi/2 + self._dgeom['extenthalf'][1]*np.r_[ltheta] + npsi = lpsi.size + assert npsi == ltheta.size + + xi_err = np.full((nlamb, npsi*nphi), np.nan) + xj_err = np.full((nlamb, npsi*nphi), np.nan) + for l in range(nlamb): + for ii in range(npsi): + i0 = np.arange(ii*nphi, (ii+1)*nphi) + xi_err[l, i0], xj_err[l, i0] = self.calc_xixj_from_phibragg( + phi=phi, bragg=bragg[l], lamb=None, n=n, + theta=ltheta[ii], psi=lpsi[ii], + det_cent=det_cent, det_nout=det_nout, + det_ei=det_ei, det_ej=det_ej, plot=False) + + # Get rocking curve error + if rocking: + pass + + # Plot + ax = _plot_optics.CrystalBragg_plot_line_tracing_on_det( + lamb, xi, xj, xi_err, xj_err, det=det, + johann=johann, rocking=rocking, + fs=fs, dmargin=dmargin, wintit=wintit, tit=tit) + def plot_data_vs_lambphi(self, xi=None, xj=None, data=None, mask=None, det_cent=None, det_ei=None, det_ej=None, theta=None, psi=None, n=None, diff --git a/tofu/geom/_plot_optics.py b/tofu/geom/_plot_optics.py index 4f7942530..e166d6a55 100644 --- a/tofu/geom/_plot_optics.py +++ b/tofu/geom/_plot_optics.py @@ -404,6 +404,59 @@ def CrystalBragg_plot_braggangle_from_xixj(xi=None, xj=None, return ax +def CrystalBragg_plot_line_tracing_on_det(lamb, xi, xj, xi_err, xj_err, + det=None, + johann=None, rocking=None, + fs=None, dmargin=None, wintit=None, tit=None): + + # Check inputs + # ------------ + + if fs is None: + fs = (6, 8) + if dmargin is None: + dmargin = {'left':0.05, 'right':0.99, + 'bottom':0.06, 'top':0.92, + 'wspace':None, 'hspace':0.4} + + if wintit is None: + wintit = _WINTIT + if tit is None: + tit = "line tracing" + if johann is True: + tit += " - johann error" + if rocking is True: + tit += " - rocking curve" + + plot_err = johann is True or rocking is True + + # Plot + # ------------ + + fig = fig = plt.figure(figsize=fs) + gs = gridspec.GridSpec(1, 1, **dmargin) + ax0 = fig.add_subplot(gs[0, 0], aspect='equal', adjustable='datalim') + + ax0.plot(det[0, :], det[1, :], ls='-', lw=1., c='k') + for l in range(lamb.size): + lab = r'$\lambda$'+' = {:6.3f} A'.format(lamb[l]*1.e10) + l0, = ax0.plot(xi[l, :], xj[l, :], ls='-', lw=1., label=lab) + if plot_err: + ax0.plot(xi_err[l, ...], xj_err[l, ...], + ls='None', lw=1., c=l0.get_color(), + marker='.', ms=1) + + ax0.legend() + + if wintit is not False: + fig.canvas.set_window_title(wintit) + if tit is not False: + fig.suptitle(tit, size=14, weight='bold') + return [ax0] + + + + def CrystalBragg_plot_johannerror(xi, xj, lamb, phi, err_lamb, err_phi, cmap=None, vmin=None, vmax=None, fs=None, dmargin=None, wintit=None, tit=None, diff --git a/tofu/version.py b/tofu/version.py index e6a5a9db5..324ba765a 100644 --- a/tofu/version.py +++ b/tofu/version.py @@ -1,2 +1,2 @@ # Do not edit, pipeline versioning governed by git tags! -__version__ = '1.4.2-a5-36-g7d3011e' +__version__ = '1.4.2-a5-37-gf44434f' From c7511173fc84f93c33896df9cfe06970a7ff1182 Mon Sep 17 00:00:00 2001 From: VEZINET Didier Date: Mon, 16 Dec 2019 10:53:32 +0100 Subject: [PATCH 17/27] [Issue202] Corrected lack of recurrence of self.to_dict(sep=...) in tf.utils.flatten_dict() --- tofu/geom/_comp_optics.py | 8 ++++---- tofu/geom/_core_optics.py | 4 ++-- tofu/utils.py | 10 +++++----- tofu/version.py | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tofu/geom/_comp_optics.py b/tofu/geom/_comp_optics.py index 4c1d2200d..2d4d38169 100644 --- a/tofu/geom/_comp_optics.py +++ b/tofu/geom/_comp_optics.py @@ -250,7 +250,7 @@ def get_lambphifit(lamb, phi, nxi, nxj): # From plasma pts # ############################################### -def calc_psidthetaphi_from_pts_lamb(pts, bragg, , nlamb, npts, ntheta): +def calc_psidthetaphi_from_pts_lamb(pts, bragg, nlamb, npts, ntheta): scaPCem = np.full((nlamb, npts), np.nan) psi = np.full((nlamb, npts, ntheta), np.nan) @@ -261,9 +261,9 @@ def calc_psidthetaphi_from_pts_lamb(pts, bragg, , nlamb, npts, ntheta): PC = C[:, None] - pts PCnorm2 = np.sum(PC**2, axis=0)**2 cos2 = np.cos(bragg)**2 - deltaon4 = (*R**2*cos2[:, None]**2 - - *(R**2*cos2[:, None] - - PCnorm2[None, :]*np.sin(bragg)[:, None]**2)) + deltaon4 = (R**2*cos2[:, None]**2 + - (R**2*cos2[:, None] + - PCnorm2[None, :]*np.sin(bragg)[:, None]**2)) # Get two relevant solutions ind = deltaon4 >= 0. diff --git a/tofu/geom/_core_optics.py b/tofu/geom/_core_optics.py index 4d39664e4..11a0a4ebf 100644 --- a/tofu/geom/_core_optics.py +++ b/tofu/geom/_core_optics.py @@ -1379,7 +1379,7 @@ def calc_thetapsi_from_lambpts(self, lamb=None, pts=None): lamb = np.r_[lamb] nlamb = lamb.size - bragg = self. + bragg = self dtheta, psi = _comp_optics.calc_psidthetaphi_from_pts_lamb() @@ -1387,7 +1387,7 @@ def calc_thetapsi_from_lambpts(self, lamb=None, pts=None): return dtheta, psi, phi, bragg - def plot_line_from_pts_on_det(self, lamb=None, pts=None,) + def plot_line_from_pts_on_det(self, lamb=None, pts=None, xi_bounds=None, xj_bounds=None, nphi=None, det_cent=None, det_nout=None, det_ei=None, det_ej=None, diff --git a/tofu/utils.py b/tofu/utils.py index 06784657a..e5efcdfbb 100644 --- a/tofu/utils.py +++ b/tofu/utils.py @@ -142,7 +142,7 @@ def flatten_dict(d, parent_key='', sep=None, deep='ref', if k not in lexcept_key: if issubclass(v.__class__, ToFuObjectBase): if deep=='dict': - v = v.to_dict(deep='dict') + v = v.to_dict(sep=sep, deep='dict') elif deep=='copy': v = v.copy(deep='copy') new_key = parent_key + sep + k if parent_key else k @@ -1625,7 +1625,7 @@ def to_dict(self, strip=None, sep=None, deep='ref'): # Call class-specific dd = self._to_dict() # --------------------- - dd['dId'] = self._get_dId() + dd['dId'] = self._get_dId(sep=sep) dd['dstrip'] = {'dict':self._dstrip, 'lexcept':None} dout = {} @@ -1646,7 +1646,7 @@ def to_dict(self, strip=None, sep=None, deep='ref'): dout = flatten_dict(dout, parent_key='', sep=sep, deep=deep) return dout - def _get_dId(self): + def _get_dId(self, sep=None): """ To be overloaded """ return {'dict':{}} @@ -1875,8 +1875,8 @@ def Id(self): """ return self._Id - def _get_dId(self): - return {'dict':self.Id.to_dict()} + def _get_dId(self, sep=None): + return {'dict':self.Id.to_dict(sep=sep)} def _reset(self): if hasattr(self,'_Id'): diff --git a/tofu/version.py b/tofu/version.py index 324ba765a..c6cbd36f8 100644 --- a/tofu/version.py +++ b/tofu/version.py @@ -1,2 +1,2 @@ # Do not edit, pipeline versioning governed by git tags! -__version__ = '1.4.2-a5-37-gf44434f' +__version__ = '1.4.2-a5-38-gb2beda9' From b0dac67b9aa0ad53b8ff3061aee1bdf83358002c Mon Sep 17 00:00:00 2001 From: VEZINET Didier Date: Mon, 16 Dec 2019 11:26:08 +0100 Subject: [PATCH 18/27] [Issue202] imas2tofu more robust vs Config saving and loading to/from ids wall, and fully recursive to_dict for .mat saving --- tofu/imas2tofu/_core.py | 8 ++--- tofu/utils.py | 66 +++++++++++++++++++++-------------------- tofu/version.py | 2 +- 3 files changed, 39 insertions(+), 37 deletions(-) diff --git a/tofu/imas2tofu/_core.py b/tofu/imas2tofu/_core.py index 2a73d5be6..1145db24c 100644 --- a/tofu/imas2tofu/_core.py +++ b/tofu/imas2tofu/_core.py @@ -2222,7 +2222,7 @@ def _get_t0(self, t0=None): def to_Config(self, Name=None, occ=None, indDescription=None, plot=True): lidsok = ['wall'] if indDescription is None: - indDescription = 0 + indDescription = 2 # --------------------------- # Preliminary checks on data source consistency @@ -3978,11 +3978,11 @@ def _save_to_imas_Config( obj, idd=None, shotfile=None, raise Exception(msg) if description_2d is None: - if nS == 1 and lcls[0] in ['Ves','PlasmaDomain']: + if nS == 1 and lcls[0] in ['Ves', 'PlasmaDomain']: description_2d = 0 else: - descrption_2d = 2 - assert description_2d in [0,2] + description_2d = 2 + assert description_2d in [0, 2] # Make sure StructIn is last (IMAS requirement) ind = lcls.index(lclsIn[0]) diff --git a/tofu/utils.py b/tofu/utils.py index e5efcdfbb..4eddd4a23 100644 --- a/tofu/utils.py +++ b/tofu/utils.py @@ -639,7 +639,7 @@ def _get_exception(q, ids, qtype='quantity'): def load_from_imas(shot=None, run=None, user=None, tokamak=None, version=None, - ids=None, Name=None, out=None, tlim=None, config=None, + ids=None, Name=None, returnas=None, tlim=None, config=None, occ=None, indch=None, indDescription=None, equilibrium=None, dsig=None, data=None, X=None, t0=None, dextra=None, plot=True, plot_sig=None, plot_X=None, @@ -657,14 +657,16 @@ def load_from_imas(shot=None, run=None, user=None, tokamak=None, version=None, raise Exception(msg) lok = ['Config', 'Plasma2D', 'Cam', 'Data'] - c0 = out is None or out in lok + c0 = returnas is None or returnas in lok if not c0: - msg = "Arg out must be in %s"%str(lok) + msg = "Arg returnas must be in %s"%str(lok) raise Exception(msg) # ------------------- # Prepare ids - assert ids is None or type(ids) in [list,str] + if type(ids) not in [list, str]: + msg = "Please specify an ids to load data from!" + raise Exception(msg) if type(ids) is str: ids = [ids] if type(ids) is list: @@ -826,17 +828,17 @@ def load_from_imas(shot=None, run=None, user=None, tokamak=None, version=None, # ------------------- - # Prepare out - loutok = ['Config','Plasma2D','Cam','Data'] - c0 = out is None - c1 = out in loutok - c2 = type(out) is list and all([oo is None or oo in loutok - for oo in out]) + # Prepare returnas + loutok = ['Config', 'Plasma2D', 'Cam', 'Data'] + c0 = returnas is None + c1 = returnas in loutok + c2 = type(returnas) is list and all([oo is None or oo in loutok + for oo in returnas]) assert c0 or c1 or c2 if c0: - out = [None for _ in ids] + returnas = [None for _ in ids] elif c1: - out = [str(out) for _ in ids] + returnas = [str(returnas) for _ in ids] # Temporary caveat if nids > 1: @@ -852,35 +854,35 @@ def load_from_imas(shot=None, run=None, user=None, tokamak=None, version=None, # Config if ids[ii] == 'wall': - assert out[ii] in [None,'Config'] - out[ii] = 'Config' - if out[ii] == 'Config': + assert returnas[ii] in [None,'Config'] + returnas[ii] = 'Config' + if returnas[ii] == 'Config': assert ids[ii] in [None,'wall'] # Plasma2D lids = imas2tofu.MultiIDSLoader._lidsplasma if ids[ii] in lids: - assert out[ii] in [None,'Plasma2D'] - out[ii] = 'Plasma2D' - if out[ii] == 'Plasma2D': + assert returnas[ii] in [None,'Plasma2D'] + returnas[ii] = 'Plasma2D' + if returnas[ii] == 'Plasma2D': assert ids[ii] in lids # Cam or Data lids = imas2tofu.MultiIDSLoader._lidsdiag if ids[ii] in lids: - assert out[ii] in [None,'Cam','Data'] - if out[ii] is None: - out[ii] = 'Data' - if out[ii] in ['Cam','Data']: + assert returnas[ii] in [None,'Cam','Data'] + if returnas[ii] is None: + returnas[ii] = 'Data' + if returnas[ii] in ['Cam','Data']: assert ids[ii] in lids - dout = {shot[jj]: {oo:[] for oo in set(out)} for jj in range(0,nshot)} + dout = {shot[jj]: {oo:[] for oo in set(returnas)} for jj in range(0,nshot)} # ------------------- # Prepare plot_ and complement ids - lPla = [ii for ii in range(0,nids) if out[ii] == 'Plasma2D'] - lCam = [ii for ii in range(0,nids) if out[ii] == 'Cam'] - lDat = [ii for ii in range(0,nids) if out[ii] == 'Data'] + lPla = [ii for ii in range(0,nids) if returnas[ii] == 'Plasma2D'] + lCam = [ii for ii in range(0,nids) if returnas[ii] == 'Cam'] + lDat = [ii for ii in range(0,nids) if returnas[ii] == 'Data'] nPla, nCam, nDat = len(lPla), len(lCam), len(lDat) if nDat > 1: plot_ = False @@ -960,23 +962,23 @@ def load_from_imas(shot=None, run=None, user=None, tokamak=None, version=None, # export to instances for ii in range(0,nids): - if out[ii] == 'Config': + if returnas[ii] == 'Config': dout[ss]['Config'].append(multi.to_Config(Name=Name, occ=occ, indDescription=indDescription, plot=False)) - elif out[ii] == 'Plasma2D': + elif returnas[ii] == 'Plasma2D': dout[ss]['Plasma2D'].append(multi.to_Plasma2D(Name=Name, occ=occ, tlim=tlim, dsig=dsig, t0=t0, plot=False, plot_sig=plot_sig, dextra=dextra, plot_X=plot_X, config=config, bck=bck)) - elif out[ii] == 'Cam': + elif returnas[ii] == 'Cam': dout[ss]['Cam'].append(multi.to_Cam(Name=Name, occ=occ, ids=lids[ii], indch=indch, config=config, plot=False)) - elif out[ii] == "Data": + elif returnas[ii] == "Data": dout[ss]['Data'].append(multi.to_Data(Name=Name, occ=occ, ids=lids[ii], tlim=tlim, dsig=dsig, config=config, data=data, X=X, indch=indch, @@ -990,7 +992,7 @@ def load_from_imas(shot=None, run=None, user=None, tokamak=None, version=None, # Config & Cam for ss in shot: - for k0 in set(['Config','Cam']).intersection(out): + for k0 in set(['Config', 'Cam']).intersection(returnas): for ii in range(0, len(dout[ss][k0])): dout[ss][k0][ii].plot() @@ -1020,7 +1022,7 @@ def load_from_imas(shot=None, run=None, user=None, tokamak=None, version=None, dout = dout[shot[0]]['Data'][0] elif nshot == 1 and nPla == 1: dout = dout[shot[0]]['Plasma2D'][0] - return out + return dout diff --git a/tofu/version.py b/tofu/version.py index c6cbd36f8..ec35551ce 100644 --- a/tofu/version.py +++ b/tofu/version.py @@ -1,2 +1,2 @@ # Do not edit, pipeline versioning governed by git tags! -__version__ = '1.4.2-a5-38-gb2beda9' +__version__ = '1.4.2-a5-39-gc751117' From e6b26ccf08658b2f24cadc40889145bb281c768e Mon Sep 17 00:00:00 2001 From: VEZINET Didier Date: Mon, 16 Dec 2019 11:34:27 +0100 Subject: [PATCH 19/27] [Issue202] save(mode='mat') default to sep='_' --- tofu/utils.py | 10 +++++++++- tofu/version.py | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tofu/utils.py b/tofu/utils.py index 4eddd4a23..58bf191e0 100644 --- a/tofu/utils.py +++ b/tofu/utils.py @@ -320,7 +320,15 @@ def save(obj, path=None, name=None, sep=None, deep=False, mode='npz', # Get stripped dictionnary deep = 'dict' if deep else 'ref' if sep is None: - sep = _SEP + if mode == 'mat': + sep = '_' + else: + sep = _SEP + if mode == 'mat' and sep == '.': + msg = ("sep='.' cannot be used when mode='mat' (incompatible)\n" + + "Matlab would interpret variables as structures") + raise Exception(msg) + dd = obj.to_dict(strip=strip, sep=sep, deep=deep) pathfileext = os.path.join(path,name+'.'+mode) diff --git a/tofu/version.py b/tofu/version.py index ec35551ce..22a278f5f 100644 --- a/tofu/version.py +++ b/tofu/version.py @@ -1,2 +1,2 @@ # Do not edit, pipeline versioning governed by git tags! -__version__ = '1.4.2-a5-39-gc751117' +__version__ = '1.4.2-a5-40-gb0dac67' From e1f9f11b602e12f9ce8d956cb8cacb03297bfd27 Mon Sep 17 00:00:00 2001 From: VEZINET Didier Date: Mon, 16 Dec 2019 18:17:29 +0100 Subject: [PATCH 20/27] [Issue202] Advanced calc_from_pts (phibragg, line projection...), TBF --- tofu/geom/_comp_optics.py | 90 +++++++++++++++++++++++++-------------- tofu/geom/_core_optics.py | 88 ++++++++++++++++++++++++++++---------- tofu/version.py | 2 +- 3 files changed, 125 insertions(+), 55 deletions(-) diff --git a/tofu/geom/_comp_optics.py b/tofu/geom/_comp_optics.py index 2d4d38169..a5fa988e2 100644 --- a/tofu/geom/_comp_optics.py +++ b/tofu/geom/_comp_optics.py @@ -213,24 +213,28 @@ def calc_xixj_from_braggphi(summit, det_cent, det_nout, det_ei, det_ej, return xi, xj def calc_braggphi_from_xixj(xi, xj, det_cent, det_ei, det_ej, - summit, nin, e1, e2): - - xi = xi[None, ...] - xj = xj[None, ...] - if xi.ndim == 1: - summit = summit[:, None] - det_cent = det_cent[:, None] - det_ei, det_ej = det_ei[:, None], det_ej[:, None] - nin, e1, e2 = nin[:, None], e1[:, None], e2[:, None] + summit, nin, e1, e2, pts=None): + + if pts is None: + xi = xi[None, ...] + xj = xj[None, ...] + if xi.ndim == 1: + summit = summit[:, None] + det_cent = det_cent[:, None] + det_ei, det_ej = det_ei[:, None], det_ej[:, None] + nin, e1, e2 = nin[:, None], e1[:, None], e2[:, None] + else: + summit = summit[:, None, None] + det_cent = det_cent[:, None, None] + det_ei, det_ej = det_ei[:, None, None], det_ej[:, None, None] + nin, e1, e2 = nin[:, None, None], e1[:, None, None], e2[:, None, None] + pts = det_cent + xi*det_ei + xj*det_ej else: + assert pts.ndim == 2 + pts = pts[:, :, None] summit = summit[:, None, None] - det_cent = det_cent[:, None, None] - det_ei, det_ej = det_ei[:, None, None], det_ej[:, None, None] nin, e1, e2 = nin[:, None, None], e1[:, None, None], e2[:, None, None] - - pts = det_cent + xi*det_ei + xj*det_ej - vect = pts - summit vect = vect / np.sqrt(np.sum(vect**2, axis=0))[None, ...] bragg = np.arcsin(np.sum(vect*nin, axis=0)) @@ -250,28 +254,34 @@ def get_lambphifit(lamb, phi, nxi, nxj): # From plasma pts # ############################################### -def calc_psidthetaphi_from_pts_lamb(pts, bragg, nlamb, npts, ntheta): +def calc_psidthetaphi_from_pts_lamb(pts, center, rcurve, + bragg, nlamb, npts, + nout, e1, e2, extenthalf, ntheta=None): + + if ntheta is None: + ntheta = 100 scaPCem = np.full((nlamb, npts), np.nan) - psi = np.full((nlamb, npts, ntheta), np.nan) dtheta = np.full((nlamb, npts, ntheta), np.nan) - phi = np.full((nlamb, npts, ntheta), np.nan) + psi = np.full((nlamb, npts, ntheta), np.nan) # Get to scalar product - PC = C[:, None] - pts - PCnorm2 = np.sum(PC**2, axis=0)**2 + PC = center[:, None] - pts + PCnorm2 = np.sum(PC**2, axis=0) cos2 = np.cos(bragg)**2 - deltaon4 = (R**2*cos2[:, None]**2 - - (R**2*cos2[:, None] + deltaon4 = (rcurve**2*cos2[:, None]**2 + - (rcurve**2*cos2[:, None] - PCnorm2[None, :]*np.sin(bragg)[:, None]**2)) # Get two relevant solutions ind = deltaon4 >= 0. + cos2 = np.repeat(cos2[:, None], npts, axis=1)[ind] PCnorm = np.tile(np.sqrt(PCnorm2), (nlamb, 1))[ind] - sol1 = -R*cos2 - np.sqrt(deltaon4[ind]) - sol2 = -R*cos2 + np.sqrt(deltaon4[ind]) - ind1 = (np.abs(sol1) <= PCnorm) & (sol1 >= -R) - ind2 = (np.abs(sol2) <= PCnorm) & (sol2 >= -R) + sol1 = -rcurve*cos2 - np.sqrt(deltaon4[ind]) + sol2 = -rcurve*cos2 + np.sqrt(deltaon4[ind]) + # em is a unit vector and ... + ind1 = (np.abs(sol1) <= PCnorm) & (sol1 >= -rcurve) + ind2 = (np.abs(sol2) <= PCnorm) & (sol2 >= -rcurve) assert not np.any(ind1 & ind2) sol1 = sol1[ind1] sol2 = sol2[ind2] @@ -286,11 +296,27 @@ def calc_psidthetaphi_from_pts_lamb(pts, bragg, nlamb, npts, ntheta): X = np.sum(PC*nout[:, None], axis=0) Y = np.sum(PC*e1[:, None], axis=0) Z = np.sum(PC*e2[:, None], axis=0) - XYnorm = np.sqrt(X**2 + Y**2) - psi = (np.arccos(XYnorm * (scaPCem[ind] - Z*np.sin(dtheta))/np.cos(dtheta)) - + np.arctan2(Y, X)) - psi = np.arctan2(np.sin(psi), np.cos(psi)) - - dtheta = extenthalf[1]*np.linspace(-1, 1, ntheta) - return dtheta, psi, phi + scaPCem = np.repeat(scaPCem[..., None], ntheta, axis=-1) + ind = ~np.isnan(scaPCem) + XYnorm = np.repeat(np.repeat(np.sqrt(X**2 + Y**2)[None, :], + nlamb, axis=0)[..., None], + ntheta, axis=-1)[ind] + Z = np.repeat(np.repeat(Z[None, :], nlamb, axis=0)[..., None], + ntheta, axis=-1)[ind] + angextra = np.repeat( + np.repeat(np.arctan2(Y, X)[None, :], nlamb, axis=0)[..., None], + ntheta, axis=-1)[ind] + dtheta[ind] = np.repeat( + np.repeat(extenthalf[1]*np.linspace(-1, 1, ntheta)[None, :], + npts, axis=0)[None, ...], + nlamb, axis=0)[ind] + + psi[ind] = (np.arccos((scaPCem[ind] + - Z*np.sin(dtheta[ind]))/(XYnorm*np.cos(dtheta[ind]))) + + angextra) + psi[ind] = np.arctan2(np.sin(psi[ind]), np.cos(psi[ind])) + indnan = (~ind) | (np.abs(psi) > extenthalf[0]) + psi[indnan] = np.nan + dtheta[indnan] = np.nan + return dtheta, psi, indnan diff --git a/tofu/geom/_core_optics.py b/tofu/geom/_core_optics.py index 11a0a4ebf..0ebe2a2ad 100644 --- a/tofu/geom/_core_optics.py +++ b/tofu/geom/_core_optics.py @@ -1160,6 +1160,18 @@ def calc_xixj_from_phibragg(self, phi=None, ax = func(bragg, xi, xj, data, ax) return xi, xj + @staticmethod + def _checkformat_pts(pts): + pts = np.atleast_1d(pts) + if pts.ndim == 1: + pts = pts.reshape((3, 1)) + if 3 not in pts.shape or pts.ndim != 2: + msg = "pts must be a (3, npts) array of (X, Y, Z) coordinates!" + raise Exception(msg) + if pts.shape[0] != 3: + pts = pts.T + return pts + @staticmethod def _checkformat_xixj(xi, xj): xi = np.atleast_1d(xi) @@ -1170,9 +1182,22 @@ def _checkformat_xixj(xi, xj): else: return xi, xj, np.meshgrid(xi, xj) + @staticmethod + def _checkformat_psidtheta(psi=None, dtheta=None, + psi_def=0., dtheta_def=0.): + if psi is None: + psi = psi_def + if dtheta is None: + dtheta = dtheta_def + psi = np.r_[psi] + dtheta = np.r_[dtheta] + if psi.size != dtheta.size: + msg = "psi and dtheta must be 1d arrays fo same size!" + raise Exception(msg) + def calc_phibragg_from_xixj(self, xi, xj, n=None, det_cent=None, det_ei=None, det_ej=None, - theta=None, psi=None, + dtheta=None, psi=None, plot=True, ax=None, **kwdargs): # Check / format inputs @@ -1185,11 +1210,8 @@ def calc_phibragg_from_xixj(self, xi, xj, n=None, lamb=self._DEFLAMB) # Get local summit nout, e1, e2 if non-centered - if theta is None: - theta = np.pi/2. - if psi is None: - psi = 0. - summit, nout, e1, e2 = self.get_local_noute1e2(theta, psi) + psi, dtheta = self._checkformat_psidtheta(psi=psi, dtheta=dtheta) + summit, nout, e1, e2 = self.get_local_noute1e2(dtheta, psi) # Compute bragg, phi = _comp_optics.calc_braggphi_from_xixj( @@ -1205,7 +1227,7 @@ def calc_phibragg_from_xixj(self, xi, xj, n=None, braggunits='deg', angunits='deg', **kwdargs) return bragg, phi - def calc_phibragg_from_pts(self, pts, n=None): + def calc_phibragg_from_pts_on_summit(self, pts, n=None): """ Return the bragg angle and phi of pts from crystal summit The pts are provided as a (x, y, z) coordinates array @@ -1213,13 +1235,7 @@ def calc_phibragg_from_pts(self, pts, n=None): """ # Check / format inputs - pts = np.asarray(pts) - assert pts.ndim in [1, 2] - assert 3 in pts.shape - if pts.ndim == 1: - pts = pts[:, None] - if pts.shape[0] != 3: - pts = pts.T + pts = self._checkformat_pts(pts) # Compute vect = pts - self._dgeom['summit'][:, None] @@ -1366,23 +1382,51 @@ def calc_johannerror(self, xi=None, xj=None, err=None, cmap=cmap, vmin=vmin, vmax=vmax, fs=fs, tit=tit, wintit=wintit) return err_lamb, err_phi - def calc_braggphi_from_pts(self, pts): + def calc_phibragg_from_pts(self, pts, dtheta=None, dpsi=None): + + # Check / format pts + pts = self._checkformat_pts(pts) + + # Get local summit nout, e1, e2 if non-centered + psi, dtheta = self._checkformat_psidtheta(psi=psi, dtheta=dtheta) + summit, nout, e1, e2 = self.get_local_noute1e2(dtheta, psi) + + # Compute + bragg, phi = _comp_optics.calc_braggphi_from_xixj( + xii, xjj, det_cent, det_ei, det_ej, + summit, -nout, e1, e2) + + + def get_lamb_avail_from_pts(self, pts): pass - def calc_thetapsi_from_lambpts(self, lamb=None, pts=None): + + def calc_thetapsi_from_lambpts(self, pts=None, lamb=None, n=None, + ntheta=None): # Check / Format inputs - pts = np.atleast_1d(pts) - if pts.ndim == 1: - pts = pts.reshape((3, 1)) + pts = self._checkformat_pts(pts) npts = pts.shape[1] + if lamb is None: + lamb = self._DEFLAMB lamb = np.r_[lamb] nlamb = lamb.size - bragg = self + bragg = self.get_bragg_from_lamb(lamb, n=n) - - dtheta, psi = _comp_optics.calc_psidthetaphi_from_pts_lamb() + dtheta, psi, indnan = _comp_optics.calc_psidthetaphi_from_pts_lamb( + pts, self._dgeom['center'], self._dgeom['rcurve'], + bragg, nlamb, npts, + self._dgeom['nout'], self._dgeom['e1'], self._dgeom['e2'], + self._dgeom['extenthalf'], ntheta=ntheta) + + import ipdb; ipdb.set_trace() # DB + bragg = np.repeat(np.repeat(bragg[:, None], npts, axis=-1)[..., None], + ntheta, axis=-1) + bragg[indnan] = np.nan + phi[ind] = self.calc_braggphi_from_pts(pts, + dtheta=dtheta[ind], + dpsi=dpsi[ind]) return dtheta, psi, phi, bragg diff --git a/tofu/version.py b/tofu/version.py index 22a278f5f..2055b7bbd 100644 --- a/tofu/version.py +++ b/tofu/version.py @@ -1,2 +1,2 @@ # Do not edit, pipeline versioning governed by git tags! -__version__ = '1.4.2-a5-40-gb0dac67' +__version__ = '1.4.2-a5-41-ge6b26cc' From 202c2cf9959c23a45daf5b75cd42c1fe8f4955ef Mon Sep 17 00:00:00 2001 From: VEZINET Didier Date: Tue, 17 Dec 2019 08:23:23 +0100 Subject: [PATCH 21/27] [Issue202] Continued calc_phibragg_from_pts(), updating related methods, TBF --- tofu/geom/_comp_optics.py | 5 +++-- tofu/geom/_core_optics.py | 18 ++++++++---------- tofu/version.py | 2 +- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/tofu/geom/_comp_optics.py b/tofu/geom/_comp_optics.py index a5fa988e2..fe31d141a 100644 --- a/tofu/geom/_comp_optics.py +++ b/tofu/geom/_comp_optics.py @@ -212,8 +212,9 @@ def calc_xixj_from_braggphi(summit, det_cent, det_nout, det_ei, det_ej, xj = np.sum((pts - det_cent[:, None])*det_ej[:, None], axis=0) return xi, xj -def calc_braggphi_from_xixj(xi, xj, det_cent, det_ei, det_ej, - summit, nin, e1, e2, pts=None): +def calc_braggphi_from_xixjpts(det_cent, det_ei, det_ej, + summit, nin, e1, e2, + xi=None, xj=None, pts=None): if pts is None: xi = xi[None, ...] diff --git a/tofu/geom/_core_optics.py b/tofu/geom/_core_optics.py index 0ebe2a2ad..f7e4adfcd 100644 --- a/tofu/geom/_core_optics.py +++ b/tofu/geom/_core_optics.py @@ -1195,6 +1195,7 @@ def _checkformat_psidtheta(psi=None, dtheta=None, msg = "psi and dtheta must be 1d arrays fo same size!" raise Exception(msg) + def calc_phibragg_from_xixj(self, xi, xj, n=None, det_cent=None, det_ei=None, det_ej=None, dtheta=None, psi=None, @@ -1210,13 +1211,7 @@ def calc_phibragg_from_xixj(self, xi, xj, n=None, lamb=self._DEFLAMB) # Get local summit nout, e1, e2 if non-centered - psi, dtheta = self._checkformat_psidtheta(psi=psi, dtheta=dtheta) - summit, nout, e1, e2 = self.get_local_noute1e2(dtheta, psi) - - # Compute - bragg, phi = _comp_optics.calc_braggphi_from_xixj( - xii, xjj, det_cent, det_ei, det_ej, - summit, -nout, e1, e2) + bragg, phi = self.calc_phibragg_from_pts(pts) if plot != False: lax = _plot_optics.CrystalBragg_plot_braggangle_from_xixj( @@ -1227,6 +1222,8 @@ def calc_phibragg_from_xixj(self, xi, xj, n=None, braggunits='deg', angunits='deg', **kwdargs) return bragg, phi + + # DEPRECATED ??? def calc_phibragg_from_pts_on_summit(self, pts, n=None): """ Return the bragg angle and phi of pts from crystal summit @@ -1392,9 +1389,10 @@ def calc_phibragg_from_pts(self, pts, dtheta=None, dpsi=None): summit, nout, e1, e2 = self.get_local_noute1e2(dtheta, psi) # Compute - bragg, phi = _comp_optics.calc_braggphi_from_xixj( - xii, xjj, det_cent, det_ei, det_ej, - summit, -nout, e1, e2) + bragg, phi = _comp_optics.calc_braggphi_from_xixjpts( + det_cent, det_ei, det_ej, + summit, -nout, e1, e2, pts=pts) + return phi, bragg def get_lamb_avail_from_pts(self, pts): diff --git a/tofu/version.py b/tofu/version.py index 2055b7bbd..25373e617 100644 --- a/tofu/version.py +++ b/tofu/version.py @@ -1,2 +1,2 @@ # Do not edit, pipeline versioning governed by git tags! -__version__ = '1.4.2-a5-41-ge6b26cc' +__version__ = '1.4.2-a5-42-ge1f9f11' From 6ffce17966f494bde5f8c6b4c97a61e5a650fb8d Mon Sep 17 00:00:00 2001 From: VEZINET Didier Date: Wed, 18 Dec 2019 11:46:39 +0100 Subject: [PATCH 22/27] [Issue202] Minor changes for better robustness in tofu/geom/_core.py and tofu/imas2tofu/_core.py --- tofu/geom/_core.py | 8 +++++--- tofu/imas2tofu/_core.py | 10 +++++----- tofu/version.py | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/tofu/geom/_core.py b/tofu/geom/_core.py index 4e4021a11..f1384dc2e 100644 --- a/tofu/geom/_core.py +++ b/tofu/geom/_core.py @@ -4141,6 +4141,11 @@ def _complete_dX12(self, dgeom): pinhole = dgeom['D'][:, 0] + k[0]*u[:, 0] dgeom['pinhole'] = pinhole + if np.any(np.isnan(dgeom['D'])): + msg = ("Some LOS have nan as starting point !\n" + + "The geometry may not be provided !") + raise Exception(msg) + # Test if all D are on a common plane or line va = dgeom["D"] - dgeom["D"][:, 0:1] @@ -4209,9 +4214,6 @@ def _complete_dX12(self, dgeom): dgeom["dX12"] = {} dgeom["dX12"].update({"nIn": nIn, "e1": e1, "e2": e2}) - if not self._is2D(): - return dgeom - # Test binning if dgeom["pinhole"] is not None: k1ref = -np.sum( diff --git a/tofu/imas2tofu/_core.py b/tofu/imas2tofu/_core.py index 1145db24c..612a17b67 100644 --- a/tofu/imas2tofu/_core.py +++ b/tofu/imas2tofu/_core.py @@ -1508,10 +1508,10 @@ def add_ids_base(self, occ=None, idd=None, user=user, tokamak=tokamak, version=version, ref=ref, isget=isget, get=get) - def add_ids_for_synthdiag(self, ids=None, occ=None, idd=None, - shot=None, run=None, refshot=None, refrun=None, - user=None, tokamak=None, version=None, - ref=None, isget=None, get=None): + def add_ids_synthdiag(self, ids=None, occ=None, idd=None, + shot=None, run=None, refshot=None, refrun=None, + user=None, tokamak=None, version=None, + ref=None, isget=None, get=None): """ Add pre-tabulated input ids necessary for calculating synth. signal The necessary input ids are given by self.get_inputs_for_synthsignal() @@ -2222,7 +2222,7 @@ def _get_t0(self, t0=None): def to_Config(self, Name=None, occ=None, indDescription=None, plot=True): lidsok = ['wall'] if indDescription is None: - indDescription = 2 + indDescription = 0 # --------------------------- # Preliminary checks on data source consistency diff --git a/tofu/version.py b/tofu/version.py index 25373e617..913701f10 100644 --- a/tofu/version.py +++ b/tofu/version.py @@ -1,2 +1,2 @@ # Do not edit, pipeline versioning governed by git tags! -__version__ = '1.4.2-a5-42-ge1f9f11' +__version__ = '1.4.2-a5-43-g202c2cf' From 39b1a00fd86379b8cc6afdaed7a74419d33afe24 Mon Sep 17 00:00:00 2001 From: VEZINET Didier Date: Wed, 18 Dec 2019 12:19:47 +0100 Subject: [PATCH 23/27] [Issue202] PEP8 Compliance 1 --- tofu/geom/_comp_optics.py | 14 ++++++++++---- tofu/version.py | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/tofu/geom/_comp_optics.py b/tofu/geom/_comp_optics.py index fe31d141a..3f31e95d1 100644 --- a/tofu/geom/_comp_optics.py +++ b/tofu/geom/_comp_optics.py @@ -125,6 +125,7 @@ def get_approx_detector_rel(rcurve, bragg, tangent_to_rowland=None): det_ei_rel = np.r_[np.cos(bragg), np.sin(bragg), 0] return det_dist, n_crystdet_rel, det_nout_rel, det_ei_rel + def get_det_abs_from_rel(det_dist, n_crystdet_rel, det_nout_rel, det_ei_rel, summit, nout, e1, e2, ddist=None, di=None, dj=None, @@ -188,6 +189,7 @@ def checkformat_vectang(Z, nn, frame_cent, frame_ang): return Z, nn, frame_cent, frame_ang + def get_e1e2_detectorplane(nn, nIn): e1 = np.cross(nn, nIn) e1n = np.linalg.norm(e1) @@ -199,6 +201,7 @@ def get_e1e2_detectorplane(nn, nIn): e2 = e2 / np.linalg.norm(e2) return e1, e2 + def calc_xixj_from_braggphi(summit, det_cent, det_nout, det_ei, det_ej, nout, e1, e2, bragg, phi): sp = (det_cent - summit) @@ -212,6 +215,7 @@ def calc_xixj_from_braggphi(summit, det_cent, det_nout, det_ei, det_ej, xj = np.sum((pts - det_cent[:, None])*det_ej[:, None], axis=0) return xi, xj + def calc_braggphi_from_xixjpts(det_cent, det_ei, det_ej, summit, nin, e1, e2, xi=None, xj=None, pts=None): @@ -228,7 +232,8 @@ def calc_braggphi_from_xixjpts(det_cent, det_ei, det_ej, summit = summit[:, None, None] det_cent = det_cent[:, None, None] det_ei, det_ej = det_ei[:, None, None], det_ej[:, None, None] - nin, e1, e2 = nin[:, None, None], e1[:, None, None], e2[:, None, None] + nin, e1, e2 = (nin[:, None, None], + e1[:, None, None], e2[:, None, None]) pts = det_cent + xi*det_ei + xj*det_ej else: assert pts.ndim == 2 @@ -243,6 +248,7 @@ def calc_braggphi_from_xixjpts(det_cent, det_ei, det_ej, phi = np.arctan2(np.sum(vect*e2, axis=0), np.sum(vect*e1, axis=0)) return bragg, phi + def get_lambphifit(lamb, phi, nxi, nxj): lambD = lamb.max()-lamb.min() lambfit = lamb.min() +lambD*np.linspace(0, 1, nxi) @@ -310,11 +316,11 @@ def calc_psidthetaphi_from_pts_lamb(pts, center, rcurve, ntheta, axis=-1)[ind] dtheta[ind] = np.repeat( np.repeat(extenthalf[1]*np.linspace(-1, 1, ntheta)[None, :], - npts, axis=0)[None, ...], + npts, axis=0)[None, ...], nlamb, axis=0)[ind] - psi[ind] = (np.arccos((scaPCem[ind] - - Z*np.sin(dtheta[ind]))/(XYnorm*np.cos(dtheta[ind]))) + psi[ind] = (np.arccos( + (scaPCem[ind] - Z*np.sin(dtheta[ind]))/(XYnorm*np.cos(dtheta[ind]))) + angextra) psi[ind] = np.arctan2(np.sin(psi[ind]), np.cos(psi[ind])) indnan = (~ind) | (np.abs(psi) > extenthalf[0]) diff --git a/tofu/version.py b/tofu/version.py index 913701f10..128bbf124 100644 --- a/tofu/version.py +++ b/tofu/version.py @@ -1,2 +1,2 @@ # Do not edit, pipeline versioning governed by git tags! -__version__ = '1.4.2-a5-43-g202c2cf' +__version__ = '1.4.2-a5-44-g6ffce17' From 29de9d5e14654870778eabaa7b48dc4026cbf221 Mon Sep 17 00:00:00 2001 From: VEZINET Didier Date: Wed, 18 Dec 2019 12:27:28 +0100 Subject: [PATCH 24/27] [Issue202] PEP8 Compliance 2 --- tofu/geom/_core_optics.py | 57 +++++++++++++++++++++------------------ tofu/version.py | 2 +- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/tofu/geom/_core_optics.py b/tofu/geom/_core_optics.py index f7e4adfcd..8762194d9 100644 --- a/tofu/geom/_core_optics.py +++ b/tofu/geom/_core_optics.py @@ -332,8 +332,10 @@ def _checkformat_dbragg(cls, dbragg=None): try: if drock.get('sigma') is not None: dbragg['rockingcurve']['sigma'] = float(drock['sigma']) - dbragg['rockingcurve']['deltad'] = float(drock.get('deltad', 0.)) - dbragg['rockingcurve']['Rmax'] = float(drock.get('Rmax', 1.)) + dbragg['rockingcurve']['deltad'] = float( + drock.get('deltad', 0.)) + dbragg['rockingcurve']['Rmax'] = float( + drock.get('Rmax', 1.)) dbragg['rockingcurve']['type'] = 'lorentz-log' elif drock.get('dangle') is not None: c2d = (drock.get('lamb') is not None @@ -344,17 +346,19 @@ def _checkformat_dbragg(cls, dbragg=None): msg = ("Tabulated 2d rocking curve should be:\n" + "\tshape = (dangle.size, lamb.size)") raise Exception(msg) - dbragg['rockingcurve']['dangle'] = np.r_[drock['dangle']] + dbragg['rockingcurve']['dangle'] = np.r_[ + drock['dangle']] dbragg['rockingcurve']['lamb'] = np.r_[drock['lamb']] dbragg['rockingcurve']['value'] = drock['value'] dbragg['rockingcurve']['type'] = 'tabulated-2d' else: if drock.get('lamb') is None: - msg = ("Please also specify the lamb for which the " - + "rocking curve was tabulated!") + msg = ("Please also specify the lamb for which " + + "the rocking curve was tabulated!") raise Exception(msg) dbragg['rockingcurve']['lamb'] = float(drock['lamb']) - dbragg['rockingcurve']['dangle'] = np.r_[drock['dangle']] + dbragg['rockingcurve']['dangle'] = np.r_[ + drock['dangle']] dbragg['rockingcurve']['value'] = np.r_[drock['value']] dbragg['rockingcurve']['type'] = 'tabulated-1d' if drock.get('source') is None: @@ -652,7 +656,8 @@ def get_summary(self, sep=' ', line='-', just='l', # ----------------------- # Build material col0 = ['formula', 'symmetry', 'cut', 'density', - 'd (A)', 'bragg(%s A) (deg)'%str(self._DEFLAMB), 'rocking curve'] + 'd (A)', 'bragg({:7.4} A) (deg)'.format(self._DEFLAMB*1e10), + 'rocking curve'] ar0 = [self._dmat['formula'], self._dmat['symmetry'], str(self._dmat['cut']), str(self._dmat['density']), '{0:5.3f}'.format(self._dmat['d']*1.e10), @@ -783,11 +788,13 @@ def get_rockingcurve_func(self, lamb=None, n=None): + " => Please set lamb accordingly") raise Exception(msg) bragg = self._checkformat_bragglamb(lamb=lamb, n=n) + def func(angle, lamb=lamb, bragg=bragg, drock=drock): return scpinterp.interp2d(drock['dangle']+bragg, drock['lamb'], drock['value'], kind='linear', bounds_error=False, fill_value=0, assume_sorted=True)(angle, lamb) + else: def func(angle, d=d, delta_bragg=delta_bragg, Rmax=drock['Rmax'], sigma=drock['sigma']): @@ -1222,7 +1229,6 @@ def calc_phibragg_from_xixj(self, xi, xj, n=None, braggunits='deg', angunits='deg', **kwdargs) return bragg, phi - # DEPRECATED ??? def calc_phibragg_from_pts_on_summit(self, pts, n=None): """ Return the bragg angle and phi of pts from crystal summit @@ -1237,7 +1243,8 @@ def calc_phibragg_from_pts_on_summit(self, pts, n=None): # Compute vect = pts - self._dgeom['summit'][:, None] vect = vect / np.sqrt(np.sum(vect**2, axis=0)) - bragg = np.pi/2 - np.arccos(np.sum(vect*self._dgeom['nin'][:, None], axis=0)) + bragg = np.pi/2 - np.arccos(np.sum(vect*self._dgeom['nin'][:, None], + axis=0)) v1 = np.sum(vect*self._dgeom['e1'][:, None], axis=0) v2 = np.sum(vect*self._dgeom['e2'][:, None], axis=0) phi = np.arctan2(v2, v1) @@ -1287,7 +1294,7 @@ def plot_line_tracing_on_det(self, lamb=None, n=None, det_ei=det_ei, det_ej=det_ej, plot=False) # Get johann-error raytracing (multiple positions on crystal) - xi_err, xj_err = None, None + xi_er, xj_er = None, None if johann and not rocking: if lpsi is None or ltheta is None: lpsi = np.linspace(-1., 1., 15) @@ -1300,12 +1307,12 @@ def plot_line_tracing_on_det(self, lamb=None, n=None, npsi = lpsi.size assert npsi == ltheta.size - xi_err = np.full((nlamb, npsi*nphi), np.nan) - xj_err = np.full((nlamb, npsi*nphi), np.nan) + xi_er = np.full((nlamb, npsi*nphi), np.nan) + xj_er = np.full((nlamb, npsi*nphi), np.nan) for l in range(nlamb): for ii in range(npsi): i0 = np.arange(ii*nphi, (ii+1)*nphi) - xi_err[l, i0], xj_err[l, i0] = self.calc_xixj_from_phibragg( + xi_er[l, i0], xj_er[l, i0] = self.calc_xixj_from_phibragg( phi=phi, bragg=bragg[l], lamb=None, n=n, theta=ltheta[ii], psi=lpsi[ii], det_cent=det_cent, det_nout=det_nout, @@ -1317,7 +1324,7 @@ def plot_line_tracing_on_det(self, lamb=None, n=None, # Plot ax = _plot_optics.CrystalBragg_plot_line_tracing_on_det( - lamb, xi, xj, xi_err, xj_err, det=det, + lamb, xi, xj, xi_er, xj_er, det=det, johann=johann, rocking=rocking, fs=fs, dmargin=dmargin, wintit=wintit, tit=tit) @@ -1341,7 +1348,6 @@ def calc_johannerror(self, xi=None, xj=None, err=None, """ - # Check / format inputs xi, xj, (xii, xjj) = self._checkformat_xixj(xi, xj) nxi = xi.size if xi is not None else np.unique(xii).size @@ -1394,11 +1400,9 @@ def calc_phibragg_from_pts(self, pts, dtheta=None, dpsi=None): summit, -nout, e1, e2, pts=pts) return phi, bragg - def get_lamb_avail_from_pts(self, pts): pass - def calc_thetapsi_from_lambpts(self, pts=None, lamb=None, n=None, ntheta=None): @@ -1418,7 +1422,7 @@ def calc_thetapsi_from_lambpts(self, pts=None, lamb=None, n=None, self._dgeom['nout'], self._dgeom['e1'], self._dgeom['e2'], self._dgeom['extenthalf'], ntheta=ntheta) - import ipdb; ipdb.set_trace() # DB + # import ipdb; ipdb.set_trace() # DB bragg = np.repeat(np.repeat(bragg[:, None], npts, axis=-1)[..., None], ntheta, axis=-1) bragg[indnan] = np.nan @@ -1428,7 +1432,6 @@ def calc_thetapsi_from_lambpts(self, pts=None, lamb=None, n=None, return dtheta, psi, phi, bragg - def plot_line_from_pts_on_det(self, lamb=None, pts=None, xi_bounds=None, xj_bounds=None, nphi=None, det_cent=None, det_nout=None, @@ -1473,7 +1476,7 @@ def plot_line_from_pts_on_det(self, lamb=None, pts=None, det_ei=det_ei, det_ej=det_ej, plot=False) # Get johann-error raytracing (multiple positions on crystal) - xi_err, xj_err = None, None + xi_er, xj_er = None, None if johann and not rocking: if lpsi is None or ltheta is None: lpsi = np.linspace(-1., 1., 15) @@ -1486,12 +1489,12 @@ def plot_line_from_pts_on_det(self, lamb=None, pts=None, npsi = lpsi.size assert npsi == ltheta.size - xi_err = np.full((nlamb, npsi*nphi), np.nan) - xj_err = np.full((nlamb, npsi*nphi), np.nan) + xi_er = np.full((nlamb, npsi*nphi), np.nan) + xj_er = np.full((nlamb, npsi*nphi), np.nan) for l in range(nlamb): for ii in range(npsi): i0 = np.arange(ii*nphi, (ii+1)*nphi) - xi_err[l, i0], xj_err[l, i0] = self.calc_xixj_from_phibragg( + xi_er[l, i0], xj_er[l, i0] = self.calc_xixj_from_phibragg( phi=phi, bragg=bragg[l], lamb=None, n=n, theta=ltheta[ii], psi=lpsi[ii], det_cent=det_cent, det_nout=det_nout, @@ -1503,7 +1506,7 @@ def plot_line_from_pts_on_det(self, lamb=None, pts=None, # Plot ax = _plot_optics.CrystalBragg_plot_line_tracing_on_det( - lamb, xi, xj, xi_err, xj_err, det=det, + lamb, xi, xj, xi_er, xj_er, det=det, johann=johann, rocking=rocking, fs=fs, dmargin=dmargin, wintit=wintit, tit=tit) @@ -1539,10 +1542,12 @@ def plot_data_vs_lambphi(self, xi=None, xj=None, data=None, mask=None, nlambfit, nphifit) lambfitbins = 0.5*(lambfit[1:] + lambfit[:-1]) ind = np.digitize(lamb, lambfitbins) - spect1d = np.array([np.nanmean(data[ind==ii]) for ii in np.unique(ind)]) + spect1d = np.array([np.nanmean(data[ind == ii]) + for ii in np.unique(ind)]) phifitbins = 0.5*(phifit[1:] + phifit[:-1]) ind = np.digitize(phi, phifitbins) - vertsum1d = np.array([np.nanmean(data[ind==ii]) for ii in np.unique(ind)]) + vertsum1d = np.array([np.nanmean(data[ind == ii]) + for ii in np.unique(ind)]) # Get phiref from mag axis lambax, phiax = None, None diff --git a/tofu/version.py b/tofu/version.py index 128bbf124..088b54876 100644 --- a/tofu/version.py +++ b/tofu/version.py @@ -1,2 +1,2 @@ # Do not edit, pipeline versioning governed by git tags! -__version__ = '1.4.2-a5-44-g6ffce17' +__version__ = '1.4.2-a5-45-g39b1a00' From b5d91931952e15c9fb4528915e6b6f5170d8a170 Mon Sep 17 00:00:00 2001 From: VEZINET Didier Date: Wed, 18 Dec 2019 12:30:56 +0100 Subject: [PATCH 25/27] [Issue202] PEP8 Compliance 3 --- tofu/geom/_plot_optics.py | 42 +++++++++++++++++++-------------------- tofu/version.py | 2 +- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/tofu/geom/_plot_optics.py b/tofu/geom/_plot_optics.py index e166d6a55..9c35ad098 100644 --- a/tofu/geom/_plot_optics.py +++ b/tofu/geom/_plot_optics.py @@ -142,6 +142,7 @@ def CrystalBragg_plot(cryst, lax=None, proj=None, res=None, element=None, ax0.figure.canvas.draw() return dax + def _CrystalBragg_plot_crosshor(cryst, proj=None, dax=None, element=None, res=None, det_cent=None, det_nout=None, @@ -238,16 +239,16 @@ def _CrystalBragg_plot_crosshor(cryst, proj=None, dax=None, p0 = np.repeat(summ[:,None], 3, axis=1) v = np.concatenate((nin[:, None], e1[:, None], e2[:, None]), axis=1) if dax['cross'] is not None: - pr = np.hypot(p0[0,:], p0[1,:]) - vr = np.hypot(p0[0,:]+v[0,:], p0[1,:]+v[1,:]) - pr - dax['cross'].quiver(pr, p0[2,:], - vr, v[2,:], + pr = np.hypot(p0[0, :], p0[1, :]) + vr = np.hypot(p0[0, :]+v[0, :], p0[1, :]+v[1, :]) - pr + dax['cross'].quiver(pr, p0[2, :], + vr, v[2, :], np.r_[0., 0.5, 1.], cmap=quiver_cmap, angles='xy', scale_units='xy', label=cryst.Id.NameLTX+" unit vect", **Vdict) if dax['hor'] is not None: - dax['hor'].quiver(p0[0,:], p0[1,:], - v[0,:], v[1,:], + dax['hor'].quiver(p0[0, :], p0[1, :], + v[0, :], v[1, :], np.r_[0., 0.5, 1.], cmap=quiver_cmap, angles='xy', scale_units='xy', label=cryst.Id.NameLTX+" unit vect", **Vdict) @@ -268,16 +269,16 @@ def _CrystalBragg_plot_crosshor(cryst, proj=None, dax=None, v = np.concatenate((det_nout[:, None], det_ei[:, None], det_ej[:, None]), axis=1) if dax['cross'] is not None: - pr = np.hypot(p0[0,:], p0[1,:]) - vr = np.hypot(p0[0,:]+v[0,:], p0[1,:]+v[1,:]) - pr - dax['cross'].quiver(pr, p0[2,:], - vr, v[2,:], + pr = np.hypot(p0[0, :], p0[1, :]) + vr = np.hypot(p0[0, :]+v[0, :], p0[1, :]+v[1, :]) - pr + dax['cross'].quiver(pr, p0[2, :], + vr, v[2, :], np.r_[0., 0.5, 1.], cmap=quiver_cmap, angles='xy', scale_units='xy', label="det unit vect", **Vdict) if dax['hor'] is not None: - dax['hor'].quiver(p0[0,:], p0[1,:], - v[0,:], v[1,:], + dax['hor'].quiver(p0[0, :], p0[1, :], + v[0, :], v[1, :], np.r_[0., 0.5, 1.], cmap=quiver_cmap, angles='xy', scale_units='xy', label="det unit vect", **Vdict) @@ -407,7 +408,8 @@ def CrystalBragg_plot_braggangle_from_xixj(xi=None, xj=None, def CrystalBragg_plot_line_tracing_on_det(lamb, xi, xj, xi_err, xj_err, det=None, johann=None, rocking=None, - fs=None, dmargin=None, wintit=None, tit=None): + fs=None, dmargin=None, + wintit=None, tit=None): # Check inputs # ------------ @@ -415,9 +417,9 @@ def CrystalBragg_plot_line_tracing_on_det(lamb, xi, xj, xi_err, xj_err, if fs is None: fs = (6, 8) if dmargin is None: - dmargin = {'left':0.05, 'right':0.99, - 'bottom':0.06, 'top':0.92, - 'wspace':None, 'hspace':0.4} + dmargin = {'left': 0.05, 'right': 0.99, + 'bottom': 0.06, 'top': 0.92, + 'wspace': None, 'hspace': 0.4} if wintit is None: wintit = _WINTIT @@ -455,8 +457,6 @@ def CrystalBragg_plot_line_tracing_on_det(lamb, xi, xj, xi_err, xj_err, return [ax0] - - def CrystalBragg_plot_johannerror(xi, xj, lamb, phi, err_lamb, err_phi, cmap=None, vmin=None, vmax=None, fs=None, dmargin=None, wintit=None, tit=None, @@ -470,9 +470,9 @@ def CrystalBragg_plot_johannerror(xi, xj, lamb, phi, err_lamb, err_phi, if cmap is None: cmap = plt.cm.viridis if dmargin is None: - dmargin = {'left':0.05, 'right':0.99, - 'bottom':0.06, 'top':0.92, - 'wspace':None, 'hspace':0.4} + dmargin = {'left': 0.05, 'right': 0.99, + 'bottom': 0.06, 'top': 0.92, + 'wspace': None, 'hspace': 0.4} assert angunits in ['deg', 'rad'] if angunits == 'deg': # bragg = bragg*180./np.pi diff --git a/tofu/version.py b/tofu/version.py index 088b54876..f08e97218 100644 --- a/tofu/version.py +++ b/tofu/version.py @@ -1,2 +1,2 @@ # Do not edit, pipeline versioning governed by git tags! -__version__ = '1.4.2-a5-45-g39b1a00' +__version__ = '1.4.2-a5-46-g29de9d5' From ac0016bdcba1e1f4704f97474883c3b5ea50d011 Mon Sep 17 00:00:00 2001 From: VEZINET Didier Date: Wed, 18 Dec 2019 12:35:15 +0100 Subject: [PATCH 26/27] [Issue202] PEP8 Compliance 4 --- tofu/utils.py | 23 ++++++++++++----------- tofu/version.py | 2 +- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/tofu/utils.py b/tofu/utils.py index 58bf191e0..d47b5494a 100644 --- a/tofu/utils.py +++ b/tofu/utils.py @@ -667,7 +667,7 @@ def load_from_imas(shot=None, run=None, user=None, tokamak=None, version=None, lok = ['Config', 'Plasma2D', 'Cam', 'Data'] c0 = returnas is None or returnas in lok if not c0: - msg = "Arg returnas must be in %s"%str(lok) + msg = "Arg returnas must be in {}".format(str(lok)) raise Exception(msg) # ------------------- @@ -862,15 +862,15 @@ def load_from_imas(shot=None, run=None, user=None, tokamak=None, version=None, # Config if ids[ii] == 'wall': - assert returnas[ii] in [None,'Config'] + assert returnas[ii] in [None, 'Config'] returnas[ii] = 'Config' if returnas[ii] == 'Config': - assert ids[ii] in [None,'wall'] + assert ids[ii] in [None, 'wall'] # Plasma2D lids = imas2tofu.MultiIDSLoader._lidsplasma if ids[ii] in lids: - assert returnas[ii] in [None,'Plasma2D'] + assert returnas[ii] in [None, 'Plasma2D'] returnas[ii] = 'Plasma2D' if returnas[ii] == 'Plasma2D': assert ids[ii] in lids @@ -878,19 +878,20 @@ def load_from_imas(shot=None, run=None, user=None, tokamak=None, version=None, # Cam or Data lids = imas2tofu.MultiIDSLoader._lidsdiag if ids[ii] in lids: - assert returnas[ii] in [None,'Cam','Data'] + assert returnas[ii] in [None, 'Cam', 'Data'] if returnas[ii] is None: returnas[ii] = 'Data' - if returnas[ii] in ['Cam','Data']: + if returnas[ii] in ['Cam', 'Data']: assert ids[ii] in lids - dout = {shot[jj]: {oo:[] for oo in set(returnas)} for jj in range(0,nshot)} + dout = {shot[jj]: {oo:[] for oo in set(returnas)} + for jj in range(0, nshot)} # ------------------- # Prepare plot_ and complement ids - lPla = [ii for ii in range(0,nids) if returnas[ii] == 'Plasma2D'] - lCam = [ii for ii in range(0,nids) if returnas[ii] == 'Cam'] - lDat = [ii for ii in range(0,nids) if returnas[ii] == 'Data'] + lPla = [ii for ii in range(0, nids) if returnas[ii] == 'Plasma2D'] + lCam = [ii for ii in range(0, nids) if returnas[ii] == 'Cam'] + lDat = [ii for ii in range(0, nids) if returnas[ii] == 'Data'] nPla, nCam, nDat = len(lPla), len(lCam), len(lDat) if nDat > 1: plot_ = False @@ -1886,7 +1887,7 @@ def Id(self): return self._Id def _get_dId(self, sep=None): - return {'dict':self.Id.to_dict(sep=sep)} + return {'dict': self.Id.to_dict(sep=sep)} def _reset(self): if hasattr(self,'_Id'): diff --git a/tofu/version.py b/tofu/version.py index f08e97218..a444f573d 100644 --- a/tofu/version.py +++ b/tofu/version.py @@ -1,2 +1,2 @@ # Do not edit, pipeline versioning governed by git tags! -__version__ = '1.4.2-a5-46-g29de9d5' +__version__ = '1.4.2-a5-47-gb5d9193' From c31f86b1f838856955686dc16e5a11ad8824f60c Mon Sep 17 00:00:00 2001 From: VEZINET Didier Date: Wed, 18 Dec 2019 12:39:16 +0100 Subject: [PATCH 27/27] [Issue202] PEP8 Compliance 5 --- tofu/utils.py | 2 +- tofu/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tofu/utils.py b/tofu/utils.py index d47b5494a..4b6289428 100644 --- a/tofu/utils.py +++ b/tofu/utils.py @@ -884,7 +884,7 @@ def load_from_imas(shot=None, run=None, user=None, tokamak=None, version=None, if returnas[ii] in ['Cam', 'Data']: assert ids[ii] in lids - dout = {shot[jj]: {oo:[] for oo in set(returnas)} + dout = {shot[jj]: {oo: [] for oo in set(returnas)} for jj in range(0, nshot)} # ------------------- diff --git a/tofu/version.py b/tofu/version.py index a444f573d..4fc937bc2 100644 --- a/tofu/version.py +++ b/tofu/version.py @@ -1,2 +1,2 @@ # Do not edit, pipeline versioning governed by git tags! -__version__ = '1.4.2-a5-47-gb5d9193' +__version__ = '1.4.2-a5-48-gac0016b'