# -*- 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
[docs]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 ##############################
#########################################################
[docs]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