From e92175fe34f91cf30e1b0b4e83cc777f9b113eda Mon Sep 17 00:00:00 2001 From: Holger Niemann <holger.niemann@ipp.mpg.de> Date: Mon, 18 Nov 2019 10:28:29 +0100 Subject: [PATCH] Update to V3.3.3: added a function for deriving peaking factor and a strike-line width, based on the wetted area calculations. Smaller fixes for better reading --- IR_image_tools.py | 923 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 759 insertions(+), 164 deletions(-) diff --git a/IR_image_tools.py b/IR_image_tools.py index 5123913..5a5b6c7 100644 --- a/IR_image_tools.py +++ b/IR_image_tools.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- """ Created on Wed May 9 14:56:32 2018 -Version: 3.3.2 +Version: 3.3.3 @author: Holger Niemann, Peter Drewelow, Yu Gao mainly to clean up the downloadversionIRdata code Tools for: - checking IR images, - calculate gain and offset again from + checking IR images, + calculate gain and offset again from check backgroundframes check coldframes ... @@ -15,7 +15,7 @@ Tools for: import numpy as np import matplotlib.pyplot as plt import matplotlib.patches as patches -from IR_config_constants import portcamdict,IRCAMBadPixels_path,parameter_file_path +from IR_config_constants import portcamdict, IRCAMBadPixels_path, parameter_file_path import os import datetime #import h5py @@ -25,11 +25,11 @@ def get_OP_by_time(time_ns=None, shot_no=None, program_str=None): '''Derives operation phase (OP) of W7-X based on either: a nanosacond time stamp, a MDSplus style shot no. or a program ID. IN: - time_ns - integer of nanosecond time stamp, + time_ns - integer of nanosecond time stamp, e.g. 1511972727249834301 (OPTIONAL) - shot_no - integer of MDSplus style shot number, + shot_no - integer of MDSplus style shot number, e.g. 171207022 (OPTIONAL) - program_str - string of CoDaQ ArchiveDB style prgram number or date, + program_str - string of CoDaQ ArchiveDB style prgram number or date, e.g. '20171207.022' or '20171207' (OPTIONAL) RETURN: conn - MDSplus connection object, to be used in e.g. 1511972727249834301 @@ -44,143 +44,145 @@ def get_OP_by_time(time_ns=None, shot_no=None, program_str=None): dateOP = datetime.datetime.strptime(program_str[:8], '%Y%m%d') else: raise Exception('get_OP_by_time: ERROR! neither time, shot no. or program ID provided') - + if dateOP.year == 2017: - if dateOP.month>8 and dateOP.month<12: - return "OP1.2a" - elif dateOP.month==8 and dateOP.day>=28: - return "OP1.2a" - elif dateOP.month==12 and dateOP.day<8: - return "OP1.2a" + if dateOP.month > 8 and dateOP.month < 12: + OP = "OP1.2a" + elif dateOP.month == 8 and dateOP.day >= 28: + OP = "OP1.2a" + elif dateOP.month == 12 and dateOP.day < 8: + OP = "OP1.2a" else: - return None + OP = None elif dateOP.year >= 2018: return "OP1.2b" elif dateOP.year <= 2016 and dateOP.year >= 2015: if (dateOP.year == 2016 and dateOP.month <= 3) or (dateOP.year == 2015 and dateOP.month == 12): - return "OP1.1" + OP = "OP1.1" else: - return None - -def bestimmtheitsmass_general(data,fit): - R=0 - if len(fit)==len(data): - mittel=np.sum(data)/len(data) - qam=quad_abweich_mittel(fit,mittel) - R=qam/(qam+quad_abweich(data,fit)) + OP = None + return OP + +def bestimmtheitsmass_general(data, fit): + R = 0 + if len(fit) == len(data): + mittel = np.sum(data)/len(data) + qam = quad_abweich_mittel(fit, mittel) + R = qam/(qam+quad_abweich(data, fit)) else: print("bestimmtheitsmass_general: Arrays must have same dimensions") return R -def bestimmheitsmass_linear(data,fit,debugmode=False): - R2=0 - if len(fit)==len(data): - mittel_D=np.mean(data)#np.sum(data)/len(data) - mittel_F=np.mean(fit) - R2=quad_abweich_mittel(fit,mittel_D)/quad_abweich_mittel(data,mittel_D) +def bestimmheitsmass_linear(data, fit, debugmode=False): + R2 = 0 + if len(fit) == len(data): + mittel_D = np.mean(data)#np.sum(data)/len(data) + mittel_F = np.mean(fit) + R2 = quad_abweich_mittel(fit, mittel_D)/quad_abweich_mittel(data, mittel_D) if debugmode: - print(mittel_D,mittel_F,quad_abweich_mittel(fit,mittel_D),quad_abweich_mittel(data,mittel_D),R2) + print(mittel_D, mittel_F, quad_abweich_mittel(fit, mittel_D), + quad_abweich_mittel(data, mittel_D), R2) else: print("bestimmtheitsmass_linear: Arrays must have same dimensions") return R2 - -def quad_abweich_mittel(data,mittel): - R=0 + +def quad_abweich_mittel(data, mittel): + R = 0 for i in data: - R=R+(i-mittel)**2 + R = R+(i-mittel)**2 return R - -def quad_abweich(data,fit): - R=0 - if len(fit)==len(data): + +def quad_abweich(data, fit): + R = 0 + if len(fit) == len(data): for i in range(len(data)): - R=R+(data[i]-fit[i])**2 + R = R+(data[i]-fit[i])**2 else: print("quad_abweich: Arrays must have same dimensions") - return R - -def find_nearest(array,value): + return R + +def find_nearest(array, value): #a=array a = [x - value for x in array] mini = np.min(np.abs(a)) - try: idx= a.index(mini) - except: idx= a.index(-mini) + try: idx = a.index(mini) + except: idx = a.index(-mini) return idx#array[idx] - -def check_coldframe(coldframe,references=None,threshold=0.5,plot_it=False): + +def check_coldframe(coldframe, references=None, threshold=0.5, plot_it=False): ''' return true/false and the quality factor ''' - shapi=np.shape(coldframe) + shapi = np.shape(coldframe) ##function (np.arange(0,768)-384)**(2)/900-50 - datasets=[] - for i in [int(shapi[1]//4),int(shapi[1]//2),int(shapi[1]//4*3)]: - dataline=coldframe[0:shapi[0],i] + datasets = [] + for i in [int(shapi[1]//4), int(shapi[1]//2), int(shapi[1]//4*3)]: + dataline = coldframe[0:shapi[0], i] datasets.append(dataline-np.mean(dataline)) - if references==None: - references=[] - for dat in datasets: - mini=np.mean(dat[shapi[0]/2-50:shapi[0]/2+50]) - a=(np.mean(dat[0:50])-mini)/(int(shapi[0]/2))**2 - reference=a*(np.arange(0,shapi[0])-int(shapi[0]/2))**(2)+mini + if references == None: + references = [] + for dat in datasets: + mini = np.mean(dat[shapi[0]/2-50:shapi[0]/2+50]) + a = (np.mean(dat[0:50])-mini)/(int(shapi[0]/2))**2 + reference = a*(np.arange(0, shapi[0])-int(shapi[0]/2))**(2)+mini references.append(reference) - bestimmtheit=[] + bestimmtheit = [] if plot_it: plt.figure() - plt.imshow(coldframe,vmin=np.mean(coldframe)-500,vmax=np.mean(coldframe)+500) + plt.imshow(coldframe, vmin=np.mean(coldframe)-500, vmax=np.mean(coldframe)+500) plt.figure() for i_dat in range(len(datasets)): - dat=datasets[i_dat] - reference=references[i_dat] - bestimmtheit.append(bestimmtheitsmass_general(dat,reference)) - if plot_it: - plt.plot(dat,label='data') - plt.plot(reference,label='reference') + dat = datasets[i_dat] + reference = references[i_dat] + bestimmtheit.append(bestimmtheitsmass_general(dat, reference)) + if plot_it: + plt.plot(dat, label='data') + plt.plot(reference, label='reference') # print(int(shapi[0]/2),1*(np.max(datasets[-1])-mini),mini) plt.legend() - if np.mean(bestimmtheit)>threshold: - return True,bestimmtheit + if np.mean(bestimmtheit) > threshold: + return True, bestimmtheit else: - return False,bestimmtheit + return False, bestimmtheit -def check_coldframe_by_refframe(coldframe,reference_frame,threshold=0.8,plot_it=False): +def check_coldframe_by_refframe(coldframe, reference_frame, threshold=0.8, plot_it=False): ''' ''' - references=[] - shapi=np.shape(reference_frame) - for i in [int(shapi[1]//5),int(shapi[1]//2),int(shapi[1]//4*3)]: - dataline=reference_frame[0:shapi[0],i] + references = [] + shapi = np.shape(reference_frame) + for i in [int(shapi[1]//5), int(shapi[1]//2), int(shapi[1]//4*3)]: + dataline = reference_frame[0:shapi[0], i] references.append(dataline-np.mean(dataline)) - return check_coldframe(coldframe,references,threshold,plot_it) - -def check_backgroundframe(backgroundframe,threshold=50): + return check_coldframe(coldframe, references, threshold, plot_it) + +def check_backgroundframe(backgroundframe, threshold=50): ''' return true or false ''' - shapi=np.shape(backgroundframe) - valid=True - dataset=[] - for i in [int(shapi[1]//4),int(shapi[1]//2),int(shapi[1]//4*3)]: - referenceline=backgroundframe[0:shapi[0],i] - meanref=referenceline-np.mean(referenceline) + shapi = np.shape(backgroundframe) + valid = True + dataset = [] + for i in [int(shapi[1]//4), int(shapi[1]//2), int(shapi[1]//4*3)]: + referenceline = backgroundframe[0:shapi[0], i] + meanref = referenceline-np.mean(referenceline) dataset.append(np.max(meanref)-np.min(meanref)) - if np.mean(dataset)<threshold: - valid=False - return valid,np.mean(dataset) - -def read_bad_pixels_from_file(port, shot_no=None, program=None,time_ns=None): + if np.mean(dataset) < threshold: + valid = False + return valid, np.mean(dataset) + +def read_bad_pixels_from_file(port, shot_no=None, program=None, time_ns=None): '''Reads bad pixels stored in *.bpx file on E4 server. Requires one of the optional arguments shot_no or program. IN port - integer of port no of camera shot_no - integer of MDSplus style shot number, e.g. 171207022 (OPTIONAL) - program - string of CoDaQ ArchiveDB style prgram number or date, + program - string of CoDaQ ArchiveDB style prgram number or date, e.g. '20171207.022' or '20171207' (OPTIONAL) OUT - bad_pixle_list - list of tuples (row,column) of pixel coordinates + bad_pixle_list - list of tuples (row,column) of pixel coordinates as integer ''' - if shot_no is not None: + if shot_no is not None: OP = get_OP_by_time(shot_no=shot_no) elif program is not None: OP = get_OP_by_time(program_str=program) @@ -188,20 +190,19 @@ def read_bad_pixels_from_file(port, shot_no=None, program=None,time_ns=None): OP = get_OP_by_time(time_ns=time_ns) else: raise Exception('read_bad_pixels_from_file: ERROR! Need either shot no. or program string.') - + port_name = 'AEF{0}'.format(port) bad_pixel_file = 'badpixel_{0}.bpx'.format(portcamdict[OP][port_name][6:]) try: data = np.genfromtxt(IRCAMBadPixels_path+bad_pixel_file, dtype=int) - bad_pixle_list = list(zip(data[:,1], data[:,0])) + bad_pixle_list = list(zip(data[:, 1], data[:, 0])) except: - bad_pixle_list=[] + bad_pixle_list = [] return bad_pixle_list - -def find_outlier_pixels(frame,tolerance=3,worry_about_edges=True,plot_it=False): +def find_outlier_pixels(frame, tolerance=3, plot_it=False):#worry_about_edges=True, ''' - This function finds the bad pixels in a 2D dataset. + This function finds the bad pixels in a 2D dataset. Tolerance is the number of standard deviations used for cutoff. ''' frame = np.array(frame)#, dtype=int) @@ -211,60 +212,59 @@ def find_outlier_pixels(frame,tolerance=3,worry_about_edges=True,plot_it=False): threshold = tolerance*np.std(difference) mean = np.mean(difference) if plot_it: - + fig = plt.figure() fig.suptitle('find_outlier_pixels: histogram') - plt.hist(difference.ravel(),50,log=True,histtype='stepfilled') - plt.axvline(mean, linewidth=2, color='k',label='mean') + plt.hist(difference.ravel(), 50, log=True, histtype='stepfilled') + plt.axvline(mean, linewidth=2, color='k', label='mean') x1 = mean - np.std(difference) x2 = mean + np.std(difference) - plt.axvspan(x1,x2, linewidth=2, facecolor='g',alpha=0.1,label='standard deviation') + plt.axvspan(x1, x2, linewidth=2, facecolor='g', alpha=0.1, label='standard deviation') x1 = mean - tolerance*np.std(difference) x2 = mean + tolerance*np.std(difference) - plt.axvspan(x1,x2, linewidth=2, facecolor='r',alpha=0.1,label='threshold for bad pixel') + plt.axvspan(x1, x2, linewidth=2, facecolor='r', alpha=0.1, label='threshold for bad pixel') plt.legend() plt.show() - + #find the hot pixels - bad_pixels = np.transpose(np.nonzero((np.abs(difference)>threshold)) ) + bad_pixels = np.transpose(np.nonzero((np.abs(difference) > threshold))) bad_pixels = (bad_pixels).tolist() bad_pixels = [tuple(l) for l in bad_pixels] - + if plot_it: plt.figure() plt.imshow(frame) for i in range(len(bad_pixels)): - plt.scatter(bad_pixels[i][1],bad_pixels[i][0],c='None') + plt.scatter(bad_pixels[i][1], bad_pixels[i][0], c='None') plt.show() - + return bad_pixels -def correct_images(images,badpixels,verbose=0): +def correct_images(images, badpixels, verbose=0): ''' ''' - if type(badpixels)!=int: + if type(badpixels) != int: if type(images) == list: # return corrected images also as list of 2D arrays for i in range(len(images)): - images[i]=restore_bad_pixels(images[i], np.invert(badpixels==1), verbose=verbose-1) + images[i] = restore_bad_pixels(images[i], np.invert(badpixels == 1), verbose=verbose-1) else: # keep shape - images = restore_bad_pixels(images, np.invert(badpixels==1), verbose=verbose-1) - + images = restore_bad_pixels(images, np.invert(badpixels == 1), verbose=verbose-1) # for i in range(len(images)): # images[i]=(restore_pixels(images[i],np.invert(badpixels==1))).astype(np.float32) - if verbose>0: + if verbose > 0: print("correct_images: done") return images -def restore_bad_pixels(frames, bad_pixel, by_list=True, check_neighbours=True, plot_it=False,verbose=0): - """Restore bad pixel by interpolation of adjacent pixels. Optionally make - sure that adjacent pixels are not bad (time consuming). Default is to use - a list of bad pixels and a for loop. For many bad pixels consider using +def restore_bad_pixels(frames, bad_pixel, by_list=True, check_neighbours=True, plot_it=False, verbose=0): + """Restore bad pixel by interpolation of adjacent pixels. Optionally make + sure that adjacent pixels are not bad (time consuming). Default is to use + a list of bad pixels and a for loop. For many bad pixels consider using the optinal alternative using a bad pixel mask. IN: - frames - either list of frames as 2D numpy array, + frames - either list of frames as 2D numpy array, or 3D numpy array (frame number, n_rows, n_cols), or 2D numpy array (n_rows, n_cols) bad_pixel - either list of tuples of bad pixel coordinates, @@ -282,7 +282,7 @@ def restore_bad_pixels(frames, bad_pixel, by_list=True, check_neighbours=True, p verbose - integer of feedback level (amount of prints) (OPTIONAL: if not provided, only ERROR output) RETURN: - frames - 3D numpy array (frame number, n_rows, n_cols) of + frames - 3D numpy array (frame number, n_rows, n_cols) of corrected frames """ @@ -308,7 +308,7 @@ def restore_bad_pixels(frames, bad_pixel, by_list=True, check_neighbours=True, p # make sure bad pixel are provided as mask and list if type(bad_pixel) is list: blist = bad_pixel - bmask = np.ones([n_rows, n_cols],dtype=bool) + bmask = np.ones([n_rows, n_cols], dtype=bool) for pix in blist: try: bmask[pix] = False @@ -318,8 +318,8 @@ def restore_bad_pixels(frames, bad_pixel, by_list=True, check_neighbours=True, p else: if np.shape(bad_pixel)[0] == n_rows and np.shape(bad_pixel)[1] == n_cols: bmask = np.invert(bad_pixel) - x,y = np.where(bmask) - blist = list(zip(x,y)) + x, y = np.where(bmask) + blist = list(zip(x, y)) else: raise Exception('restore_bad_pixels: ERROR! bad_pixel in bad shape {0}'.format(np.shape(bad_pixel))) @@ -327,21 +327,21 @@ def restore_bad_pixels(frames, bad_pixel, by_list=True, check_neighbours=True, p print('restore_bad_pixels: {0} bad pixels to be restored: {1} ... '.format(len(blist), blist[:3])) # expand frame by rows and columns of zeros to simplify treatment of edges - frames = np.dstack([np.zeros([n_frames,n_rows], dtype=frame_dtype), frames, np.zeros([n_frames,n_rows], dtype=frame_dtype)]) - frames = np.hstack([np.zeros([n_frames,1,n_cols+2], dtype=frame_dtype), frames, np.zeros([n_frames,1,n_cols+2], dtype=frame_dtype)]) - bmask = np.vstack([np.zeros([1,n_cols], dtype=bool), bmask, np.zeros([1,n_cols], dtype=bool)]) - bmask = np.hstack([np.zeros([n_rows+2,1], dtype=bool), bmask, np.zeros([n_rows+2,1], dtype=bool)]) + frames = np.dstack([np.zeros([n_frames, n_rows], dtype=frame_dtype), frames, np.zeros([n_frames, n_rows], dtype=frame_dtype)]) + frames = np.hstack([np.zeros([n_frames, 1, n_cols+2], dtype=frame_dtype), frames, np.zeros([n_frames, 1, n_cols+2], dtype=frame_dtype)]) + bmask = np.vstack([np.zeros([1, n_cols], dtype=bool), bmask, np.zeros([1, n_cols], dtype=bool)]) + bmask = np.hstack([np.zeros([n_rows+2, 1], dtype=bool), bmask, np.zeros([n_rows+2, 1], dtype=bool)]) # define number of neighbours (up to 4) ina an array of expanded frame size n_neighbours = np.ones([n_frames, n_rows+2, n_cols+2])*4 - n_neighbours[:,1,:] = 3 - n_neighbours[:,-2,:] = 3 - n_neighbours[:,:,1] = 3 - n_neighbours[:,:,-2] = 3 - n_neighbours[:,1,1] = 2 - n_neighbours[:,1,-2] = 2 - n_neighbours[:,-2,1] = 2 - n_neighbours[:,-2,-2] = 2 + n_neighbours[:, 1, :] = 3 + n_neighbours[:, -2, :] = 3 + n_neighbours[:, :, 1] = 3 + n_neighbours[:, :, -2] = 3 + n_neighbours[:, 1, 1] = 2 + n_neighbours[:, 1, -2] = 2 + n_neighbours[:, -2, 1] = 2 + n_neighbours[:, -2, -2] = 2 if by_list: # ===== correct bad pixels using the list of bad pixels ===== @@ -353,22 +353,22 @@ def restore_bad_pixels(frames, bad_pixel, by_list=True, check_neighbours=True, p if check_neighbours: # takes only neighbours that are not bad - pos_l = np.where(bmask[pos[0]+1,:pos[1]+1]==False)[0] + pos_l = np.where(bmask[pos[0]+1,:pos[1]+1] == False)[0] if len(pos_l) != 0: pos_l = pos_l[-1] else: pos_l = pos[1]+1 - pos_r = np.where(bmask[pos[0]+1,pos[1]+1:]==False)[0] + pos_r = np.where(bmask[pos[0]+1,pos[1]+1:] == False)[0] if len(pos_r) != 0: pos_r = pos_r[0] + pos[1]+1 else: pos_r = pos[1]+2 - pos_t = np.where(bmask[:pos[0]+1,pos[1]+1]==False)[0] + pos_t = np.where(bmask[:pos[0]+1,pos[1]+1] == False)[0] if len(pos_t) != 0: pos_t = pos_t[-1] else: pos_t = pos[0]+1 - pos_b = np.where(bmask[pos[0]+1:,pos[1]+1]==False)[0] + pos_b = np.where(bmask[pos[0]+1:,pos[1]+1] == False)[0] if len(pos_b) != 0: pos_b = pos_b[0] + pos[0]+1 else: @@ -392,10 +392,10 @@ def restore_bad_pixels(frames, bad_pixel, by_list=True, check_neighbours=True, p # (insensitive to neighbours being bad as well!) # prepare mask arrays for neighbours by shifting it to left, right, top and bottom - bmask_l = np.hstack([bmask[:,1:], np.zeros([n_rows+2,1], dtype=bool)]) - bmask_r = np.hstack([np.zeros([n_rows+2,1], dtype=bool), bmask[:,:-1]]) - bmask_t = np.vstack([bmask[1:,:], np.zeros([1,n_cols+2], dtype=bool)]) - bmask_b = np.vstack([np.zeros([1,n_cols+2], dtype=bool), bmask[:-1,:]]) + bmask_l = np.hstack([bmask[:, 1:], np.zeros([n_rows+2, 1], dtype=bool)]) + bmask_r = np.hstack([np.zeros([n_rows+2, 1], dtype=bool), bmask[:, :-1]]) + bmask_t = np.vstack([bmask[1:, :], np.zeros([1, n_cols+2], dtype=bool)]) + bmask_b = np.vstack([np.zeros([1, n_cols+2], dtype=bool), bmask[:-1, :]]) # ----------------------------------- @@ -461,20 +461,20 @@ def calculate_gain_offset_image(cold_image,hot_image=None,reference_cold=None,re # Sc_ref = cold_image[ ( np.int( (np.shape(cold_image)[0]) /2 ) ) ][( np.int( (np.shape(cold_image)[1]) /2 ) ) ] # print(hot_image[( np.int( np.shape(hot_image)[0]/2) )-2: (np.int( np.shape(hot_image)[0]/2))+3,np.int((np.shape(hot_image)[1]/2))-2:np.int((np.shape(hot_image)[1]/2))+3 ]) # print(cold_image[( np.int( np.shape(hot_image)[0]/2) )-2: (np.int( np.shape(hot_image)[0]/2))+3,np.int((np.shape(hot_image)[1]/2))-2:np.int((np.shape(hot_image)[1]/2))+3 ]) - Sh_ref = np.mean( hot_image[( np.int( np.shape(hot_image)[0]/2) )-2: (np.int( np.shape(hot_image)[0]/2))+3,np.int((np.shape(hot_image)[1]/2))-2:np.int((np.shape(hot_image)[1]/2))+3 ]) - Sc_ref = np.mean(cold_image[( np.int( np.shape(cold_image)[0]/2) )-2: (np.int( np.shape(cold_image)[0]/2))+3,np.int((np.shape(cold_image)[1]/2))-2:np.int((np.shape(cold_image)[1]/2))+3 ]) - difference_image=hot_image - cold_image - indexlist=np.where(difference_image==0) - difference_image[indexlist]=0.001 - Gain_rel = ( Sh_ref - Sc_ref ) / ( difference_image) + Sh_ref = np.mean(hot_image[( np.int(np.shape(hot_image)[0]/2))-2: (np.int(np.shape(hot_image)[0]/2))+3,np.int((np.shape(hot_image)[1]/2))-2:np.int((np.shape(hot_image)[1]/2))+3]) + Sc_ref = np.mean(cold_image[( np.int(np.shape(cold_image)[0]/2))-2: (np.int(np.shape(cold_image)[0]/2))+3,np.int((np.shape(cold_image)[1]/2))-2:np.int((np.shape(cold_image)[1]/2))+3]) + difference_image = hot_image - cold_image + indexlist = np.where(difference_image==0) + difference_image[indexlist] = 0.001 + Gain_rel = ( Sh_ref - Sc_ref ) / ( difference_image) Gain_rel[indexlist]=0 - Off_h_rel = Sh_ref - hot_image*Gain_rel - Off_c_rel = Sc_ref - cold_image*Gain_rel - Offset_rel = ( Off_h_rel + Off_c_rel ) /2 - return Gain_rel,Offset_rel + Off_h_rel = Sh_ref - hot_image*Gain_rel + Off_c_rel = Sc_ref - cold_image*Gain_rel + Offset_rel = ( Off_h_rel + Off_c_rel ) /2 + return Gain_rel, Offset_rel #%% functions from Yu Gao -""" functions by Yu Gao""" +#""" functions by Yu Gao""" #outdated by download_hot_cold_reference in downloadversionIRdata. removed to remove dependency on data on the E4-drive #============================================================================== # def load_ref_images(port, exposuretime, verbose=0): @@ -526,22 +526,22 @@ def get_work_list(pipepath,typ="q"): """ today=datetime.datetime.now() cam_programs=[] - if typ=='q' or typ=='load': + if typ in ('q','load'): f=open(pipepath+str(today.year)+str(today.month)+"_"+typ+"_requests.txt") else: reasons=[] - f=open(pipepath+"problematic_programs.txt") + f = open(pipepath+"problematic_programs.txt") for line in f: koline=line.split("\t") if len(koline)>1: - prog=koline[0] - if typ=='q' or typ=='load': + prog = koline[0] + if typ in ('q','load'): cam_programs.append((prog,koline[1].split("\n")[0])) else: cam_programs.append((prog,koline[1])) reasons.append(koline[2].split("\n")[0]) f.close() - if typ=='q' or typ=='load': + if typ in ('q','load'): bla=check_dublicates_2(cam_programs) cam_programs=bla[0] return cam_programs @@ -588,7 +588,7 @@ def read_finger_info(file_name=None, OP='OP1.2b', verbose=0): file_name='finger_info_HHF.csv' full_path = os.path.join(parameter_file_path, file_name) print(full_path) - if verbose>0: + if verbose > 0: print('read_finger_info: reading from file {0} in {1}'.format(file_name, parameter_file_path)) if not os.path.isfile(full_path): raise Exception('read_finger_info: ERROR! file not found') @@ -618,6 +618,524 @@ def read_finger_info(file_name=None, OP='OP1.2b', verbose=0): return finger_dic +def derive_strike_line_width_per_module(heat_flux, mapping,mode='average', q_max=None, + profile_average_range=3, noise_threshold=2E5, + ports_loaded=None, verbose=0): + ''' Derive strike-line width of heat flux array by integrating the total power load + and dividing with a peak heat flux value. + The peak heat flux value is either: + 1) the maximum within each divertor finger (mode='finger') + 2) the maximum of a whole divertor module (mode='module') + 3) the maximum of the mean divertor averaged toroidally (mode='average'). + Mode average is default. In case the input heat flux has only 2 dimensions + (from one divertor), mode 'average' and 'module' result in the same. + Returned are the strike-line width(s) and the corresponding q_max value(s). + + INPUT + ----- + heat_flux: numpy array + array of heat fluxes from THEODOR on the profiles defined + in the IR mapping; can be 2D (one divertor), or 3D (multiple divertor modules) + mapping: dictionary + IR profile mapping information as returned by + downloadversionIRdara.download_heatflux_mapping_reference(); + minimum necessary keys are 'finger_ID' and 's' + mode: str, optional + label to identify the normalization mode, either + 'module' (normalize by peak heat flux per torus module), + 'average' (normalize by peak heat flux in the toroidally mean heat flux pattern), + 'finger' (normalize by peak heat flux per finger in each torus module) + (OPTIONAL: default is 'average') + q_max: float or numpy array, optional + either single peak heat flux value or a peak heat flux for each + divertor module or each finger (depends on mode) + (OPTIONAL: default is None, i.e. derived based on mode) + profile_average_range: int, optional + number of central profiles on each finger to average the + integral heat flux on (this avoids hot leading edges and shadowed edges) + (OPTIONAL: default is 3 profiles) + noise_threshold: float, optional + minimum heat flux level to crop heat_flux to, if heat flux has negative values + (OPTIONAL: default is 200kW/m²) + ports_loaded: list or str or int, optional if mode not 'average' + label of divertor modules provided in heat_flux array for plots; + int of port number for single divertor data and list of + port numbers for heat flux from multiple divertor modules; # + gets renamed if a mean heat flux pattern is used (mode 'average') + (OPTIONAL: default is None, i.e. label will be 'w_s') + verbose: integer, optional + feedback level (details of print messages) + (OPTIONAL: if not provided, only ERROR output) + RESULT + ------ + total_mean_width: float or numpy array + mean strike-line width in a shape that depends on the mode (see NOTES) + q_max: float or numpy array + peak heat flux used for normalizatin in a shape that depends on the mode (see NOTES) + NOTES + ----- + The shape of the results varies depending on the dimension of the input + * 2D: singel divertor modules heat flux + * 3D: heat flux from multiple divertor modules + and the mode to derive q_max ('module', 'average', 'finger'): + * '2D' + 'module' or 'average' --> one value for total_wetted_area and q_max + * '3D' + 'average' --> 1D numpy arrays with two values for total_wetted_area and q_max (upper and lower divertors) + * '3D' + 'module' --> 1D numpy arays with a value for each torus module (first dimension of heat_flux) + * '2D' + 'finger' --> 1D numpy arays with a value for each divertor finger + * '3D' + 'finger' --> 2D numpy arays with a value for each torus module and each divertor finger + ''' + #check input + heat_flux_dim = len(np.shape(heat_flux)) + if mode == 'average' and ports_loaded == None and heat_flux_dim>2: + raise Exception("derive_strike_line_width_per_module: ports must be specified in average mode since V3.3.2") + elif mode == 'average' and heat_flux_dim > 2: + try: + llen=len(ports_loaded) + except: + raise Exception("derive_strike_line_width_per_module: each divertor need a description to calcualte proper the wetted area!") + else: + if llen!=len(heat_flux): + raise Exception("derive_strike_line_width_per_module: number of given divertors and number of descriptions does not match!") + # prepare mapping and finger information + finger_dic = read_finger_info(verbose=verbose-1) + finger_ID = finger_dic['ID'] + profile_no = mapping['Finger_ID'][0] + + # find profile IDs of central profiles on each finger + central_profiles_on_finger = [] + is_central_profile = [] + for i_finger in range(len(finger_ID)): + n_profiles = finger_dic['n_profiles'][i_finger] + i_profile_start = n_profiles//2 - profile_average_range//2 -1 + central_profiles = i_finger*100 + np.arange(i_profile_start, i_profile_start+profile_average_range) + central_profiles_on_finger.append(central_profiles) + is_central_profile.append(np.logical_or.reduce([profile_no == centre_profile for centre_profile in central_profiles])) + central_profiles_on_finger = np.array(central_profiles_on_finger) + + if np.nanmin(heat_flux) < 0: + heat_flux[heat_flux<noise_threshold] = 0 + if verbose>0: + print('derive_strike_line_width_per_module: set heat_flux < {0:.1f}kW/m² to 0'.format(noise_threshold/1E3)) + + # reduce dimension of heat_flux if in 'average' mode + if heat_flux_dim == 2 and mode == 'average': + mode = 'module' + elif heat_flux_dim == 3 and mode == 'average': +# heat_flux = np.nanmean(heat_flux, axis=0) + ## sort the divertors + updiv=[] + downdiv=[] + for i in range(len(ports_loaded)): + # entries in the array/list are either int or str or even float + try: + port=int(ports_loaded[i]) + except: #okay it is not an int or an int like string + ## what can it be? 'AEFXX'? But what about OP2 with the A or K ports? Still be 3 letters + port=int(ports_loaded[i][3:]) + if port%10==0: + downdiv.append(heat_flux[i]) + else: + updiv.append(heat_flux[i]) + heat_flux=np.array([np.nanmean(np.asarray(updiv),axis=0),np.nanmean(np.asarray(downdiv),axis=0)]) + del downdiv,updiv +# heat_flux_dim = 3 + ports_loaded = [1,0]#'upper divertor','lower divertor']#'mean heat flux' + mode = 'module' + if verbose>0: + print('derive_strike_line_width_per_module: averaged 3D heat flux array over first dimension') + + if heat_flux_dim==3: + # assume dimensions: toroidal index (camera ports), row, column + n_ports = np.shape(heat_flux)[0]#, n_rows, n_cols + if verbose>0: + print('derive_strike_line_width_per_module: deriving wetted area on {0} divertor modules in {1} mode...'.format(n_ports, mode)) + # derive q_max for normalization of integral + if q_max is None: + # derive q_max on every finger + q_max_on_finger = [] + for i_finger in range(len(finger_ID)): + q_max_on_finger.append([np.nanmax(h[is_central_profile[i_finger]]) for h in heat_flux]) + q_max_on_finger = np.array(q_max_on_finger) + q_max_on_finger[q_max_on_finger==0] = 1 + if mode == 'module': + # one value per torus half module + q_max = np.nanmax(q_max_on_finger, axis=0) + elif mode == 'finger': + # one value per finger in each torus half module + q_max = q_max_on_finger + q_max[q_max==0] = 1 + # integrate over profiles +# goodcounter=np.zeros([n_ports]) + finger_strikeline_width = np.zeros([len(finger_ID), n_ports]) + strikeline_int = np.zeros([len(finger_ID), n_ports]) + for i_finger in range(len(finger_ID)): + # initialize temporary line integral for each module + central_line_integral = np.zeros(n_ports) + # integrate over each central profile and average + for i_profile in central_profiles_on_finger[i_finger]: + ij_profile = np.where(profile_no==i_profile) + s = mapping['s'][ij_profile] + h = heat_flux[:,ij_profile[0],ij_profile[1]] + central_line_integral += np.nan_to_num( np.trapz(h, x=s, axis=1) ) + # complete averaging process + central_line_integral = central_line_integral / profile_average_range + central_line_integral = central_line_integral *((central_line_integral>noise_threshold*max(s))*1) +# goodcounter+=(central_line_integral>noise_threshold*max(s))*1 + # normalize by q_max and multiply with width of finger + strikeline_int[i_finger,:] = central_line_integral + if mode == 'module': + finger_strikeline_width[i_finger,:] = central_line_integral / q_max #* finger_dic['width'][i_finger] + elif mode == 'finger': + finger_strikeline_width[i_finger,:] = central_line_integral / q_max[i_finger] #* finger_dic['width'][i_finger] + + elif heat_flux_dim==2: + # assume dimensions: row, column + if verbose>0: + print('derive_strike_line_width_per_module: deriving wetted area on single divertor module in {0} mode...'.format(mode)) + # derive q_max for normalization of integral + if q_max is None: + if mode == 'average' or mode == 'module': + # one value + q_max = np.nanmax(heat_flux[np.logical_or.reduce(is_central_profile)]) + elif mode == 'finger': + # one value per finger + q_max = [] + for i_finger in range(len(finger_ID)): + q_max.append( np.nanmax(heat_flux[is_central_profile[i_finger]]) ) + q_max = np.array(q_max) + q_max[q_max==0] = 1 + # integrate over profiles +# goodcounter=0 + finger_strikeline_width = np.zeros([len(finger_ID)]) + strikeline_int = np.zeros([len(finger_ID)]) + for i_finger in range(len(finger_ID)): + # integrate over each central profile and average + central_line_integral = 0 + for i_profile in central_profiles_on_finger[i_finger]: + ij_profile = np.where(profile_no==i_profile) + s = mapping['s'][ij_profile] + h = heat_flux[ij_profile[0],ij_profile[1]] + central_line_integral += np.nan_to_num( np.trapz(h, x=s, axis=0) ) + central_line_integral = central_line_integral / profile_average_range + central_line_integral = central_line_integral *((central_line_integral>noise_threshold*max(s))*1) +# goodcounter+=(central_line_integral>noise_threshold*s)*1 + strikeline_int[i_finger] = central_line_integral + if mode == 'average' or mode == 'module': + finger_strikeline_width[i_finger] = central_line_integral / q_max #* finger_dic['width'][i_finger] + elif mode == 'finger': + finger_strikeline_width[i_finger] = central_line_integral / q_max[i_finger] #* finger_dic['width'][i_finger] + + + # merge half-fingers of TM5 and TM6 + if np.any(finger_dic['finger_part']): + if verbose>0: + print('derive_strike_line_width_per_module: merge wetted area on half fingers of TM05 and TM06') + new_finger_ID = np.copy(finger_ID) + # scan backwards over fingers, merge and delete second finger halfs + for i_finger in finger_ID[:0:-1]: + if finger_dic['finger_part'][i_finger]: + new_finger_ID = np.delete(new_finger_ID, i_finger) + if heat_flux_dim==3 and mode != 'average': + finger_strikeline_width[i_finger-1,:] = finger_strikeline_width[i_finger-1,:] + finger_strikeline_width[i_finger,:] + strikeline_int[i_finger-1,:] = strikeline_int[i_finger-1,:] + strikeline_int[i_finger,:] + else: + finger_strikeline_width[i_finger-1] = finger_strikeline_width[i_finger-1] + finger_strikeline_width[i_finger] + strikeline_int[i_finger-1] = strikeline_int[i_finger-1] + strikeline_int[i_finger] + finger_strikeline_width = np.delete(finger_strikeline_width, i_finger, axis=0) + strikeline_int=np.delete(strikeline_int, i_finger, axis=0) + if mode == 'finger' and heat_flux_dim==3: + q_max[i_finger-1,:] = np.maximum(q_max[i_finger-1,:], q_max[i_finger,:]) + q_max = np.delete(q_max, i_finger, axis=0) + elif mode == 'finger' and heat_flux_dim==2: + q_max[i_finger-1] = np.maximum(q_max[i_finger-1], q_max[i_finger]) + q_max = np.delete(q_max, i_finger, axis=0) + + # sum up + # 'average' mode: sum all fingers over all torus modules --> wetted area of all divertors + # divide by n_ports --> get average wetted area per divertor + # 'module' mode: in each torus module sum over wetted area on all fingers + # --> individual wetted areas per divertor + # 'finger' mode: do not sum, since each finger was normalized with a differen q_max + # --> individual wetted areas per finger + # if only one divertors heat flux is given, proceed as in local mode + if mode == 'finger': + total_mean_width = finger_strikeline_width + else: + Weights=np.nan_to_num(strikeline_int/np.sum(strikeline_int,axis=0)) +# print(Weights) + WS=np.sum(Weights,axis=0) + bla=np.where(WS==0)[0] + for b in bla: + Weights[:,b]=Weights[:,b]+1 + total_mean_width = np.average(finger_strikeline_width,axis=0,weights=Weights) + + return total_mean_width, q_max + +def derive_peaking_factor_per_module(heat_flux, mapping,mode='average', q_max=None, + profile_average_range=3, noise_threshold=2E5, + ports_loaded=None, verbose=0): + ''' Derive peaking of heat flux array by dividing the peak heat flux value + with the mean heat flux value the total power load. + The peak heat flux value is either: + 1) the maximum within each divertor finger (mode='finger') + 2) the maximum of a whole divertor module (mode='module') + 3) the maximum of the mean divertor averaged toroidally (mode='average'). + Mode average is default. In case the input heat flux has only 2 dimensions + (from one divertor), mode 'average' and 'module' result in the same. + Returned are the strike-line width(s) and the corresponding q_max value(s). + + INPUT + ----- + heat_flux: numpy array + array of heat fluxes from THEODOR on the profiles defined + in the IR mapping; can be 2D (one divertor), or 3D (multiple divertor modules) + mapping: dictionary + IR profile mapping information as returned by + downloadversionIRdara.download_heatflux_mapping_reference(); + minimum necessary keys are 'finger_ID' and 's' + mode: str, optional + label to identify the normalization mode, either + 'module' (normalize by peak heat flux per torus module), + 'average' (normalize by peak heat flux in the toroidally mean heat flux pattern), + 'finger' (normalize by peak heat flux per finger in each torus module) + (OPTIONAL: default is 'average') + q_max: float or numpy array, optional + either single peak heat flux value or a peak heat flux for each + divertor module or each finger (depends on mode) + (OPTIONAL: default is None, i.e. derived based on mode) + profile_average_range: int, optional + number of central profiles on each finger to average the + integral heat flux on (this avoids hot leading edges and shadowed edges) + (OPTIONAL: default is 3 profiles) + noise_threshold: float, optional + minimum heat flux level to crop heat_flux to, if heat flux has negative values + (OPTIONAL: default is 200kW/m²) + ports_loaded: list or str or int, optional if mode not 'average' + label of divertor modules provided in heat_flux array for plots; + int of port number for single divertor data and list of + port numbers for heat flux from multiple divertor modules; # + gets renamed if a mean heat flux pattern is used (mode 'average') + (OPTIONAL: default is None, i.e. label will be 'w_s') + verbose: integer, optional + feedback level (details of print messages) + (OPTIONAL: if not provided, only ERROR output) + RESULT + ------ + mean_peaking_factor: float or numpy array + mean peaking factor in a shape that depends on the mode (see NOTES) + q_max: float or numpy array + peak heat flux used for normalizatin in a shape that depends on the mode (see NOTES) + NOTES + ----- + The shape of the results varies depending on the dimension of the input + * 2D: singel divertor modules heat flux + * 3D: heat flux from multiple divertor modules + and the mode to derive q_max ('module', 'average', 'finger'): + * '2D' + 'module' or 'average' --> one value for total_wetted_area and q_max + * '3D' + 'average' --> 1D numpy arrays with two values for total_wetted_area and q_max (upper and lower divertors) + * '3D' + 'module' --> 1D numpy arays with a value for each torus module (first dimension of heat_flux) + * '2D' + 'finger' --> 1D numpy arays with a value for each divertor finger + * '3D' + 'finger' --> 2D numpy arays with a value for each torus module and each divertor finger + ''' + #check input + heat_flux_dim = len(np.shape(heat_flux)) + if mode == 'average' and ports_loaded == None and heat_flux_dim>2: + raise Exception("derive_peaking_factor_per_module: ports must be specified in average mode since V3.3.2") + elif mode == 'average' and heat_flux_dim>2: + try: + llen=len(ports_loaded) + except: + raise Exception("derive_peaking_factor_per_module: each divertor need a description to calcualte proper the wetted area!") + else: + if llen!=len(heat_flux): + raise Exception("derive_peaking_factor_per_module: number of given divertors and number of descriptions does not match!") + # prepare mapping and finger information + finger_dic = read_finger_info(verbose=verbose-1) + finger_ID = finger_dic['ID'] + profile_no = mapping['Finger_ID'][0] + + # find profile IDs of central profiles on each finger + central_profiles_on_finger = [] + is_central_profile = [] + for i_finger in range(len(finger_ID)): + n_profiles = finger_dic['n_profiles'][i_finger] + i_profile_start = n_profiles//2 - profile_average_range//2 -1 + central_profiles = i_finger*100 + np.arange(i_profile_start, i_profile_start+profile_average_range) + central_profiles_on_finger.append(central_profiles) + is_central_profile.append( np.logical_or.reduce([profile_no == centre_profile for centre_profile in central_profiles]) ) + central_profiles_on_finger = np.array(central_profiles_on_finger) + + if np.nanmin(heat_flux) < 0: + heat_flux[heat_flux<noise_threshold] = 0 + if verbose>0: + print('derive_peaking_factor_per_module: set heat_flux < {0:.1f}kW/m² to 0'.format(noise_threshold/1E3)) + + # reduce dimension of heat_flux if in 'average' mode + if heat_flux_dim == 2 and mode == 'average': + mode = 'module' + elif heat_flux_dim == 3 and mode == 'average': +# heat_flux = np.nanmean(heat_flux, axis=0) + ## sort the divertors + updiv=[] + downdiv=[] + for i in range(len(ports_loaded)): + # entries in the array/list are either int or str or even float + try: + port=int(ports_loaded[i]) + except: #okay it is not an int or an int like string + ## what can it be? 'AEFXX'? But what about OP2 with the A or K ports? Still be 3 letters + port=int(ports_loaded[i][3:]) + if port%10==0: + downdiv.append(heat_flux[i]) + else: + updiv.append(heat_flux[i]) + heat_flux=np.array([np.nanmean(np.asarray(updiv),axis=0),np.nanmean(np.asarray(downdiv),axis=0)]) + del downdiv,updiv +# heat_flux_dim = 3 + ports_loaded = [1,0]#'upper divertor','lower divertor']#'mean heat flux' + mode = 'module' + if verbose>0: + print('derive_peaking_factor_per_module: averaged 3D heat flux array over first dimension') + + if heat_flux_dim==3: + # assume dimensions: toroidal index (camera ports), row, column + n_ports = np.shape(heat_flux)[0]#, n_rows, n_cols + if verbose>0: + print('derive_peaking_factor_per_module: deriving wetted area on {0} divertor modules in {1} mode...'.format(n_ports, mode)) + # derive q_max for normalization of integral + if q_max is None: + # derive q_max on every finger + q_max_on_finger = [] + for i_finger in range(len(finger_ID)): + q_max_on_finger.append([np.nanmax(h[is_central_profile[i_finger]]) for h in heat_flux]) + q_max_on_finger = np.array(q_max_on_finger) + q_max_on_finger[q_max_on_finger==0] = 1 + if mode == 'module': + # one value per torus half module + q_max = np.nanmax(q_max_on_finger, axis=0) + elif mode == 'finger': + # one value per finger in each torus half module + q_max = q_max_on_finger + q_max[q_max==0] = 1 + # integrate over profiles +# goodcounter=np.zeros([n_ports]) + finger_strikeline_width = np.zeros([len(finger_ID), n_ports]) + strikeline_int = np.zeros([len(finger_ID), n_ports]) + for i_finger in range(len(finger_ID)): + # initialize temporary line integral for each module + central_line_integral = np.zeros(n_ports) + # integrate over each central profile and average + for i_profile in central_profiles_on_finger[i_finger]: + ij_profile = np.where(profile_no==i_profile) +# s = mapping['s'][ij_profile] + h = heat_flux[:,ij_profile[0],ij_profile[1]] + hh=[] + for ele in h: + hh=np.append(hh,np.mean(ele[np.where(ele>0)])) + central_line_integral += np.nan_to_num(hh)#np.mean(h[:][np.where(h[:]>0)]))#np.trapz(h, x=s, axis=1) ) + # complete averaging process + central_line_integral = central_line_integral / profile_average_range +# central_line_integral = central_line_integral *((central_line_integral>noise_threshold*max(s))*1) +# goodcounter+=(central_line_integral>noise_threshold*max(s))*1 + # normalize by q_max and multiply with width of finger + strikeline_int[i_finger,:] = central_line_integral + if mode == 'module': + finger_strikeline_width[i_finger,:] = q_max / central_line_integral #/ q_max #* finger_dic['width'][i_finger] + elif mode == 'finger': + if np.min(q_max[i_finger] / central_line_integral) < 1: + print("here comes something strange", q_max[i_finger], central_line_integral, np.shape(h)) + finger_strikeline_width[i_finger,:] = q_max[i_finger] / central_line_integral #/ q_max[i_finger] #* finger_dic['width'][i_finger] + + elif heat_flux_dim==2: + # assume dimensions: row, column + if verbose>0: + print('derive_peaking_factor_per_module: deriving peaking factor on single divertor module in {0} mode...'.format(mode)) + # derive q_max for normalization of integral + if q_max is None: + if mode == 'average' or mode == 'module': + # one value + q_max = np.nanmax(heat_flux[np.logical_or.reduce(is_central_profile)]) + elif mode == 'finger': + # one value per finger + q_max = [] + for i_finger in range(len(finger_ID)): + q_max.append( np.nanmax(heat_flux[is_central_profile[i_finger]]) ) + q_max = np.array(q_max) + q_max[q_max==0] = 1 + # integrate over profiles +# goodcounter=0 + finger_strikeline_width = np.zeros([len(finger_ID)]) + strikeline_int = np.zeros([len(finger_ID)]) + for i_finger in range(len(finger_ID)): + # integrate over each central profile and average + central_line_integral = 0 + for i_profile in central_profiles_on_finger[i_finger]: + ij_profile = np.where(profile_no==i_profile) +# s = mapping['s'][ij_profile] + h = heat_flux[ij_profile[0],ij_profile[1]] + central_line_integral += np.nan_to_num(np.mean(h[np.where(h>0)]))#np.trapz(h, x=s, axis=0) ) + central_line_integral = central_line_integral / profile_average_range +# central_line_integral = central_line_integral *((central_line_integral>noise_threshold*max(s))*1) +# goodcounter+=(central_line_integral>noise_threshold*s)*1 + strikeline_int[i_finger] = central_line_integral + if mode == 'average' or mode == 'module': + finger_strikeline_width[i_finger] = q_max / central_line_integral #* finger_dic['width'][i_finger] + elif mode == 'finger': + if q_max[i_finger] / central_line_integral < 1: + print("here comes something strange", q_max[i_finger], central_line_integral) + finger_strikeline_width[i_finger] = q_max[i_finger] / central_line_integral #* finger_dic['width'][i_finger] + + + # merge half-fingers of TM5 and TM6 + if np.any(finger_dic['finger_part']): + if verbose > 0: + print('derive_peaking_factor_per_module: merge wetted area on half fingers of TM05 and TM06') + new_finger_ID = np.copy(finger_ID) + # scan backwards over fingers, merge and delete second finger halfs + for i_finger in finger_ID[:0:-1]: + if finger_dic['finger_part'][i_finger]: + new_finger_ID = np.delete(new_finger_ID, i_finger) + if heat_flux_dim==3 and mode != 'average': + finger_strikeline_width[i_finger-1,:] = finger_strikeline_width[i_finger-1,:] + finger_strikeline_width[i_finger,:] + strikeline_int[i_finger-1,:] = strikeline_int[i_finger-1,:] + strikeline_int[i_finger,:] + else: + finger_strikeline_width[i_finger-1] = finger_strikeline_width[i_finger-1] + finger_strikeline_width[i_finger] + strikeline_int[i_finger-1] = strikeline_int[i_finger-1] + strikeline_int[i_finger] + finger_strikeline_width = np.delete(finger_strikeline_width, i_finger, axis=0) + strikeline_int=np.delete(strikeline_int, i_finger, axis=0) + if mode == 'finger' and heat_flux_dim==3: + q_max[i_finger-1,:] = np.maximum(q_max[i_finger-1,:], q_max[i_finger,:]) + q_max = np.delete(q_max, i_finger, axis=0) + elif mode == 'finger' and heat_flux_dim==2: + q_max[i_finger-1] = np.maximum(q_max[i_finger-1], q_max[i_finger]) + q_max = np.delete(q_max, i_finger, axis=0) + + # sum up + # 'average' mode: sum all fingers over all torus modules --> wetted area of all divertors + # divide by n_ports --> get average wetted area per divertor + # 'module' mode: in each torus module sum over wetted area on all fingers + # --> individual wetted areas per divertor + # 'finger' mode: do not sum, since each finger was normalized with a differen q_max + # --> individual wetted areas per finger + # if only one divertors heat flux is given, proceed as in local mode + if mode == 'finger': + peaking_factor = finger_strikeline_width + else: + Weights=np.nan_to_num(strikeline_int/np.sum(strikeline_int,axis=0)) + #deal with unloaded parts + inf_pos=np.where(np.isinf(finger_strikeline_width)) + finger_strikeline_width[inf_pos] = 0 + Weights[inf_pos] = 0 +# print(Weights) +# print(finger_strikeline_width) + WS=np.sum(Weights,axis=0) + bla=np.where(WS==0)[0] + for b in bla: + Weights[:,b]=Weights[:,b]+1 + peaking_factor = np.average(finger_strikeline_width,axis=0,weights=Weights) + + return peaking_factor, q_max + def derive_wetted_area_per_module(heat_flux, mapping, mode='average', q_max=None, profile_average_range=3, noise_threshold=2E5, ports_loaded=None, plot_it=False, verbose=0): @@ -727,7 +1245,7 @@ def derive_wetted_area_per_module(heat_flux, mapping, mode='average', q_max=None elif heat_flux_dim == 3 and mode == 'average': # heat_flux = np.nanmean(heat_flux, axis=0) ## sort the divertors - updiv=([]) + updiv=[] downdiv=[] for i in range(len(ports_loaded)): # entries in the array/list are either int or str or even float @@ -743,14 +1261,91 @@ def derive_wetted_area_per_module(heat_flux, mapping, mode='average', q_max=None heat_flux=np.array([np.nanmean(np.asarray(updiv),axis=0),np.nanmean(np.asarray(downdiv),axis=0)]) del downdiv,updiv # heat_flux_dim = 3 - ports_loaded = ['upper divertor','lower divertor']#'mean heat flux' + ports_loaded = [1,0]#'upper divertor','lower divertor']#'mean heat flux' mode = 'module' if verbose>0: print('derive_wetted_area_per_module: averaged 3D heat flux array over first dimension') - + if plot_it: + fig,ax=plt.subplots(1,2) + ax[0].imshow(heat_flux[0]/1e6,vmin=0,vmax=5) + ax[1].imshow(heat_flux[1]/1e6,vmin=0,vmax=5) + elif heat_flux_dim == 3 and mode == 'average_central_max': + ## assuming that the strike lines on each divertor are shifted a little bit radially to each other, shift the profiles so that maximimas agree in position + # if not p max is lower and the strike-line and wetted area would to too large. + ## sort the divertors + updiv=[] + downdiv=[] + for i in range(len(ports_loaded)): + try: + port=int(ports_loaded[i]) + except: #okay it is not an int or an int like string + port=int(ports_loaded[i][3:]) + if port%10==0: + downdiv.append(heat_flux[i]) + else: + updiv.append(heat_flux[i]) + dummyup=updiv[0].copy() + for i in range(1,len(updiv)): + for j in range(len(finger_ID)): + for k in range(finger_dic['n_profiles'][j]): + loc=np.where(mapping['Finger_ID'][0]==j*100+k) + line1=updiv[0][loc] + line2=updiv[i][loc] + if np.argmax(line1)!=0 and np.argmax(line2)!=0: + pmax_div=np.argmax(line1)-np.argmax(line2) + if pmax_div<0: + pstart=0 + pend=len(loc[0])+pmax_div + else: + pstart=pmax_div + pend=len(loc[0])-pmax_div + else: + pmax_div=0 + pstart=0 + pend=len(loc[0]) +# print(pend,max(loc[0]),pmax_div,np.shape(dummyup),max(loc[0]),max(loc[1])) + for y in range(pstart,pend): + dummyup[loc[0][y],loc[1][0]]=dummyup[loc[0][y],loc[1][0]]+updiv[i][loc[0][y]+pmax_div,loc[1][0]] + dummyup=dummyup/len(updiv) + dummydwn=downdiv[0].copy() + for i in range(1,len(downdiv)): + for j in range(len(finger_ID)): + for k in range(finger_dic['n_profiles'][j]): + loc=np.where(mapping['Finger_ID'][0]==j*100+k) + line1=downdiv[0][loc] + line2=downdiv[i][loc] + if np.argmax(line1)!=0 and np.argmax(line2)!=0: + pmax_div=np.argmax(line1)-np.argmax(line2) + if pmax_div<0: + pstart=0 + pend=len(loc[0])+pmax_div + else: + pstart=pmax_div + pend=len(loc[0])-pmax_div + else: + pmax_div = 0 + pstart =0 + pend=len(loc[0]) + for y in range(pstart,pend): + dummydwn[loc[0][y],loc[1][0]] = dummydwn[loc[0][y],loc[1][0]]+downdiv[i][loc[0][y]+pmax_div,loc[1][0]] + dummydwn = dummydwn/len(downdiv) + + heat_flux = np.array([dummyup,dummydwn]) + heat_flux = np.nan_to_num(heat_flux) + ports_loaded = [1,0]#'upper divertor','lower divertor']#'mean heat flux' + mode = 'module' + if plot_it: + print(np.shape(dummyup),np.shape(dummydwn)) +# plt.figure() + fig,ax = plt.subplots(2,2) + ax[0][0].imshow(np.nanmean(np.asarray(updiv), axis=0), vmin=0, vmax=5e6) + ax[1][0].imshow(dummyup, vmin=0, vmax=5e6) + ax[0][1].imshow(np.nanmean(np.asarray(downdiv), axis=0),vmin=0,vmax=5e6) + ax[1][1].imshow(dummydwn,vmin=0,vmax=5e6) +# print("in development") if heat_flux_dim==3: # assume dimensions: toroidal index (camera ports), row, column - n_ports, n_rows, n_cols = np.shape(heat_flux) + n_ports = np.shape(heat_flux)[0]#, n_rows, n_cols if verbose>0: print('derive_wetted_area_per_module: deriving wetted area on {0} divertor modules in {1} mode...'.format(n_ports, mode)) # derive q_max for normalization of integral @@ -840,7 +1435,7 @@ def derive_wetted_area_per_module(heat_flux, mapping, mode='average', q_max=None h = heat_flux[ij_profile[0],ij_profile[1]] central_line_integral += np.nan_to_num( np.trapz(h, x=s, axis=0) ) central_line_integral = central_line_integral / profile_average_range - if mode == 'average' or mode == 'module': + if mode in ('average','module'): finger_wetted_area[i_finger] = central_line_integral / q_max * finger_dic['width'][i_finger] elif mode == 'finger': finger_wetted_area[i_finger] = central_line_integral / q_max[i_finger] * finger_dic['width'][i_finger] @@ -857,7 +1452,7 @@ def derive_wetted_area_per_module(heat_flux, mapping, mode='average', q_max=None i_max = np.argmax([np.sum(h) for h in h_profiles]) h_max.append( h_profiles[ i_max ] ) s_max.append( mapping['s'][profile_no == central_profiles[i_max]] ) - if mode == 'module' or mode == 'average': + if mode in ('module','average'): width_max.append(np.nan_to_num(np.trapz(h_max[-1], x=s_max[-1]) / q_max)) height_max.append(q_max) elif mode == 'finger': -- GitLab