tofu.plugins.AUG.SXR.geom._core — tofu v1.1

# Source code for tofu.plugins.AUG.SXR.geom._core

    # -*- coding: utf-8 -*-
    """
    Provides the basic ToFu geometry handling for the SXR diagnostic of AUG
    """

    import itertools as itt
    import os
    import numpy as np
    import math
    import datetime as dtm
    import warnings

    # ToFu specific
    import tofu.defaults as tfd
    import tofu.pathfile as tfpf
    import tofu.helper as tfh
    import tofu.geom as tfg
    from ... import Ves as tfaugVes
    from ... import _path as _tfaug_path
    from .. import _helper as tfaugSXRh

    # AUG specific
    import dd

    __author__ =    "D. Vezinet"
    __all__ = ["create","load","get_GeomFromShot"]

    _addpath = '/tofu/plugins/AUG/SXR/geom'

    #########################################################
    #########################################################
    ### Storing changes in geometry in a dictionary #########
    #########################################################

    def _compareDictChan(Dict1, Dict2):
        Crit = ['FiltThick','RPINHOLE','ZPINHOLE','REND','ZEND','Tor_Pos','CAMANGLE','P_Length','P_Width','Foc_Len','D_Length','D_Width','D_Gap','MULTIA02']
        Lim = [5.e-6, 0.5e-3, 0.5e-3, 0.5e-3, 0.5e-3, 0.5, 0.5, 0.1, 0.01, 0.1, 0.1, 0.01, 0.001, 1.e6]
        CritCh = []
        Change = {}

        Names1 = sorted(Dict1.keys())
        Names2 = sorted(Dict2.keys())
        Names = sorted(list(set(Names1+Names2)))

        for nn in Names:
            Change[nn] = {}

            if not nn in Names1:
                Change[nn] = Dict2[nn]

            elif not nn in Names2:
                Change[nn] = Dict1[nn]

            else:
                for ii in range(0,len(Crit)):
                    diff = math.fabs(float(Dict1[nn][Crit[ii]]-Dict2[nn][Crit[ii]]))
                    if diff > Lim[ii]:
                        Change[nn][Crit[ii]] = diff

            if len(Change[nn].keys())==0:
                del Change[nn]

        return Change

    def _get_GeomFromShot_CSX(shot, Nums, CamH, Chan='CamHeads', Verb=True):
        assert Chan in ['All','CamHeads'] or (type(Chan) is list and all([type(cc) is str for cc in Chan])), "Arg Chan must be in ['All','CamHeads'] or a list of channel names !"

        DictChan = {}
        sh = dd.shotfile('CSX',shot)

        # Which channels shall be used for the testing
        if Chan=='All':
            Dir = sh.getObjectNames()
        elif Chan=='CamHeads':  # One per camera head
            Dir = ['CF_020','CG_020','CH_020','CH_052','CH_087','CI_016','CI_052','CI_091','CJ_017','CJ_052','CJ_087','CK_020','CK_055','CL_020','CM_020']
        else:   # Custom
            for ii in range(0,len(Chan)):
                if not Chan[ii][0]=='C':
                    Chan[ii] = 'C'+Chan[ii]
            Dir = Chan
        Dir = [Dir[ii] for ii in range(0,len(Dir)) if all([ss in Dir[ii] for ss in ['C','_']]) and all([nn in Nums for nn in Dir[ii][-3:]])]

        # Do the testing
        LNames = []
        for ii in range(0,len(Dir)):
            try:
                Name = Dir[ii][1:]
                load = sh('C'+Name)
                Diag = str(load['SX_DIAG'].data[0]+load['SX_DIAG'].data[1]+load['SX_DIAG'].data[2])
                if not Diag=='000' and Name in CamH.keys():
                    tfaugSXRh._get_DictChanFromload(DictChan, Name, load, CamH, Diag)
                    if Verb:
                        print("        Done for "+Name)
            except Exception:
                pass
        sh.close()
        return DictChan

    def _get_GeomFromShot_CSXscan( Lshots=range(22000,40000,1), Chan='CamHeads', Verb=True ):

        CamH = tfaugSXRh._CamHeads()

        DictShot = {}
        DictChan0 = {}
        DictChan1 = {}
        init = True
        Nums = map(str,range(0,10))
        Change = {}

        Inshots = [True for ss in Lshots]
        for ii in range(0,len(Lshots)):
            shot = int(Lshots[ii])
            success = False
            while not success:
                try:
                    if init:
                        DictChan0 = _get_GeomFromShot_CSX(shot, Nums, CamH, Chan=Chan, Verb=Verb)
                        init = False
                        if Verb:
                            print("")
                            print("    Initiated with shot {0}".format(shot))
                        minshot = int(shot)
                        Lshots[ii] = int(shot)
                    elif shot>minshot:
                        DictChan1 = _get_GeomFromShot_CSX(shot, Nums, CamH, Chan=Chan, Verb=Verb)
                        change = _compareDictChan(DictChan0, DictChan1)
                        if len(change.keys())>0:
                            Change[shot] = change
                            DictChan0 = dict(DictChan1)
                            if Verb:
                                print("    Change detected for shot {0}".format(shot))
                        minshot = int(shot)
                        Lshots[ii] = int(shot)
                    else:
                        Inshots[ii] = False
                    success = True
                except Exception:
                    if ii==len(Lshots)-1:
                        shot = shot-1
                    else:
                        shot = shot+1
        Lshots = [Lshots[ii] for ii in range(0,len(Lshots)) if Inshots[ii]]
        return Change, Lshots

    def get_GeomFromShot( shot_init=22006, shot_end=33730, Ds=1000, Chan='CamHeads', Verb=True, save=True ):
        """ Scan the database using diagnostic 'CSX' to identify the shots where changes were made to the SXR diagnostic

        The geometry of a diagnostic changes from year to year due to improvements, adjustments...
        It is necessary to know from which shot a new geometry is valid.
        This function uses the 'CSX' data to scan the stored geometry (and other parameters) of all channels of the SXR diagnostic on a large span of shots to identify the first shots presenting changes.
        Beware... it is long (typically several hours for a scann of all channels on a span of several thousands of shots).

        Parameters
        ----------
        shot_init :     int
            The first shot of the interval to be scanned
        shot_end :      int
            The last shot of the interval to be scanned
        Ds :            10*int
            The step by which the shot numbers will be increased for scanning (initial value, an algorithm then reduces it by dividing by 10 successively to focus on the identified changes)
        Verb :          bool
            Flag indicating whether some extra comments should be printed
        save :          bool
            Flag indicating whether the result should be saved

        Returns
        -------
        Change :        dict
            A dictionary of changes, where the keys are the shot numbers identified as first to change and where the values are themselves dictionaries of the changes implemented
        Array :        np.ndarray
            (2,N) np.ndarray where the first line is the list of shots tested in the last iteration and the second line is 0 or 1 (1 if changes are observed between the corresponding shot and its predecessor)

        """

        Lshots = sorted(list(set(range(shot_init, shot_end+1, Ds)+[shot_end])))
        ds = int(Ds)
        while not ds<1:
            if Verb:
                print("")
                print("    In: ", Lshots)
            Change, Lshots = _get_GeomFromShot_CSXscan( Lshots, Chan=Chan, Verb=Verb )
            Lshots = sorted(list(set(Lshots)))
            Lch = Change.keys()

            Array = np.asarray([Lshots, [ii in Lch for ii in Lshots]])
            if Verb:
                print("    Out: ", Array)

            ds = ds/10
            if ds>=1:
                Lshots = sorted(list(set(itt.chain.from_iterable([range(Lshots[ii-1],Lshots[ii]+1,ds)+[Lshots[ii]] for ii in range(1,len(Lshots)) if Array[1,ii]]))))

        if save:
            Chanstr = Chan if type(Chan) is str else 'Custom'
            Name = 'Changes_'+Chanstr+'_shot{0}-{1}_Ds{2}'.format(shot_init,shot_end,Ds)
            np.savez(_tfaug_path._Root+'/tofu/plugins/AUG/SXR/geom/'+Name+'.npz', Change=[Change], Array=Array)

        return Change, Array

    #########################################################
    #########################################################
    ### Using the dictionary to determine relevant shots ####
    #########################################################

    def _get_GeomFromShot_New(shot, chfile='Changes_CamHeads_shot22006-33730_Ds1000.npz', chpath=_tfaug_path._Root):
        """ Return the last reference geometry shot depending on the input shot and on the Change dictionary selected

        Parameters
        ----------
        shot :      int
            Shot for which the user wants to know which geometry to use
        chfile :    str
            Name of the Changes dictionary to be used

        Returns
        -------
        Rshot:      int
            Reference geometry shot corresponding to the input shot

        """

        shot_init = int(chfile[chfile.index('_shot')+5:chfile.index('-')])
        shot_end = int(chfile[chfile.index('-')+1:chfile.index('-')+6])
        assert shot >= shot_init and shot <= shot_end, "Geometry not computed yet !"

        # Loading the Changes dictionary
        Change = np.load(chpath+_addpath+'/'+chfile)['Change'][0]
        lshots = np.unique(Change.keys())
        assert shot >= np.min(lshots) and shot <= np.max(lshots), "Geometry not computed yet (stored shots in Changes) !"
        Rshot = int(np.max(lshots[lshots<=shot]))

        return Rshot

    def _get_RectifiedFromShot(shot):
        """ Get the real lower shot number from CSX

        Check whether the desired shot number is available in the database of CSX diagnostic, if not return the first following one which is

        Parameters
        -----------
        shot :      int
            Desired shot number to be checked

        Returns
        -------
        shotbis :   int
            Closest valid shot number

        """
        Done = False
        shotbis = int(shot)
        while not Done:
            try:
                sh = dd.shotfile('CSX',shotbis)
                Done = True
            except Exception:
                shotbis = shotbis + 1
        sh.close()
        return shotbis

    def _get_Tiles_FromShot(shot):
        """ Return the list of camera heads that should be affected by view-limiting tiles depending on the shot number

        In theory, the limiting tiles were removed after #31802, but in practice the gap was probably just widened (but how much ?)

        Parameters
        ----------
        shot :  int
            The shot number for which to return the list

        Returns
        -------
        Tiles :     list
            The list of camera heads with limiting tiles

        """
        Tiles = ['F','G']
        if shot<31802:
            Tiles = Tiles + ['L','M']
        return Tiles

    # Old, mot used any more (keep for backward compatibility and double checks)
    def _get_GeomFromShot(shot):     # Deprecated
        """ Deprecated """
        assert shot>=24190, "Geometry not computed yet !"
        if shot >= 24190 and shot < 24492:     # Lower bound not adjusted to minimum !
            shot, LCamH = 24190, "All"
        elif shot >= 24492 and shot < 25962:
            shot, LCamH = 24492, "All"
        elif shot >= 25962 and shot < 27439:
            shot, LCamH = 25962, "All"
        elif shot >= 27439 and shot < 31802:
            shot, LCamH = 27439, "All"
        elif shot >= 31802:   #Upper bound to be updated !
            shot, LCamH = 31802, ['L','M']
        return shot, LCamH

    #########################################################
    #########################################################
    ### Helper routines to find out which cameras have already been created ####
    #########################################################

    def _listavailCams(Exp='AUG', Diag='SXR', SavePathObj=None, Root=_tfaug_path._Root, shotstr='_sh'):
        """ Return a dictionary listing, for each camera head (keys), the shot numbers for which it was already created

        A GDetect object is created for each camera head, and several versions of it exist depending on the shot number because of geometry changes in time
        This routine tells you, for each camera head, for which shots it was already created

        Parameters
        ----------
        Exp :           str
            The experiment, here always 'AUG'
        Diag :          str
            The diagnostic, here always 'SXR'
        SavePathObj :   None / str
            The path where to look for already created GDetect objects, if None takes default
        Root : str
            The root path to which '/tofu/plugins/AUG/SXR/geom/Objects/' will be appended to build a default path if SavePathObj is None
        shotstr :       str
            The string pattern used to identify the shot number in the file names

        Returns
        -------
        DC :    dict
            The dictionary containing all relevant info

        """

        if SavePathObj is None:
            SavePathObj = Root + _addpath+ '/Objects/'

        kstr = 'TFG_GDetect_'+Exp+'_Dg'+Diag+'_'
        LC = os.listdir(SavePathObj)
        LC = [cc for cc in LC if all([ss in cc for ss in [kstr,shotstr,'.npz']])]
        DC = {}
        for ii in range(0,len(LC)):
            ind = LC[ii].index(shotstr)
            shot = int(LC[ii][ind+len(shotstr):ind+len(shotstr)+5])
            ind = LC[ii].index(kstr)
            strtemp = LC[ii][ind+len(kstr):]
            indbis = strtemp.index('_')
            CamH = strtemp[:indbis]
            if not CamH in DC.keys():
                DC[CamH] = []
            DC[CamH].append(shot)

        return DC

    #########################################################
    #########################################################
    ######## Create cameras (apertures + detectors) #########
    #########################################################

    def _getLApert( Dict, dd, Ves, shot, SavePathObj, dtime, dtFormat, Exp='AUG', Tiles=['F','G','L','M']):
        """ Return the list of apertures associated to a specific SXR channel

        In AUG, each camera head is associated to a single aperture (data stored in CSX)
        However, for some camera heads, a fraction of the VOS might be obstructed by

        Parameters
        ----------
        Dict :          dict
            Dictionary with all info from CSX, issued by :meth:`~tofu.plugins.AUG.SXR._helper._WhichSX()`
        dd :            str
            Key to Dict, should be a channel name
        Ves :           :class:`tofu.geom.Ves`
            The Ves instance with which to build the Apert objects
        shot :          int
            The shot number with which to build the Apert objects
        SavePathObj :   None / str
            The SavePath where the Apert objects should be saved (None recommended for default)
        dtime :         dtm.datetime
            A dtm.datetime object for identifying the create Apert objects (mostly for debugging)
        dtFormat :      str
            A str flag indicating the format with which dtime should be written in the automatically generated SaveName (if necessary)
        Exp :           str
            The experiment flag on which the Apert objects are created, should be the same as for Ves
        Tiles :      bool
            Flag indicating the list of caamera heads for which an additional aperture should be built from tiles (can be de-activated simply by removing the concerned camera head from the list)

        Returns
        ------
        LApert :        list
            List of apertures, created using the data available in CSX via Dict

        """

        A = np.array([Dict[dd]['RPINHOLE'], Dict[dd]['ZPINHOLE']])
        B = np.array([Dict[dd]['REND'], Dict[dd]['ZEND']])
        phi = Dict[dd]['Tor_Pos']*np.pi/180.
        theta = Dict[dd]['CAMANGLE']*np.pi/180.

        PolyApert, M = tfh.PolyFromLine(A, B, A, theta, phi, float(Dict[dd]['P_Length']*1.e-3), float(Dict[dd]['P_Width']*1.e-3))
        assert np.hypot(np.hypot(M[0],M[1])-A[0], M[2]-A[1]) < 1.e-10, "Something wrong with Apert poly !"
        IdApert0 = tfpf.ID('Apert', Dict[dd]['CamHead'], shot=shot, Diag='SXR', SaveName=None, SavePath=SavePathObj, dtime=dtime, Exp=Exp, LObj=None, dtFormat=dtFormat,
                           USRdict={'Cam':Dict[dd]['Cam'], 'CamHead':Dict[dd]['CamHead'], 'FiltMat':Dict[dd]['FiltMat'], 'FiltThick':Dict[dd]['FiltThick']})
        Apert0 = tfg.Apert(IdApert0, PolyApert, Ves=Ves)
        LApert, LIdApert = [Apert0], [IdApert0]

        # Add extra apertures (space between tiles) for the relevant cameras
        if Dict[dd]['CamHead'] in Tiles:
            if Dict[dd]['Cam']=='F':
                d = 0.034
                LTor, L2 = 0.012, 0.12
                Ang = -(np.pi/2-theta)
            elif Dict[dd]['Cam']=='G':
                d = 0.034
                LTor, L2 = 0.0117, 0.12
                Ang = -(np.pi/2-theta)
            elif Dict[dd]['Cam']=='L':
                d  = 0.040
                LTor, L2 = 0.0064, 0.2
                Ang = -36.9*np.pi/180.
            else:
                d  = 0.036
                LTor, L2 = 0.0054, 0.15
                Ang = 16.75*np.pi/180.
            C = tfh.RZ2XYZ_1D(A,phi) + d*Apert0.nIn
            e2 = np.cross(np.array([-np.sin(phi),np.cos(phi),0]),Apert0.nIn)
            e2 = e2/np.linalg.norm(e2)
            nP = Apert0.nIn*np.cos(Ang) + e2*np.sin(Ang)
            PolyApert = tfh.RectFromPlaneCenter(C, nP, np.array([-np.sin(phi),np.cos(phi),0]), LTor, L2, Test=True)
            LIdApert.append(tfpf.ID('Apert', Dict[dd]['CamHead']+"_Tiles", shot=shot, Diag='SXR', SaveName=None, SavePath=SavePathObj, dtime=dtime, Exp=Exp, LObj=None, dtFormat=dtFormat,
                                    USRdict={'Cam':Dict[dd]['Cam'],'CamHead':Dict[dd]['CamHead'],'FiltMat':None,'FiltThick':None}))
            Apert1 = tfg.Apert(LIdApert[-1], PolyApert, Ves=Ves)
            LApert.append(Apert1)
        return LApert

    def create(shot=0, VesName='V1', SavePathObj=None, Root=_tfaug_path._Root, forceshot=False, overwrite=False, save=True, dtime=None, dtFormat=tfd.dtmFormat,
               CalcEtend=True, CalcSpanImp=True, CalcCone=True, CalcPreComp=True, Calc=True, Verb=True,
               Etend_Method='quad', Etend_RelErr=1.e-3, Etend_dX12=[0.01,0.01], Etend_dX12Mode='rel', Etend_Ratio=0.02, Colis=True, LOSRef='Cart',
               Cone_DRY=0.0025, Cone_DXTheta=np.pi/1024., Cone_DZ=0.0025, Cone_NPsi=20, Cone_Nk=60):
        """ Create, save and return all the GDetect objects relevant for the input shot, unless already created for a relevant reference shot

        Create the :class:`tofu.geom.GDetect` objects (i.e.: the cameras, which are groups of detectors) from geometry taken from CSX disgnostic for the proposed shot or earlier (looks for the oldest version of the matching geometry) and stores them in the SavePathObj.

        All extra arguments are fed to :class:`~tofu.geom.Detect`

        Parameters
        ----------
        shot : int
            Shot number for which to build the geometry
        VesName : str
            Name of the tfg.Ves object to be fed as an input to the :class:`tofu.geom.GDetect` objects
        SavePathObj : None / str
            Absolute path where the created :class:`tofu.geom.GDetect` objects should be saved (if save=True), if None the default is used
        Root : str
            If SavePathObj=None, a default value is created by appending '/tofu/plugins/AUG/SXR/geom/Objects/' to Root
        forceshot : bool
            Flag indicating whether the shot number shall be downgraded to the oldest shot with the same geometry (False) or whether the provided shot number shall be enforced (True, for all camera heads)
        overwrite : bool
            Flag indicating whether new :class:`tofu.geom.GDetect` objects shall be computed (and possibly saved) when similar ones already exist (True)
        save : bool
            Flag indicating whether to save the created :class:`tofu.geom.GDetect` objects (in SavePathObj)
        dtime : None / dtm.datetime
            If provided (i.e.: not None), used as a label of the created :class:`tofu.geom.GDetect` objects (mostly used for debugging)
        dtFormat : str
            The time format to be used for labelling the created :class:`tofu.geom.GDetect` objects (mostly used for debugging)

        Returns
        -------
        LGD : list
            A list of all the created tfg.GDetect objects

        """

        # Pre-formatting inputs
        if SavePathObj is None:
            SavePathObj = Root + _addpath + '/Objects/'

        # LMNoTile = _get_LMTilesFromShot(shot)  # Deprecated, replace by proper scanning / refit of several successive shots
        if not forceshot:
            #shot, LCamH = _get_GeomFromShot(shot)
            shot = _get_GeomFromShot_New(shot)
        #else:
        #    LCamH = "All"
        #if not LCamH=="All":
        #    assert LCamH in CamHeads or all([cc in CamHeads for cc in LCamH])
        #else:
        #    LCamH = CamHeads
        shot = _get_RectifiedFromShot(shot)

        Dict = tfaugSXRh._WhichSX(shot=shot)
        Ves = tfaugVes.load(VesName)

        Tiles = _get_Tiles_FromShot(shot)

        # Computing and saving
        UnCamH = sorted(list(set([Dict[dd]['CamHead'] for dd in Dict.keys()])))
        ListGDPre = _listavailCams(Exp='AUG', Diag='SXR', SavePathObj=SavePathObj, Root=Root, shotstr='_sh')
        LGD = []
        for ii in range(0,len(UnCamH)):
            cond0 = overwrite
            cond1 = not UnCamH[ii] in ListGDPre.keys() or not shot in ListGDPre[UnCamH[ii]]
            if any([cond0, cond1]):
                lds = sorted([dd for dd in Dict.keys() if Dict[dd]['CamHead']==UnCamH[ii]])
                LApert = _getLApert(Dict, lds[0], Ves, shot, SavePathObj, dtime, dtFormat, Tiles=Tiles, Exp='AUG')
                LD = []
                for dd in lds:
                    Id = tfpf.ID('Detect', dd, shot=shot, Diag='SXR', SaveName=None, SavePath=SavePathObj, dtime=dtime, Exp='AUG', LObj=None, dtFormat=dtFormat,
                                 USRdict={'Cam':Dict[dd]['Cam'], 'CamHead':Dict[dd]['CamHead'], 'Diag':Dict[dd]['Diag'], 'Sig':Dict[dd]['Sig'], 'Num':Dict[dd]['Num'], 'FiltMat':Dict[dd]['FiltMat'], 'FiltThick':Dict[dd]['FiltThick'],
                                          'Sampl':Dict[dd]['Sampling'], 'Address':Dict[dd]['Address']})

                    A = np.array([Dict[dd]['RPINHOLE'], Dict[dd]['ZPINHOLE']])
                    B = np.array([Dict[dd]['REND'], Dict[dd]['ZEND']])
                    phi = Dict[dd]['Tor_Pos']*np.pi/180.
                    theta = Dict[dd]['CAMANGLE']*np.pi/180.

                    RIn = np.hypot(LApert[0].BaryS[0]+LApert[0].nIn[0],LApert[0].BaryS[1]+LApert[0].nIn[1]) - np.hypot(LApert[0].BaryS[0],LApert[0].BaryS[1])
                    Sgn = np.sign(RIn*np.cos(theta) + LApert[0].nIn[2]*np.sin(theta))
                    P = A - Sgn*1.e-3*Dict[dd]['Foc_Len']*np.array([np.cos(theta), np.sin(theta)])
                    DTor, DPol, DGap = float(1.e-3*Dict[dd]['D_Length']), float(1.e-3*Dict[dd]['D_Width']), float(1.e-3*Dict[dd]['D_Gap'])
                    Poly, M = tfh.PolyFromLine(A, B, P, theta, phi, DTor, DPol)
                    DD = np.hypot(np.hypot(M[0],M[1])-P[0], M[2]-P[1])
                    nn = DD/(DGap+DPol)
                    assert np.abs(nn-np.round(nn))<0.017, "Inconsistent location of center for "+dd+", abs(nn-round(nn)) = "+str(np.abs(nn-np.round(nn)))+" with nn = "+str(nn)

                    Det = tfg.Detect(Id, Poly, Optics=LApert, Ves=Ves, Sino_RefPt=None,
                                     CalcEtend=CalcEtend, CalcSpanImp=CalcSpanImp, CalcCone=CalcCone, CalcPreComp=CalcPreComp, Calc=Calc, Verb=Verb,
                                     Etend_Method=Etend_Method, Etend_RelErr=Etend_RelErr, Etend_dX12=Etend_dX12, Etend_dX12Mode=Etend_dX12Mode, Etend_Ratio=Etend_Ratio, Colis=Colis, LOSRef=LOSRef,
                                     Cone_DRY=Cone_DRY, Cone_DXTheta=Cone_DXTheta, Cone_DZ=Cone_DZ, Cone_NPsi=Cone_NPsi, Cone_Nk=Cone_Nk,
                                     arrayorder='C', Clock=False, Type=None, Exp='AUG', Diag='SXR', shot=shot, dtime=None, dtimeIn=False, SavePath=SavePathObj)

                    LD.append(Det)

                GD = tfg.GDetect(UnCamH[ii], LDetect=LD, Type=None, Exp='AUG', Diag='SXR', shot=shot, Sino_RefPt=None, LOSRef='Cart', arrayorder='C', Clock=False, dtime=None, dtimeIn=False, SavePath=SavePathObj)

                if save:
                    GD.save(SynthDiag=True)
                LGD.append(GD)
        return LGD

    #########################################################
    #########################################################
    ############ Loading tools ##############################
    #########################################################

    def load(Cams=None, shot=None, SavePathObj=None, Root=_tfaug_path._Root, sort=False, out='full', Test=True):
        """ Load and return the desired :class:`~tofu.geom.GDetect` objects (i.e.: camera heads)

        Directly fecthes and loads the desired :class:`~tofu.geom.GDetect` objects.

        Parameters
        ----------
        Cams :          str / list
            A name or a list of names of the camera heads to be loaded (available are ['F','G','H1','H2','H3','I1','I2','I3','J1','J2','J3','K1','K2','L','M'])
        shot : int / float / np.float
            A shot number indicating which version of the geometry should be loaded (the )
        SavePathObj :   None / str
            Absolute path where the created :class:`tofu.geom.GDetect` objects should be saved (if save=True), if None the default is used
        Root : str
            If SavePathObj=None, a default value is created by appending '/tofu/plugins/AUG/SXR/geom/Objects/' to Root
        sort :          bool
            Flag indicating whether the loaded :class:`tofu.geom.GDetect` objects shall be returned sorted by alphabetical order of the names (True) or in the same order as asked in Cams (False)
        out :           str
            Flag indicating whether the object should be loaded completely ('full'), in a light dismissing the heaviest attributes ('light') or whether only the Id or a list of Id should be returned ('Id'), valid only for '.npz'
        Test :          bool
            Flag indicating whether the inputs should be tested for conformity

        Returns:
        --------
        LGD :           list / :class:`tofu.geom.GDetect`
            The loaded :class:`tofu.geom.GDetect`, returned as a single object if Cams was provided as a single name, as a list otherwise

        """

        if Test:
            assert Cams is None or type(Cams) is str or (type(Cams) is list and all([type(cc) is str for cc in Cams])), "Arg Cams must be a str or a list of such !"
            assert shot is None or type(shot) in [int,float,np.float], "Arg shot must be a int !"

        # Pre-formatting inputs
        Flagstr = False
        if type(Cams) is str:
            Cams = [Cams]
            Flagstr = True
        if SavePathObj is None:
            SavePathObj = Root + _addpath + '/Objects/'

        # List available cameras heads, sort and select the relevant files
        DC = _listavailCams(Exp='AUG', Diag='SXR', SavePathObj=SavePathObj, Root=Root, shotstr='_sh')
        Cams = sorted(DC.keys()) if Cams is None else Cams
        assert all([cc in DC.keys() for cc in Cams]), "Some Cams asked for are not available in "+SavePathObj
        Cams = sorted(Cams) if sort else Cams

        # Loading
        LGD = []
        for ii in range(0,len(Cams)):
            sh = DC[Cams[ii]] if shot is None else [sh for sh in DC[Cams[ii]] if sh<shot]
            if len(sh)==0:
                warnings.warn("    Camera "+Cams[ii]+" could not be loaded because it was only computed for later shots !")
            else:
                sh = max(sh)
                ff = 'TFG_GDetect_AUG_DgSXR_'+Cams[ii]+'_sh'+str(sh)+'.npz'
                gd = tfpf.Open(SavePathObj + ff, out=out)
                LGD.append( gd )
        if len(LGD)>0:
            LGD = LGD[0] if Flagstr else LGD
        return LGD