import sys
import configparser
import numpy as np
import shtns
import matplotlib.pyplot as plt
import rev_process as revpro
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
from mpi4py import MPI

def compute_chi2_components(comm, size, rank, config_file):

    if rank == 0:
        print()
        print('  Computing Earth-likeness components according to Christensen et al. (EPSL, 2010) ')
        print()

    config_file = config_file
    config = configparser.ConfigParser(interpolation=None)
    config.read(config_file)
    Verbose = config['Common'].getboolean('Verbose')
    fname_gauss = config['Gauss coefficients']['filename']
    gauss_unit = config['Gauss coefficients']['unit']
    outdir = config['Common']['output_directory']
    ltrunc_gauss = int(config['Gauss coefficients']['ltrunc'])
    tag = config['Common']['tag']
    time_unit = config['Rescaling factors and units']['time unit']

    npzfile =  np.load(outdir+'/'+fname_gauss)
    time = npzfile['time']
    glm = npzfile['glm']
    hlm = npzfile['hlm']

    a = 6371.2
    c = 3485.
    ltrunc = 8
    sh_par = shtns.sht(9, 9, norm=shtns.sht_fourpi | shtns.SHT_NO_CS_PHASE | shtns.SHT_REAL_NORM)
    sh_par.set_grid(nlat=48, nphi=96)
    ad_over_nad = np.zeros_like(time, dtype=float)
    O_over_E = np.zeros_like(time, dtype=float)
    z_over_nz = np.zeros_like(time, dtype=float)
    fcf = np.zeros_like(time, dtype=float)
#
# mpi 1D domain decomposition
    nsamp = len(time)
    nsamp_per_process = int(nsamp / size)
    mysamp_beg = rank * nsamp_per_process
    mysamp_end = mysamp_beg + nsamp_per_process
    if (rank == size-1):
        mysamp_end = nsamp
    if size > 1:
        ier = comm.Barrier()
    if Verbose is True:
        if rank == 0:
            print('    1D domain decomposition for processing:', flush=True)
    if Verbose is True:
       print('        beg end ', mysamp_beg, mysamp_end, ' for process ', rank, flush=True)

    #for itime in range(len(time)):
    for itime in range(mysamp_beg, mysamp_end):
    	if np.mod(itime+1-mysamp_beg, int(nsamp_per_process/10)) == 0 and Verbose is True:
             if rank==0:
                 print('        rank ', rank, ' performed ', itime+1-mysamp_beg, ' analyses', flush=True)
	# AD/NAD ratio
    	nad = 0.
    	nad = nad + 2.*(glm[itime,1,1]**2 + hlm[itime,1,1]**2)
    	for il in range(2,ltrunc+1):
    		toto = 0.
    		for im in range(0,il+1):
    			toto = toto + (glm[itime,il,im]**2+hlm[itime,il,im]**2)
    		nad = nad + (il+1.) * (a/c)**(2.*il-2.)*toto
    	ad_over_nad[itime] = (2.* glm[itime, 1,0]**2 ) / nad

	# Equatorial symmetry
    	odd = 0.
    	even = 0.
    	for il in range(2,ltrunc+1):
    		for im in range(0,il+1):
    			if ( (il+im) % 2 == 0 ):
    				even = even + (a/c)**(2*il-2) * (il+1.) * (glm[itime,il,im]**2+hlm[itime,il,im]**2)
    			else:
    				odd  = odd + (a/c)**(2*il-2) * (il+1.) * (glm[itime,il,im]**2+hlm[itime,il,im]**2)
    	O_over_E[itime] = odd / even
	# Zonality
    	zonal = 0.
    	nonzonal = 0.
    	for il in range(2,ltrunc+1):
    		zonal = zonal + (a/c)**(2*il-2) * (il+1.) * (glm[itime,il,0]**2+hlm[itime,il,0]**2)
    		#print(zonal, glm[itime,il,0], hlm[itime,il,0])
    		for im in range(1,il+1):
    			nonzonal = nonzonal + (a/c)**(2*il-2) * (il+1.) * (glm[itime,il,im]**2+hlm[itime,il,im]**2)
    			#print(nonzonal, glm[itime,il,im], hlm[itime,il,im])
    	z_over_nz[itime] = zonal / nonzonal
	# Flux concentration factor
	# need br_lm_trunc at degree 8
    	br_lm_trunc = revpro.compute_brlm_from_glmhlm(glm[itime,:,:], hlm[itime,:,:], sh_par, ltrunc = ltrunc, bscale =None, radius=None)
    	br2_rms = np.sum( (np.abs(br_lm_trunc))**2 )
    	brf = sh_par.synth(br_lm_trunc)
    	brf2 = brf*brf
    	br2_lm = sh_par.analys(brf2)
    	br4_rms = np.sum( (np.abs(br2_lm))**2 )
    	fcf[itime] = (br4_rms - br2_rms**2) / br2_rms**2

    if size>1:
        ad_over_nad = comm.allreduce( ad_over_nad, op=MPI.SUM)
        O_over_E = comm.allreduce( O_over_E, op=MPI.SUM)
        z_over_nz = comm.allreduce( z_over_nz, op=MPI.SUM)
        fcf = comm.allreduce( fcf, op=MPI.SUM)
    # safety net with z_over_nz that can go inf during transients
    mask = ~np.isinf(z_over_nz)
    ad_over_nad = ad_over_nad[mask]
    O_over_E = O_over_E[mask]
    z_over_nz = z_over_nz[mask]
    fcf = fcf[mask]

    return ad_over_nad, O_over_E, z_over_nz, fcf

def compute_chi2(comm, size, rank, config_file):

    ad_over_nad, O_over_E, z_over_nz, fcf = compute_chi2_components(comm, size, rank, config_file)
#   Earth values are there
    ad_over_nad_earth = 1.4
    O_over_E_earth = 1.
    z_over_nz_earth = 0.15
    fcf_earth = 1.5
    static_chi2 = ( np.log(np.mean(ad_over_nad)/ad_over_nad_earth) / np.log(2.0) ) **2 \
                 +( np.log(np.mean(O_over_E)/O_over_E_earth) / np.log(2.0) ) **2 \
                 +( np.log(np.mean(z_over_nz)/z_over_nz_earth) / np.log(2.5) ) **2 \
                 +( np.log(np.mean(fcf)/fcf_earth) / np.log(1.75) ) **2
    all_chi2 =    ( np.log(ad_over_nad/ad_over_nad_earth) / np.log(2.0) ) **2 \
                 +( np.log(O_over_E/O_over_E_earth) / np.log(2.0) ) **2 \
                 +( np.log(z_over_nz/z_over_nz_earth) / np.log(2.5) ) **2 \
                 +( np.log(fcf/fcf_earth) / np.log(1.75) ) **2
    if rank == 0:
        print()
        print('			AD_OVER_NAD = ', np.mean(ad_over_nad))
        print('			O_OVER_E = ', np.mean(O_over_E))
        print('			Z_OVER_NZ = ', np.mean(z_over_nz))
        print('			Flux Conc. Fact. = ', np.mean(fcf))
        print()
        print('  	 chi2 = ', static_chi2)
        print(' 	 median  chi2 over time = ', np.median(all_chi2))

    return static_chi2

def get_rescaling_factors(comm, size, rank, config_file):

    if rank == 0:
        print()
        print('  Determining rescaling factors for time and magnetic field strength ')
        print()

# initialize parameters
    config_file = config_file
    config = configparser.ConfigParser(interpolation=None)
    config.read(config_file)
    dynamo_code = config['Common']['dynamo_code']
    outdir = config['Common']['output_directory']
    tag = config['Common']['tag']
    Verbose = config['Common'].getboolean('Verbose')
    dump_spectra = config['Rescaling'].getboolean('dump_spectra')
    plot_tausv = config['Rescaling'].getboolean('plot_tausv')

    ltrunc = 13

    if rank == 0:
        if Verbose is True:
            print('    mpi parallel size is ', size)
            print('    dynamo code is ', dynamo_code)

    if dynamo_code == "xshells":
        percent = int(config['Rescaling']['percent_scales'])
        fname = config['Common']['filename'] # in case xshells is post-processed

        if Verbose is True and rank==0:
            print('    filename is ', fname)
        raw = np.fromfile(fname, dtype=np.float64)
        sh = shtns.sht(13)
        sh_schmidt = shtns.sht(13, norm=shtns.sht_fourpi | shtns.SHT_REAL_NORM)
        nlm = shtns.nlm_calc(13,13,1)
        raw = raw.reshape((-1,2*nlm+1))
        raw.shape
#
        if Verbose is True and rank==0:
            print('    considering the first ', percent,' percent of data to establish rescaling factors', flush=True)
        tag = tag+'_'+str(percent)+'%'
#
        twork = raw[:,0]
        end = int( percent * len(twork) / 100 )
        t = raw[:end,0]
        keep = revpro.clean_series(t, Verbose=Verbose, myrank=rank)
        t = t[keep]
        br_lm = (raw[:end,1::2] + 1j*raw[:end,2::2])*sh.l*(sh.l+1)   # multiply by l(l+1)
        br_lm = br_lm[keep,:]
        sh.set_grid(nlat=48, nphi=96)#, flags=shtns.sht_reg_poles)
        sh_schmidt.set_grid(nlat=48, nphi=96)#, flags=shtns.sht_reg_poles)
        if rank == 0 and Verbose is True:
            print('    total number of samples = ', len(t))
# nsamp = int( len(t)/1000)
        nsamp = len(t)
        time = np.zeros( nsamp )
        g10 = np.zeros( nsamp )
        sp_b = np.zeros( (nsamp, ltrunc+1) )
        sp_bdot = np.zeros( (nsamp, ltrunc+1) )
        tau_l = np.zeros( (nsamp, ltrunc+1) )
        tau_sv_avg = np.zeros( ltrunc+1 )
        mask = np.zeros(nsamp, dtype=bool)
        mask[:] = False
# one_percent = nsamp/100
        t_max = -0.1
#
# mpi 1D domain decomposition
        nsamp_per_process = int( nsamp / size)
        mysamp_beg = rank * nsamp_per_process
        mysamp_end = mysamp_beg + nsamp_per_process
        if (rank == size-1):
            mysamp_end = nsamp
        if size >1:
            comm.Barrier()
        if Verbose is True:
            if rank == 0:
                print('    1D domain decomposition for processing:', flush=True)
        if Verbose is True:
           print('        beg end ', mysamp_beg, mysamp_end, ' for process ', rank, flush=True)
###
#
        for i in range(mysamp_beg,mysamp_end):
            br = sh.synth(br_lm[i,:])
            br_lm_schmidt = sh_schmidt.analys(br)
            glm, hlm, ghlm = revpro.compute_glmhlm_from_brlm( br_lm_schmidt, sh_schmidt, ltrunc = None, bscale = None)
            time[i] = t[i]
            deltat = t[i] - t[i-1]
            test = (deltat > 0. and (i > mysamp_beg) )
            if test is True:
                mask[i] = True
                glmdot = ( glm - glm_old ) / deltat
                hlmdot = ( hlm - hlm_old ) / deltat
                for il in range(1, 14):
                    for im in range(0, il+1):
                        sp_b[ i, il] = sp_b[ i, il] + (il+1) * ( glm[ il, im]**2 + hlm[il, im]**2 )
                        sp_bdot[ i,  il] = sp_bdot[ i, il] + (il+1) * ( glmdot[ il, im]**2 + hlmdot[il, im]**2 )
                    tau_l[i, il] = np.sqrt ( sp_b[ i, il] / sp_bdot[ i, il] )
            glm_old = glm
            hlm_old = hlm
            g10[i] = glm[1,0]
#
    elif dynamo_code == "parody":
#
        ltrunc = 12
        import parody_toolbox_wf as ptool
        import glob
        path = config['Parody']['data_location']
        fname = config['Parody']['surface_fname']
        files = glob.glob( path + '/' + fname )
        nlat, nphi, time, br, dbrdt = ptool.get_data_from_S_file(files[rank], Verbose=False)
        sh_schmidt = shtns.sht(ltrunc, norm=shtns.sht_fourpi | shtns.SHT_REAL_NORM)
        sh_schmidt.set_grid(nlat=nlat, nphi=nphi)
        #sh = shtns.sht( lmax, mmax, mres, norm=shtns.sht_fourpi | shtns.SHT_REAL_NORM)
        #sh.set_grid(nlat, nphi)
        theta = np.arccos(sh_schmidt.cos_theta)
        phi =  (2. * np.pi / np.float(nphi))*np.arange(nphi) - np.pi
        #revpro.mollweide_surface(br, theta, phi, fname=None, vmax=None, vmin=None, Title=None, positive=False, cmap=None, unit="nondim")
        nsamp = len(files)
        if Verbose is True:
            print('number of surface files', nsamp)
        time = np.zeros( nsamp )
        g10 = np.zeros( nsamp )
        br = np.zeros( (nsamp, nlat, nphi) )
        dbrdt = np.zeros( (nsamp, nlat, nphi) )
        sp_b = np.zeros( (nsamp, ltrunc+1) )
        sp_bdot = np.zeros( (nsamp, ltrunc+1) )
        tau_l = np.zeros( (nsamp, ltrunc+1) )
        tau_sv_avg = np.zeros( ltrunc+1 )
        mask = np.zeros(nsamp, dtype=bool)
        mask[:] = False
        # mpi 1D domain decomposition
        nsamp_per_process = int( nsamp / size)
        mysamp_beg = rank * nsamp_per_process
        mysamp_end = mysamp_beg + nsamp_per_process
        if (rank == size-1):
            mysamp_end = nsamp
        if size >1:
            comm.Barrier()
        if Verbose is True:
            if rank == 0:
                print('    1D domain decomposition for processing:', flush=True)
        if Verbose is True:
           print('        beg end ', mysamp_beg, mysamp_end, ' for process ', rank, flush=True)
###
        for i in range(mysamp_beg,mysamp_end):
            nlat, nphi, this_time, this_br, this_dbrdt = ptool.get_data_from_S_file(files[i], Verbose=False)
            time[i] = this_time
            br[i,:,:] = this_br[:,:]
            dbrdt[i,:,:] = this_dbrdt[:,:]
        if size > 1:
            time = comm.allreduce( time, op=MPI.SUM)
            br = comm.allreduce( br, op=MPI.SUM)
            dbrdt = comm.allreduce( dbrdt, op=MPI.SUM)
        time = time[ np.argsort(time) ]
        br = br[ np.argsort(time) ]
        dbrdt = dbrdt[ np.argsort(time) ]

        for i in range( mysamp_beg, mysamp_end):
            br_lm = sh_schmidt.analys( br[i,:,:] )
            glm, hlm, ghlm = revpro.compute_glmhlm_from_brlm(br_lm, sh_schmidt, ltrunc = ltrunc, bscale = None)
            brdot_lm = sh_schmidt.analys( dbrdt[i,:,:] )
            glmdot, hlmdot, ghlmdot = revpro.compute_glmhlm_from_brlm(brdot_lm, sh_schmidt, ltrunc = ltrunc, bscale = None)
            test = True
            if test is True:
                mask[i] = True
                for il in range(1, ltrunc+1):
                    for im in range(0, il+1):
                        sp_b[ i, il] = sp_b[ i, il] + (il+1) * ( glm[ il, im]**2 + hlm[il, im]**2 )
                        sp_bdot[ i,  il] = sp_bdot[ i, il] + (il+1) * ( glmdot[ il, im]**2 + hlmdot[il, im]**2 )
                    tau_l[i, il] = np.sqrt ( sp_b[ i, il] / sp_bdot[ i, il] )
                """
                plt.semilogy( np.arange(1, 14), sp_b[i, 1:14], label='field')
                plt.semilogy( np.arange(1, 14), sp_bdot[i, 1:14], label='SV')
                plt.semilogy( np.arange(1, 14), tau_l[i, 1:14], label=r'$\tau_\ell$')
                plt.legend()
                plt.show()
                sys.exit()
                """
            g10[i] = glm[1,0]

    if size>1:
        g10 = comm.allreduce(g10, op=MPI.SUM)
        tau_l = comm.allreduce(tau_l, op=MPI.SUM)
        mask = comm.allreduce(mask, op=MPI.SUM)
        sp_b = comm.allreduce(sp_b, op=MPI.SUM)
        sp_bdot = comm.allreduce(sp_bdot, op=MPI.SUM)

    if rank == 0:
        my_g10 = g10[mask]
        my_time = time[mask]-time[0] # start at t=0.
        my_tau_l = tau_l[mask,:]
        my_sp_b = sp_b[mask,:]
        my_sp_bdot = sp_bdot[mask,:]
        if dump_spectra is True:
            fname = 'extended_spectra_unprocessed_'+tag
            np.savez_compressed(outdir+'/'+fname,  sp_b = my_sp_b, sp_bdot = my_sp_bdot, tau_l = my_tau_l)
            fname = fname+'.npz'
            config.set('Diags', 'spectra_file', fname)
            lfile = open(config_file, 'w')
            config.write(lfile)
            lfile.close()
        for il in range(1,ltrunc+1):
            tau_sv_avg[ il] = np.sqrt( np.average(my_sp_b[:,:], axis =0)[il] / np.average(my_sp_bdot[:,:], axis =0)[il] )

        def one_over_l(x,tau_sv):

            return tau_sv / x

# fit with a 1 / ell law for tau_ell
        popt, pcov = curve_fit(one_over_l, np.arange(2, ltrunc+1), tau_sv_avg[2:ltrunc+1])
        if Verbose is True:
            print('    secular variation time scales ')
            print('       {}      {}    {}'.format('SH degree', 'tau_l', 'tau_sv / l') )
            for il in range(2,ltrunc+1):
                print('           {:2d}    {:>10f}     {:>10f}'.format(il, tau_sv_avg[il], float(one_over_l(il, popt))) )
        scaling_factor_time = float( 415. / popt)
        if Verbose is True:
            print( '    Scaling factors: ')
            print( '        time conversion factor = ', scaling_factor_time)

        if plot_tausv is True:
            plt.scatter( range(2, 13), tau_sv_avg[2:13], marker='s', color='r', label=r'average $\tau_\ell$ ')
            plt.plot( range(2, 13), one_over_l( range(2,13), popt), lw=2, label='(1/$\ell$) fit')
            plt.xscale('log')
            plt.yscale('log')
            plt.xlabel('spherical harmonic degree $\ell$')
            plt.legend(loc='best')
            plt.tight_layout()
            plt.savefig(outdir+'/'+'sv_timescale_'+tag+'.pdf')
            plt.close()

        g10_mean = np.mean(abs(g10))
        vadm_earth = 7.46*1.e22
        r_earth = 6371.2e3
        mu0 = 4. * np.pi * 1.e-7
        g10_mean_earth = vadm_earth * mu0 / (4. *np.pi * r_earth**3)
        scaling_factor_mag = g10_mean_earth * 1.e3 / g10_mean # in mT
        if Verbose is True:
            print( '        magnetic field conversion factor (to obtain mT) = ', scaling_factor_mag)
            print( '        time average abs(g10) = {:>3f} nT'.format( scaling_factor_mag * 1e6 * g10_mean) )
        np.savez(outdir+'/'+'conversion_factors_'+tag, scaling_factor_time = scaling_factor_time, scaling_factor_mag = scaling_factor_mag)

        if config.has_section('Rescaling factors and units') is False:
	        config.add_section('Rescaling factors and units')
        config.set('Rescaling factors and units', 'scaling_factor_mag', str(scaling_factor_mag))
        config.set('Rescaling factors and units', 'mag unit', 'mT')
        config.set('Rescaling factors and units', 'scaling_factor_time', str(scaling_factor_time))
        config.set('Rescaling factors and units', 'time unit', 'yr')
        config.set('Common', 'rescaling_done', 'True')
        config_file = open(config_file, 'w')
        config.write(config_file)
        config_file.close()

def make_gauss_history(comm, size, rank, config_file):

    if rank == 0:
        print()
        print('  Constructing history of Gauss coefficients ')
        print(flush=True)

# initialize parameters
    config_gauss = configparser.ConfigParser(interpolation=None)
    config_gauss.read(config_file)
    dynamo_code = config_gauss['Common']['dynamo_code']
    fname = config_gauss['Common']['filename']
    tag = config_gauss['Common']['tag']
    outdir = config_gauss['Common']['output_directory']
    Verbose = config_gauss['Common'].getboolean('Verbose')
    nskip_analysis = int(config_gauss['Common']['nskip_analysis'])
    scaling_factor_time = float(config_gauss['Rescaling factors and units']['scaling_factor_time'])
    scaling_factor_mag = float(config_gauss['Rescaling factors and units']['scaling_factor_mag'])
    mag_unit = config_gauss['Rescaling factors and units']['mag unit']
    time_unit = config_gauss['Rescaling factors and units']['time unit']

    if rank == 0:
        if Verbose is True:
            print('    mpi parallel size is ', size)

    if Verbose is True:
        if rank == 0:
            print('        scaling_factor_time', scaling_factor_time)
            print('        scaling_factor_mag', scaling_factor_mag)

    if dynamo_code == "xshells":

        raw = np.fromfile(fname, dtype=np.float64)
        ltrunc = 13
        sh = shtns.sht(13)
        sh_schmidt = shtns.sht(13, norm=shtns.sht_fourpi | shtns.SHT_REAL_NORM)
        nlm = shtns.nlm_calc(13,13,1)
        raw = raw.reshape((-1,2*nlm+1))
        raw.shape
#
        nskip = nskip_analysis
#
        t = raw[::nskip,0]
        keep = revpro.clean_series(t, Verbose=Verbose, myrank=rank)
        t = t[keep]
        br_lm = (raw[::nskip,1::2] + 1j*raw[::nskip,2::2])*sh.l*(sh.l+1)   # multiply by l(l+1)
        br_lm = br_lm[keep,:]
        sh.set_grid(nlat=48, nphi=96)#, flags=shtns.sht_reg_poles)
        sh_schmidt.set_grid(nlat=48, nphi=96)#, flags=shtns.sht_reg_poles)

        if rank == 0 and Verbose is True:
            print('    total number of samples = ', len(t))

        nsamp = len(t)
        t = t * scaling_factor_time
        br_lm = br_lm * scaling_factor_mag

    elif dynamo_code == "parody":
#
        ltrunc = 13
        import parody_toolbox_wf as ptool
        import glob
        path = config_gauss['Parody']['data_location']
        fname = config_gauss['Parody']['surface_fname']
        files = glob.glob( path + '/' +fname )
        nlat, nphi, time, br, dbrdt = ptool.get_data_from_S_file(files[rank], Verbose=False)
        sh_schmidt = shtns.sht(ltrunc, norm=shtns.sht_fourpi | shtns.SHT_REAL_NORM)
        sh_schmidt.set_grid(nlat=nlat, nphi=nphi)
        theta = np.arccos(sh_schmidt.cos_theta)
        phi =  (2. * np.pi / np.float(nphi))*np.arange(nphi) - np.pi
        nsamp = len(files)
        if Verbose is True:
            print('number of surface files', nsamp)

    glm = np.zeros( (nsamp, ltrunc+1, ltrunc+1) )
    hlm = np.zeros( (nsamp, ltrunc+1, ltrunc+1) )
    ghlm = np.zeros( (nsamp, ltrunc *(ltrunc+2) ) )
    time = np.zeros( nsamp )
    if dynamo_code == "parody":
        br = np.zeros( (nsamp, nlat, nphi), dtype=float)
    mask = np.zeros(nsamp, dtype=bool)
    mask[:] = False
#
# mpi 1D domain decomposition
    nsamp_per_process = int(nsamp / size)
    mysamp_beg = rank * nsamp_per_process
    mysamp_end = mysamp_beg + nsamp_per_process
    if (rank == size-1):
        mysamp_end = nsamp
    if size > 1:
        ier = comm.Barrier()
        samp_beg = np.zeros( size, dtype=int)
        samp_end = np.zeros( size, dtype=int)
        samp_beg[rank] = mysamp_beg
        samp_end[rank] = mysamp_end
        samp_beg = comm.allreduce(samp_beg, op=MPI.SUM)
        samp_end = comm.allreduce(samp_end, op=MPI.SUM)
    if Verbose is True:
        if rank == 0:
            print('    1D domain decomposition for processing:', flush=True)
    if Verbose is True:
       print('        beg end ', mysamp_beg, mysamp_end, ' for process ', rank, flush=True)

###
    if dynamo_code == "xshells":
        for i in range(mysamp_beg,mysamp_end):
            br = sh.synth(br_lm[i,:])
            br_lm_schmidt = sh_schmidt.analys(br)
            if mag_unit == 'mT':
                bscale = 1.e6
                gauss_unit = 'nT'
            else:
                bscale = None
                gauss_unit = 'ND'
            glm[ i, :, :], hlm[ i, :, :], ghlm[ i, :] = revpro.compute_glmhlm_from_brlm( br_lm_schmidt, sh_schmidt, ltrunc = ltrunc, bscale = bscale)
            time[i] = t[i]
            deltat = t[i] - t[i-1]
            test = (deltat > 0. and (i > mysamp_beg) )
            if test == True:
                 mask[i] = True
    elif dynamo_code == "parody":
        for i in range(mysamp_beg,mysamp_end):
            nlat, nphi, this_time, this_br, this_dbrdt = ptool.get_data_from_S_file(files[i], Verbose=False)
            time[i] = this_time
            br[i,:,:] = this_br[:,:]
        if size > 1:
            time = comm.allreduce( time, op=MPI.SUM)
            br = comm.allreduce( br, op=MPI.SUM)
        time = time[ np.argsort(time) ] * scaling_factor_time
        br = br[ np.argsort(time) ] * scaling_factor_mag

        for i in range( mysamp_beg, mysamp_end):
            br_lm = sh_schmidt.analys( br[i,:,:] )
            mask[i] = True
            if mag_unit == 'mT':
                bscale = 1.e6
                gauss_unit = 'nT'
            else:
                bscale = None
                gauss_unit = 'ND'
            glm[ i, :, :], hlm[ i, :, :], ghlm[ i, :] = revpro.compute_glmhlm_from_brlm(br_lm, sh_schmidt, ltrunc = ltrunc, bscale = bscale)

    if size>1:
        ier = comm.Barrier()
        if rank==0:
            for sender in range(1,size):
                data = comm.recv(source=sender, tag=sender)
                print('glm sender=', sender, 'shape=',np.shape(data))
                glm[ samp_beg[sender]:samp_end[sender] ] = data
        else:
            comm.send(glm[mysamp_beg:mysamp_end], dest=0, tag=rank)
        ier = comm.Barrier()
        if rank==0:
            for sender in range(1,size):
                data = comm.recv(source=sender, tag=sender)
                print('hlm sender=', sender, 'shape=',np.shape(data))
                hlm[ samp_beg[sender]:samp_end[sender] ] = data
        else:
            comm.send(hlm[mysamp_beg:mysamp_end], dest=0, tag=rank)
        ier = comm.Barrier()
        if rank==0:
            for sender in range(1,size):
                data = comm.recv(source=sender, tag=sender)
                print('ghlm sender=', sender, 'shape=',np.shape(data))
                ghlm[ samp_beg[sender]:samp_end[sender] ] = data
        else:
            comm.send(ghlm[mysamp_beg:mysamp_end], dest=0, tag=rank)
        ier = comm.Barrier()
        if rank==0:
            for sender in range(1,size):
                data = comm.recv(source=sender, tag=sender)
                print('sender=', sender, 'shape=',np.shape(data))
                mask[ samp_beg[sender]:samp_end[sender] ] = data
        else:
            comm.send(mask[mysamp_beg:mysamp_end], dest=0, tag=rank)
        ier = comm.Barrier()
        if rank==0:
            for sender in range(1,size):
                data = comm.recv(source=sender, tag=sender)
                print('sender=', sender, 'shape=',np.shape(data))
                time[ samp_beg[sender]:samp_end[sender] ] = data
        else:
            comm.send(time[mysamp_beg:mysamp_end], dest=0, tag=rank)

    if rank == 0:
        my_glm = glm[mask, :, :]
        my_hlm = hlm[mask, :, :]
        my_ghlm = ghlm[mask, :]
        my_time = time[mask]-time[0] # start at t=0.
        if dynamo_code == "xshells":
            gauss_fname = 't_gauss_nskip%i_'%nskip+tag
        elif dynamo_code == "parody":
            gauss_fname = 't_gauss_'+tag
        np.savez(outdir+'/'+gauss_fname, time = my_time, glm = my_glm, hlm = my_hlm, ghlm = my_ghlm)
        if config_gauss.has_section('Gauss coefficients') is False:
            config_gauss.add_section('Gauss coefficients')
        config_gauss.set('Gauss coefficients', 'ltrunc', str(ltrunc) )
        config_gauss.set('Gauss coefficients', 'unit', gauss_unit)
        config_gauss.set('Gauss coefficients', 'filename', gauss_fname+'.npz')
        config_gauss.set('Common', 'gauss_done', 'True')
        config_file = open(config_file, 'w')
        config_gauss.write(config_file)
        config_file.close()
#
def prepare_SHB_plot( comm, size, rank, config_file):

    if rank == 0:
        print()
        print('  building design matrices on regular grid ')
        print()

    config_file = config_file
    config = configparser.ConfigParser(interpolation=None)
    config.read(config_file)
    Verbose = config['Common'].getboolean('Verbose')
    outdir = config['Common']['output_directory']

    l_trunc = 10
    sh = shtns.sht(l_trunc)
    sh.set_grid(nlat=48, nphi=96)
    theta = np.arccos( sh.cos_theta )
    phi = np.arange(0,sh.nphi) * 2 * np.pi / sh.nphi

    ntheta = len(theta)
    nphi = len(phi)
    npt = ntheta * nphi

    colatitude_in_deg = np.zeros( npt, dtype = float )
    longitude_in_deg = np.zeros( npt, dtype = float )
    ipt = -1
    for itheta in range(ntheta):
        for iphi in range(nphi):
            ipt = ipt + 1
            colatitude_in_deg[ ipt] = np.rad2deg(theta[itheta])
            longitude_in_deg[ ipt] = np.rad2deg(phi[iphi])

    radius = 6371.2 * np.ones_like(colatitude_in_deg)

    nsh = l_trunc*(l_trunc+2)
    SHBX = np.zeros( (npt, nsh ), dtype=float)
    SHBY = np.zeros( (npt, nsh ), dtype=float)
    SHBZ = np.zeros( (npt, nsh ), dtype=float)

    npt_per_process = int(npt / size)
    mypt_beg = rank * npt_per_process
    mypt_end = mypt_beg + npt_per_process
    if (rank == size-1):
        mypt_end = npt
    if size > 1:
        ier = comm.Barrier()
    if Verbose is True:
        if rank == 0:
            print('    1D domain decomposition for processing:', flush=True)
    if Verbose is True:
       print('        beg end ', mypt_beg, mypt_end, ' for process ', rank, flush=True)

    for ipt in range( mypt_beg, mypt_end):
        SHBX[ipt,:] = revpro.SHB_X(colatitude_in_deg[ipt], longitude_in_deg[ipt], radius[ipt], ll=l_trunc )
        SHBY[ipt,:] = revpro.SHB_Y(colatitude_in_deg[ipt], longitude_in_deg[ipt], radius[ipt], ll=l_trunc )
        SHBZ[ipt,:] = revpro.SHB_Z(colatitude_in_deg[ipt], longitude_in_deg[ipt], radius[ipt], ll=l_trunc )

    if size>1:
        SHBX = comm.allreduce(SHBX, op=MPI.SUM)
        SHBY = comm.allreduce(SHBY, op=MPI.SUM)
        SHBZ = comm.allreduce(SHBZ, op=MPI.SUM)

    if rank == 0:
        filename = 'SHB_nlat%i_nlon%i'%(ntheta,nphi)
        np.savez(outdir+'/'+filename, SHBX = SHBX, SHBY = SHBY, SHBZ = SHBZ, l_trunc=l_trunc, npt=npt, theta=theta,phi=phi)
        if config.has_section('Design matrices on regular grid') is False:
            config.add_section('Design matrices on regular grid')
        config.set('Design matrices on regular grid', 'l_trunc', str(l_trunc) )
        config.set('Design matrices on regular grid', 'nlat', str(ntheta) )
        config.set('Design matrices on regular grid', 'nlon', str(nphi) )
        config.set('Design matrices on regular grid', 'npt', str(npt) )
        config.set('Design matrices on regular grid', 'filename', filename+'.npz')
        config.set('Common', 'SHB_plot_done', 'True')
        config_file = open(config_file, 'w')
        config.write(config_file)

def get_transition_time( comm, size, rank, config_file):

    if rank == 0:
        print()
        print('  Detection of polarity transitions ')
        print()

    config_file = config_file
    config = configparser.ConfigParser(interpolation=None)
    config.read(config_file)
    outdir = config['Common']['output_directory']
    fname = outdir+'/'+config['Diags']['pole_latitude_file']

    npz = np.load(fname)
    pole_lat = npz['pole_latitude']
    mask_tra = ( np.abs(pole_lat) <= 45. )
    mask_stb = ( np.abs(pole_lat) > 45. )

    return mask_tra, mask_stb

def analyze_transitional_field( comm, size, rank, config_file):

    config_file = config_file
    config = configparser.ConfigParser(interpolation=None)
    config.read(config_file)
    outdir = config['Common']['output_directory']
    Verbose = config['Common'].getboolean('Verbose')

    mask_tra, mask_stb = get_transition_time( comm, size, rank, config_file)
    if rank == 0:
        print()
        print('  Spectra of stable and transitional fields ')
        print()
    spectra_file = config['Diags']['spectra_file']
    fname = outdir+'/'+spectra_file
    scaling_factor_mag = float(config['Rescaling factors and units']['scaling_factor_mag'])
    mag_unit = config['Rescaling factors and units']['mag unit']
    if rank == 0:
        npz = np.load(fname)
        sp_b = npz['sp_b']
        if Verbose is True:
            print('   Conformity of arrays   ')
            print('           ',np.shape(sp_b))
            print('           ',np.shape(mask_tra))
        sp_b_tra = sp_b[mask_tra,:] * scaling_factor_mag**2
        sp_b_stb = sp_b[mask_stb,:] * scaling_factor_mag**2
        plt.semilogy(range(1,14), np.mean(sp_b_tra, axis=0)[1:14], 'o', label='transitional')
        plt.semilogy(range(1,14), np.mean(sp_b_stb, axis=0)[1:14], 'x', label='stable' )
        plt.xlabel('SH degree')
        plt.ylabel(r'B$^2$ in '+mag_unit+'$^2$')
        plt.xticks(range(1,14))
        plt.legend(loc='best')
        plt.savefig(outdir+'/'+'sp_a.pdf')
        plt.close()
        a = 6371.2
        c = 3485.0
        for il in range(1,14):
            sp_b_tra[:,il] = (a/c)**(2*il+4) *  sp_b_tra[:,il]
            sp_b_stb[:,il] = (a/c)**(2*il+4) *  sp_b_stb[:,il]
        plt.semilogy(range(1,14), np.mean(sp_b_tra, axis=0)[1:14], 'o', label='transitional')
        plt.semilogy(range(1,14), np.mean(sp_b_stb, axis=0)[1:14], 'x', label='stable' )
        plt.xlabel('SH degree')
        plt.ylabel(r'B$^2$ in '+mag_unit+'$^2$')
        plt.xticks(range(1,14))
        plt.legend(loc='best')
        plt.savefig(outdir+'/'+'sp_c.pdf')
        plt.close()
        for iens in range(np.shape(sp_b_tra)[0]):
                plt.semilogy(range(1,14), sp_b_tra[iens,1:14], color='r',lw=0.1, alpha=0.3)
        for iens in range(np.shape(sp_b_tra)[0]):
                plt.semilogy(range(1,14), sp_b_stb[iens,1:14], color='b' ,lw=0.1, alpha=0.3)
        plt.semilogy(range(1,14), np.mean(sp_b_tra, axis=0)[1:14], 'o', label='transitional', color='r')
        plt.semilogy(range(1,14), np.mean(sp_b_stb, axis=0)[1:14], 'x', label='stable', color='b' )
        plt.xlabel('SH degree')
        plt.ylabel(r'B$^2$ in '+mag_unit+'$^2$')
        plt.xticks(range(1,14))
        plt.legend(loc='best')
        plt.savefig(outdir+'/'+'sp_c_ens.pdf')
        plt.close()

    return

def get_pole_latitude( comm, size, rank, config_file):

    if rank == 0:
        print()
        print('  Computation of geomagnetic pole latitude ')
        print()

    config_file = config_file
    config = configparser.ConfigParser(interpolation=None)
    config.read(config_file)
    Verbose = config['Common'].getboolean('Verbose')
    fname_gauss = config['Gauss coefficients']['filename']
    gauss_unit = config['Gauss coefficients']['unit']
    outdir = config['Common']['output_directory']
    ltrunc_gauss = int(config['Gauss coefficients']['ltrunc'])
    tag = config['Common']['tag']
    time_unit = config['Rescaling factors and units']['time unit']

    npzfile =  np.load(outdir+'/'+fname_gauss)
    time = npzfile['time']
    ghlm = npzfile['ghlm']

    ntime = int ( len(time) ) # / 5 )
    ntime_per_process = int(ntime / size)
    mytime_beg = rank * ntime_per_process
    mytime_end = mytime_beg + ntime_per_process
    if (rank == size-1):
        mytime_end = ntime
    if size > 1:
        ier = comm.Barrier()
    if Verbose is True:
        if rank == 0:
            print('    1D domain decomposition for processing:', flush=True)
    if Verbose is True:
       print('        beg end ', mytime_beg, mytime_end, ' for process ', rank, flush=True)

    pole_latitude = np.zeros( ntime, dtype=float)

    for itime in range(mytime_beg, mytime_end):
        g10 = ghlm[itime, 0]
        g11 = ghlm[itime, 1]
        h11 = ghlm[itime, 2]
        pole_latitude[itime] = np.rad2deg( np.arctan2( g10, np.sqrt(g11**2+h11**2) )  )

    if size>1:
        pole_latitude = comm.allreduce( pole_latitude, op=MPI.SUM)

    if rank==0:
        fname = 'pole_latitude'
        np.savez(outdir+'/'+fname, time=time, pole_latitude=pole_latitude, time_unit = time_unit)
        fname = fname + '.npz'
        config.set('Diags', 'pole_latitude_file', fname)
        lfile = open(config_file, 'w')
        config.write(lfile)
        lfile.close()

    if rank==0:
        #distibute plot over 3 rows
        nrows = 3
        ndat = len(time)
        nstep = int( ndat / nrows )
        yticks = np.linspace( -90, 90, 5, dtype=float)
        fig, ax = plt.subplots( nrows, 1, figsize=(15,6))
        for i in range(nrows):
            istart = i * nstep
            if i < nrows -1:
                iend = istart + nstep
            else:
                iend = ndat-1
            ax[i].plot( time[istart:iend], pole_latitude[istart:iend])
            ax[i].set_xlim(time[istart],time[iend])
            ax[i].set_ylim(-90,90)
            ax[i].set_yticks(yticks)
            ax[i].set_ylabel('pole latitude (deg)')
        ax[nrows-1].set_xlabel('time in ' + time_unit)
        plt.savefig(outdir+'/pole_latitude.pdf')
        plt.savefig(outdir+'/pole_latitude.png')
        plt.close()

    return time, pole_latitude, time_unit

def get_eccentricity( comm, size, rank, config_file):

    if rank == 0:
        print()
        print('  Computation of dipole eccentricity ')
        print()

    config_file = config_file
    config = configparser.ConfigParser(interpolation=None)
    config.read(config_file)
    Verbose = config['Common'].getboolean('Verbose')
    fname_gauss = config['Gauss coefficients']['filename']
    gauss_unit = config['Gauss coefficients']['unit']
    outdir = config['Common']['output_directory']
    ltrunc_gauss = int(config['Gauss coefficients']['ltrunc'])
    tag = config['Common']['tag']
    time_unit = config['Rescaling factors and units']['time unit']

    npzfile =  np.load(outdir+'/'+fname_gauss)
    time = npzfile['time']
    ghlm = npzfile['ghlm']

    ntime = int ( len(time) ) # / 5 )
    ntime_per_process = int(ntime / size)
    mytime_beg = rank * ntime_per_process
    mytime_end = mytime_beg + ntime_per_process
    if (rank == size-1):
        mytime_end = ntime
    if size > 1:
        ier = comm.Barrier()
    if Verbose is True:
        if rank == 0:
            print('    1D domain decomposition for processing:', flush=True)
    if Verbose is True:
       print('        beg end ', mytime_beg, mytime_end, ' for process ', rank, flush=True)

    sc = np.zeros( ntime, dtype=float)
    zc = np.zeros( ntime, dtype=float)
    a = 6371.2 # mean Earth radius

    for itime in range(mytime_beg, mytime_end):
        g10 = ghlm[itime, 0]
        g11 = ghlm[itime, 1]
        h11 = ghlm[itime, 2]
        g20 = ghlm[itime, 3]
        g21 = ghlm[itime, 4]
        h21 = ghlm[itime, 5]
        g22 = ghlm[itime, 6]
        h22 = ghlm[itime, 7]
        # Gallet et al. EPSL 2009 (appendix)
        L0 =  2. * g10 * g20 + np.sqrt(3.) * ( g11 * g21 + h11 * h21             )
        L1 = -1. * g11 * g20 + np.sqrt(3.) * ( g10 * g21 + g11 * g22 + h11 * h22 )
        L2 = -1. * h11 * g20 + np.sqrt(3.) * ( g10 * h21 + g11 * h22 - h11 * g22 )
        m2 = g10**2 + g11**2 + h11**2
        E = ( L0 * g10 + L1 * g11 + L2 * h11  )/ ( 4. * m2 )
        xc = a * ( L1 - E * g11 ) / (3. * m2 )
        yc = a * ( L2 - E * h11 ) / (3. * m2 )
        zc[itime] = a * ( L0 - E * g10 ) / (3. * m2 )
        sc[itime] = np.sqrt( xc**2 + yc**2 )

    if size>1:
        sc = comm.allreduce( sc, op=MPI.SUM)
        zc = comm.allreduce( zc, op=MPI.SUM)

    if rank==0:
        fname = 'eccentricity'
        np.savez(outdir+'/'+fname, time=time, s_ecc=sc, z_ecc=zc, time_unit = time_unit)
        fname = fname + '.npz'
        config.set('Diags', 'eccentricity_file', fname)
        lfile = open(config_file, 'w')
        config.write(lfile)
        lfile.close()

    if rank==0:
        #distibute plot over 3 rows
        nrows = 3
        ndat = len(time)
        nstep = int( ndat / nrows )
        yticks = np.linspace( -90, 90, 5, dtype=float)
        fig, ax = plt.subplots( nrows, 1, figsize=(15,6), sharey=True)
        for i in range(nrows):
            istart = i * nstep
            if i < nrows -1:
                iend = istart + nstep
            else:
                iend = ndat-1
            ax[i].plot( time[istart:iend], sc[istart:iend], label='s_c')
            ax[i].plot( time[istart:iend], zc[istart:iend], label='z_c')
            ax[i].set_xlim(time[istart],time[iend])
            ax[i].set_ylabel('km')
            ax[i].legend(loc='best')
        ax[nrows-1].set_xlabel('time in ' + time_unit)
        plt.savefig(outdir+'/eccentricity.pdf')
        plt.savefig(outdir+'/eccentricity.png')
        plt.close()

    return time, sc, zc, time_unit

def get_rms_intensity( comm, size, rank, config_file):

    if rank == 0:
        print()
        print('  Analysis of geomagnetic intensity ')
        print()

    config_file = config_file
    config = configparser.ConfigParser(interpolation=None)
    config.read(config_file)
    Verbose = config['Common'].getboolean('Verbose')
    fname_gauss = config['Gauss coefficients']['filename']
    gauss_unit = config['Gauss coefficients']['unit']
    outdir = config['Common']['output_directory']
    ltrunc_gauss = int(config['Gauss coefficients']['ltrunc'])
    tag = config['Common']['tag']
    time_unit = config['Rescaling factors and units']['time unit']
    ltrunc_SHB = int(config['Design matrices on regular grid']['l_trunc'])
    nlat = int(config['Design matrices on regular grid']['nlat'])
    nlon = int(config['Design matrices on regular grid']['nlon'])
    fname_SHB = config['Design matrices on regular grid']['filename']

    npzfile =  np.load(outdir+'/'+fname_gauss)
    time = npzfile['time']
    ghlm = npzfile['ghlm']

    npzfile_SHB = np.load(outdir+'/'+fname_SHB)
    SHBX = npzfile_SHB['SHBX']
    SHBY = npzfile_SHB['SHBY']
    SHBZ = npzfile_SHB['SHBZ']
    sh = shtns.sht(ltrunc_SHB, ltrunc_SHB, norm=shtns.sht_fourpi | shtns.SHT_NO_CS_PHASE | shtns.SHT_REAL_NORM)
    sh.set_grid(nlat=nlat, nphi=nlon)
    theta = np.arccos( sh.cos_theta )
    phi = np.arange(0,sh.nphi) * 2 * np.pi / sh.nphi
    nsh = ltrunc_SHB * ( ltrunc_SHB + 2 )

    ntime = int ( len(time) ) # / 5 )
    ntime_per_process = int(ntime / size)
    mytime_beg = rank * ntime_per_process
    mytime_end = mytime_beg + ntime_per_process
    F_rms = np.zeros(ntime, dtype=float)
    if (rank == size-1):
        mytime_end = ntime
    if size > 1:
        ier = comm.Barrier()
    if Verbose is True:
        if rank == 0:
            print('    1D domain decomposition for processing:', flush=True)
    if Verbose is True:
       print('        beg end ', mytime_beg, mytime_end, ' for process ', rank, flush=True)
    for itime in range(mytime_beg, mytime_end):
        X = np.dot(SHBX, ghlm[itime,0:nsh])
        Y = np.dot(SHBY, ghlm[itime,0:nsh])
        Z = np.dot(SHBZ, ghlm[itime,0:nsh])
        F = np.sqrt( X**2 + Y**2 + Z**2 )
        F = np.reshape( F, (nlat,nlon) )
        F_lm = sh.analys(F)
        F_rms[itime] = np.sqrt( np.sum( (np.abs(F_lm))**2 ) )

    if size > 1:
        F_rms = comm.allreduce(F_rms, op=MPI.SUM)
#
    if rank == 0:
        fname = 'F_rms'
        np.savez(outdir+'/'+fname, time=time, F_rms=F_rms, gauss_unit = gauss_unit, time_unit = time_unit)
        fname = fname + '.npz'
        config.set('Diags', 'rms_intensity_file', fname)
        lfile = open(config_file, 'w')
        config.write(lfile)
        lfile.close()

    if rank==0:
        #distibute plot over 3 rows
        nrows = 3
        ndat = len(time)
        nstep = int( ndat / nrows )
        #yticks = np.linspace( -90, 90, 5, dtype=float)
        fig, ax = plt.subplots( nrows, 1, figsize=(15,6), sharey=True)
        for i in range(nrows):
            istart = i * nstep
            if i < nrows -1:
                iend = istart + nstep
            else:
                iend = ndat-1
            ax[i].plot( time[istart:iend], F_rms[istart:iend])
            ax[i].set_xlim(time[istart],time[iend])
            #ax[i].set_yticks(yticks)
            ax[i].set_ylabel('rms Intensity in ' + gauss_unit)
        ax[nrows-1].set_xlabel('time in ' + time_unit)
        plt.savefig(outdir+'/F_rms.pdf')
        plt.savefig(outdir+'/F_rms.png')
        plt.close()

    return time, F_rms, gauss_unit, time_unit

def quadratic_disp(lat, alpha, beta):
    return alpha**2 + (beta*lat)**2

def angular_distance_2sphere( lat1, lon1, lat2, lon2, Verbose=False):

    lam1       = np.deg2rad(lat1)
    lam2       = np.deg2rad(lat2)
    dphi       = np.deg2rad(lon2 - lon1)

    if Verbose is True:
        print('delta calculation')
        print(dphi)
        print(lam1)
        print(lam2)

    delta = np.rad2deg( np.arccos( np.sin(lam1)*np.sin(lam2) + np.cos(lam1)*np.cos(lam2)*np.cos(dphi) )  )
    return delta

def compute_Delta_QPM(QPMsimu, QPMearth, Verbose=False):
    if ( np.abs(QPMsimu.a_med) > QPMearth.a_med):
        denom = (QPMearth.a_high - QPMearth.a_med) + (np.abs(QPMsimu.a_med) - np.abs(QPMsimu.a_low))
        deltaQPM_a = ( np.abs( QPMsimu.a_med) - QPMearth.a_med ) / denom
    else:
        denom = (QPMearth.a_med - QPMearth.a_low) + (np.abs(QPMsimu.a_high) - np.abs(QPMsimu.a_med))
        deltaQPM_a = ( QPMearth.a_med - np.abs(QPMsimu.a_med) ) / denom
#
    if ( np.abs(QPMsimu.b_med) > QPMearth.b_med):
        denom = (QPMearth.b_high - QPMearth.b_med) + (np.abs(QPMsimu.b_med) - np.abs(QPMsimu.b_low))
        deltaQPM_b = ( np.abs( QPMsimu.b_med) - QPMearth.b_med ) / denom
    else:
        denom = (QPMearth.b_med - QPMearth.b_low) + (np.abs(QPMsimu.b_high) - np.abs(QPMsimu.b_med))
        deltaQPM_b = ( QPMearth.b_med - np.abs(QPMsimu.b_med) ) / denom
#
    if ( np.abs(QPMsimu.delta_Inc_med) > QPMearth.delta_Inc_med):
        diff = np.abs( QPMsimu.delta_Inc_med) - QPMearth.delta_Inc_med
        denom = (QPMearth.delta_Inc_high - QPMearth.delta_Inc_med) + (np.abs(QPMsimu.delta_Inc_med) - np.abs(QPMsimu.delta_Inc_low))
        deltaQPM_delta_Inc = diff / denom
    else:
        diff = QPMearth.delta_Inc_med - np.abs( QPMsimu.delta_Inc_med)
        denom = (QPMearth.delta_Inc_med - QPMearth.delta_Inc_low) + (np.abs(QPMsimu.delta_Inc_high) - np.abs(QPMsimu.delta_Inc_med))
        deltaQPM_delta_Inc = diff / denom
#   Vpercent
    if ( QPMsimu.Vpercent_med > QPMearth.Vpercent_med):
        denom = (QPMearth.Vpercent_high - QPMearth.Vpercent_med) + (QPMsimu.Vpercent_med - QPMsimu.Vpercent_low)
    else:
        denom = (QPMearth.Vpercent_med - QPMearth.Vpercent_low) + (QPMsimu.Vpercent_high - QPMsimu.Vpercent_med)
    deltaQPM_Vpercent = np.abs( QPMsimu.Vpercent_med - QPMearth.Vpercent_med ) / denom
#   Rev
    denom = QPMearth.taut_high - QPMearth.taut_med
    deltaQPM_rev = np.abs( QPMsimu.taut - QPMearth.taut_med ) / denom

    if Verbose is True:
        print('deltaQPM_a         = %10.2f' % (deltaQPM_a) )
        print('deltaQPM_b         = %10.2f' % (deltaQPM_b) )
        print('deltaQPM_delta_Inc = %10.2f' % (deltaQPM_delta_Inc) )
        print('deltaQPM_rev       = %10.2f ' % (deltaQPM_rev) )
        print('deltaQPM_Vpercent  = %10.2f ' % (deltaQPM_Vpercent) )

    DeltaQPM = np.array([ deltaQPM_a, deltaQPM_b, deltaQPM_delta_Inc,  deltaQPM_rev,  deltaQPM_Vpercent])
    mask = ( DeltaQPM < 1. )
    if Verbose is True:
        print()
        print( ' DeltaQPM is %10.2f ' %(np.sum(DeltaQPM) ) )
        print( ' QPM is %i ' % (np.sum(mask) ) )

def compute_mean_VGP( VGP_lat, VGP_lon):
#
    l = np.cos(np.deg2rad(VGP_lat)) * np.cos(np.deg2rad(VGP_lon))
    m = np.cos(np.deg2rad(VGP_lat)) * np.sin(np.deg2rad(VGP_lon))
    n = np.sin(np.deg2rad(VGP_lat))
    r = np.sqrt( np.sum(l, axis=-1)**2 + np.sum(m, axis=-1)**2 + np.sum(n, axis=-1)**2 )
    l_site = np.sum(l, axis=-1) / r
    m_site = np.sum(m, axis=-1) / r
    n_site = np.sum(n, axis=-1) / r

    VGP_lam_avg = np.arcsin(n_site)
    VGP_phi_avg = np.arctan2(m_site,l_site)
    VGP_lat_avg = np.rad2deg(VGP_lam_avg)
    VGP_lon_avg = np.mod(np.rad2deg(VGP_phi_avg), 360.)

    return VGP_lat_avg, VGP_lon_avg

def compute_S( VGP_lat, VGP_lon, VGP_lat_avg, VGP_lon_avg):
#
    delta = angular_distance_2sphere( VGP_lat, VGP_lon, \
                                      np.transpose(np.tile(VGP_lat_avg, (np.shape(VGP_lat)[-1],1))), \
                                      np.transpose(np.tile(VGP_lon_avg, (np.shape(VGP_lon)[-1],1))), Verbose=False)

    ASD = np.sqrt(np.sum(delta**2,axis=-1)/(np.shape(delta)[-1]-1))
    return ASD

def compute_Svd( VGP_lat, VGP_lon, VGP_lat_avg, VGP_lon_avg):
#   For future vectorized version
    """
#
    delta = angular_distance_2sphere( VGP_lat, VGP_lon, \
                                      np.transpose(np.tile(VGP_lat_avg, (np.shape(VGP_lat)[-1],1))), \
                                      np.transpose(np.tile(VGP_lon_avg, (np.shape(VGP_lon)[-1],1))), Verbose=False)

    ASD = np.sqrt(np.sum(delta**2,axis=-1)/(np.shape(delta)[-1]-1))
    A = (1.8 * ASD + 5.)
    delta_max = np.max(delta, axis=-1)
    Ndat = np.ones( len(delta_max), dtype=int)
    for i in range(len(delta_max)):
        VGP_lat_loc = VGP_lat[i,:]
        VGP_lon_loc = VGP_lon[i,:]
        Aloc = A[i]
        ASD_loc = ASD[i]
        dmax_loc = delta_max[i]
        delta_loc = delta[i,:]
        Ndat_loc = Ndat[i]
        while dmax_loc > Aloc:
            mask_delta = ( delta_loc < dmax_loc )
            VGP_lat_loc = VGP_lat_loc[ mask_delta ]
            VGP_lon_loc = VGP_lon_loc[ mask_delta ]
            VGP_lat_avg_loc, VGP_lon_avg_loc = compute_mean_VGP( VGP_lat_loc, VGP_lon_loc)
            delta_loc = angular_distance_2sphere( VGP_lat_loc, VGP_lon_loc, \
                                      VGP_lat_avg_loc * np.ones( (len(VGP_lat_loc)), dtype=float), \
                                      VGP_lon_avg_loc * np.ones( (len(VGP_lon_loc)), dtype=float), Verbose=False)
            ASD_loc = np.sqrt( np.sum(delta_loc**2)/(np.shape(delta_loc)[0]-1) )
            Aloc = 1.8 * ASD_loc + 5.
            dmax_loc = np.max(delta_loc)
            Ndat_loc = len(delta_loc)
        ASD[i] = ASD_loc
        A[i] = Aloc
        Ndat[i] = Ndat_loc
    """

    VGP_lat_loc = VGP_lat
    VGP_lon_loc = VGP_lon
    VGP_lat_avg_loc, VGP_lon_avg_loc = compute_mean_VGP( VGP_lat_loc, VGP_lon_loc)
    delta_loc = angular_distance_2sphere( VGP_lat_loc, VGP_lon_loc, \
                                  VGP_lat_avg_loc * np.ones( (len(VGP_lat_loc)), dtype=float), \
                                  VGP_lon_avg_loc * np.ones( (len(VGP_lon_loc)), dtype=float), Verbose=False)
    ASD_loc = np.sqrt( np.sum(delta_loc**2)/(np.shape(delta_loc)[0]-1) )
    Aloc = 1.8 * ASD_loc + 5.
    dmax_loc = np.max(delta_loc)
    Ndat_loc = len(delta_loc)
    while dmax_loc > Aloc:
        mask_delta = ( delta_loc < dmax_loc )
        VGP_lat_loc = VGP_lat_loc[ mask_delta ]
        VGP_lon_loc = VGP_lon_loc[ mask_delta ]
        VGP_lat_avg_loc, VGP_lon_avg_loc = compute_mean_VGP( VGP_lat_loc, VGP_lon_loc)
        delta_loc = angular_distance_2sphere( VGP_lat_loc, VGP_lon_loc, \
                                  VGP_lat_avg_loc * np.ones( (len(VGP_lat_loc)), dtype=float), \
                                  VGP_lon_avg_loc * np.ones( (len(VGP_lon_loc)), dtype=float), Verbose=False)
        ASD_loc = np.sqrt( np.sum(delta_loc**2)/(np.shape(delta_loc)[0]-1) )
        Aloc = 1.8 * ASD_loc + 5.
        dmax_loc = np.max(delta_loc)
        Ndat_loc = len(delta_loc)
    ASD = ASD_loc
    A = Aloc
    Ndat = Ndat_loc

    return ASD,  A, Ndat

def compute_QPM(comm, size, rank, config_file):
    import psv10_class
    import random
    from scipy.optimize import curve_fit
    if rank == 0:
        print()
        print('  Calculation of QPM (Sprain et al. EPSL 2019) ')
        print()

	# paleomag reference
    QPMearth = psv10_class.QPM(type='QPM_std', acro='earth')
    Filename='Sprain_etal_EPSL_2019_1-s2.0-S0012821X19304509-mmc7.txt'
    datPSV10 = psv10_class.DataTable(Filename, type='PSV10')
    lmax = datPSV10.l_trunc
    nloc = len(datPSV10.location)

    # this dynamo simulation
    config_file = config_file
    config = configparser.ConfigParser(interpolation=None)
    config.read(config_file)
    tag = config['Common']['tag']
    Verbose = config['Common'].getboolean('Verbose')
    fname_gauss = config['Gauss coefficients']['filename']
    outdir = config['Common']['output_directory']
    gauss_unit = config['Gauss coefficients']['unit']
    time_unit = config['Rescaling factors and units']['time unit']
    ltrunc_gauss = int(config['Gauss coefficients']['ltrunc'])

    QPMsimu = psv10_class.QPM(type='QPM_std', acro=tag)
    npzfile = np.load(outdir+'/'+fname_gauss)
    t = npzfile['time']
    ghlm_arr = npzfile['ghlm'][:,0:lmax*(lmax+2)]
    nsamp = len(t)
    simulation_time = t[-1] - t[0]
    time_intervals = np.ediff1d(t, to_end=[0.])
    if rank == 0:
        print('    total number of samples = ', nsamp)
        print('    total simulation time =', simulation_time ,' ', time_unit)

    dipole_latitude = np.zeros(nsamp, dtype=float)
    for i in range(nsamp):
        g10 = ghlm_arr[ i, 0]
        g11 = ghlm_arr[ i, 1]
        h11 = ghlm_arr[ i, 2]
        dipole_latitude[i] = np.rad2deg(np.arctan2( g10 , np.sqrt(g11**2+h11**2) ))
    #
    # Rev criterion
    #
    # "normal" polarity
    mask_n = dipole_latitude > 45.
    tau_n = np.sum(time_intervals[mask_n]) / simulation_time
    # "reverse" polarity
    mask_r = dipole_latitude < -45.
    tau_r = np.sum(time_intervals[mask_r]) / simulation_time
    # excursion time
    mask_t = np.abs(dipole_latitude) < 45.
    tau_t = np.sum(time_intervals[mask_t]) / simulation_time

    QPMsimu.taut = tau_t
    QPMsimu.taur = tau_r
    QPMsimu.taun = tau_n
    if rank == 0 and Verbose is True:
        print('     tau_t = ', tau_t)

    nbins = 19
    bins = np.linspace( -90, 90, nbins)
#   number of localities
    nloc_tot = len(datPSV10.location)
    if rank == 0 and Verbose is True:
        print('    total number of localities = ', nloc_tot)
    ndraw = 10000
    inc_anom = np.zeros( (ndraw, nbins), dtype=float)
    scatter_squared = np.zeros( (ndraw, nloc_tot), dtype=float)
    Vpercent = np.zeros( (ndraw), dtype=float )
    bin_lat = np.zeros( nbins, dtype=float)
    empty_bin = np.zeros( nbins, dtype=bool)
    empty_bin[:] = True
    r_earth = 6371.2e3
    vdm_fact = 1.e7 * r_earth**3
   # mpi 1D domain decomposition
    ndraw_per_process = int(ndraw / size)
    mydraw_beg = rank * ndraw_per_process
    mydraw_end = mydraw_beg + ndraw_per_process
    if rank==size-1:
        mydraw_end = ndraw

    for idraw in range(mydraw_beg, mydraw_end):
        if np.mod(idraw+1-mydraw_beg,ndraw_per_process/10) == 0 and Verbose is True:
            if rank == 0:
                print('        rank ', rank, ' performed ', idraw+1-mydraw_beg, 'draws', flush=True)
        VDM = None
        iloc_glob = -1
        for ibin in range(len(bins)-1):
            lambda_min = bins[ibin]
            lambda_max = bins[ibin+1]
            mask =  ( datPSV10.latitude_in_deg > lambda_min ) *  ( datPSV10.latitude_in_deg < lambda_max)
            my_location = datPSV10.location[mask]
            my_nloc = len(my_location)
            if my_nloc > 0:
                empty_bin[ ibin ] = False
                my_number_of_sites =  datPSV10.number_of_sites[mask]
                my_latitude_in_deg = datPSV10.latitude_in_deg[mask]
                my_longitude_in_deg = datPSV10.longitude_in_deg[mask]
                my_SHB_X = datPSV10.SHB_X[mask,:]
                my_SHB_Y = datPSV10.SHB_Y[mask,:]
                my_SHB_Z = datPSV10.SHB_Z[mask,:]
                n_north = None
                n_east = None
                n_down = None
                bin_lat[ibin] = np.mean(my_latitude_in_deg)
                Inc_GAD = np.arctan( 2. * np.tan(np.deg2rad(np.mean(my_latitude_in_deg))))
                for iloc in range(my_nloc):
                    iloc_glob = iloc_glob + 1
                    nsite = my_number_of_sites[iloc]
                    # take nsite random samples
                    timesteps = random.sample(range(nsamp), nsite)
                    VGP_lat = []
                    VGP_lon = []
                    gh = np.transpose( np.reshape( np.repeat( -1. * np.sign( ghlm_arr[ timesteps,0]), 120, axis=0 ), (nsite,120) ) * ghlm_arr[ timesteps, :] )
                    X = np.dot(my_SHB_X[iloc,:], gh)
                    Y = np.dot(my_SHB_Y[iloc,:], gh)
                    Z = np.dot(my_SHB_Z[iloc,:], gh)
                    F = np.sqrt( X**2 + Y**2 + Z**2 )
                    H = np.sqrt( X**2 + Y**2 )
                    Inc = np.arctan2( Z , H)
                    Dec = np.arctan2( Y , X)
                    g10 = gh[0]
                    g11 = gh[1]
                    h11 = gh[2]
                    theta_mag = np.arctan2( np.sqrt(g11**2+h11**2) , g10 )
                    if VDM is None:
                        VDM = vdm_fact * F / np.sqrt( 1. + 3.*(np.cos(theta_mag))**2 )
                    else:
                        VDM = np.concatenate( ( VDM,  vdm_fact * F / np.sqrt( 1. + 3.*(np.cos(theta_mag))**2  ) ), axis=None)
                    p = np.arctan2(2.* H, Z )
                    VGP_lam = np.arcsin( np.sin(np.deg2rad(my_latitude_in_deg[iloc])) * np.cos(p)\
					                    + np.cos(np.deg2rad(my_latitude_in_deg[iloc]))*np.sin(p)*np.cos(Dec) )
                    beta = np.rad2deg( np.arcsin( np.sin(p) * np.sin(Dec) / np.cos(VGP_lam) ) )
                    VGP_phi = np.zeros(nsite, dtype=float)
                    for istep in range(nsite):
                        if ( np.cos(p[istep]) > ( np.sin(np.deg2rad(my_latitude_in_deg[iloc])) * np.sin(VGP_lam[istep]) ) ):
                            VGP_phi[istep] = my_longitude_in_deg[iloc] + beta[istep]
                        else:
                            VGP_phi[istep] = my_longitude_in_deg[iloc] + 180. - beta[istep]
                        if ( VGP_lam[istep] < 0. ):
                            VGP_lam[istep] = -1. * VGP_lam[istep]
                            VGP_phi[istep] = VGP_phi[istep] + 180.
                    VGP_lat = np.rad2deg(VGP_lam)
                    VGP_lon = np.mod(VGP_phi, 360.)
                    VGP_lat_avg, VGP_lon_avg = compute_mean_VGP( VGP_lat, VGP_lon)
                    if n_north is None:
                        n_north =  X/F
                        n_east =  Y/F
                        n_down =  Z/F
                    else:
                        n_north = np.concatenate( (n_north, X/F), axis=None)
                        n_east = np.concatenate( (n_east, Y/F), axis=None)
                        n_down = np.concatenate( (n_down, Z/F), axis=None)
                    """
                    delta = 90. - VGP_lat
                    ASD = np.sqrt(np.sum(delta**2)/(np.shape(delta)[0]-1))
                    A = 1.8 * ASD + 5.
                    delta_max = np.max(delta)
                    while delta_max > A:
                        mask_delta = delta < delta_max * np.ones_like(delta)
                        delta = delta[ mask_delta ]
                        ASD = np.sqrt(np.sum(delta**2)/(np.shape(delta)[0]-1))
                        A = 1.8 * ASD + 5.
                        delta_max = np.max(delta)
                    scatter_squared[idraw, iloc_glob] = np.sum(delta**2)/(np.shape(delta)[0]-1)
				# Nuts and bolts of paleomagnetism, Cox & Hart, page 310
                    """
                    Svd, cutoff, Ndat = compute_Svd( VGP_lat, VGP_lon, VGP_lat_avg, VGP_lon_avg)
                    scatter_squared[idraw, iloc_glob] = Svd**2
                r_north = np.sum(n_north)
                r_east = np.sum(n_east)
                r_down = np.sum(n_down)
                inc_avg = np.arctan2( r_down , np.sqrt( r_north**2 + r_east**2 ) )
                inc_anom[idraw, ibin] = np.rad2deg(inc_avg - Inc_GAD)
        Vmed = np.median(VDM, axis=None)
        VDM75, VDM25 = np.percentile(VDM, [75 ,25])
        Viqr = VDM75 - VDM25
        Vpercent[idraw] = Viqr / Vmed

    a = np.zeros( ndraw, dtype=float)
    b = np.zeros( ndraw, dtype=float)
    for idraw in range(mydraw_beg, mydraw_end):
        my_scatter_squared = scatter_squared[idraw,:]
        mask_test = np.isfinite(my_scatter_squared)
        my_scatter_squared = my_scatter_squared[mask_test]
        my_latitude_in_deg = datPSV10.latitude_in_deg[mask_test]
        # popt, pcov = curve_fit( quadratic_disp, np.abs(my_latitude_in_deg), my_scatter_squared, check_finite=True, p0=[25.,0.5], method='dogbox', bounds=([0.,0.],[100.,1.]))
        popt, pcov = curve_fit( quadratic_disp, np.abs(my_latitude_in_deg), my_scatter_squared, check_finite=True, p0=[25.,0.5], bounds=([0.,0.],[100.,2.]))
        a[idraw] = np.abs(popt[0])
        b[idraw] = np.abs(popt[1])
	#
	# Global gather if draw done in parallel
    # Vpercent
    if size>1:
        Vpercent = comm.allreduce(Vpercent, op=MPI.SUM)
        a = comm.allreduce(a, op=MPI.SUM)
        b = comm.allreduce(b, op=MPI.SUM)
# dbg
        scatter_squared = comm.allreduce(scatter_squared, op=MPI.SUM)
#       scatter_squared = np.reshape(scatter_squared, (ndraw,nloc_tot))
#
        for ibin in range(len(bins)-1):
            inc_anom[:,ibin] = comm.allreduce(inc_anom[:,ibin], op=MPI.SUM)
	# inspection of arrays
    Vpercent = Vpercent[np.isfinite(Vpercent)]
    a = a[np.isfinite(a)]
    b = b[np.isfinite(b)]
# dbg
#   scatter_squared = scatter_squared[np.isfinite(scatter_squared)]
#   mean_Ssq = np.mean(scatter_squared, axis=0)
#   if rank == 0:
#        print(np.shape(mean_Ssq), flush=True)
#        print(np.shape(scatter_squared), flush=True)
#        Sfile = open('S_vd_mean.dat','w+')
#        for i in range(np.size(mean_Ssq)):
#             Sfile.write( '%g %g \n' % (my_latitude_in_deg[i], mean_Ssq[i] ) )
#        Sfile.close()

    QPMsimu.Vpercent_med = np.median( Vpercent)
    QPMsimu.Vpercent_low = np.percentile( Vpercent, 2.5)
    QPMsimu.Vpercent_high = np.percentile( Vpercent, 97.5)
    if rank ==0 and Verbose is True:
        print()
        print('        Vpercent med low high  = ', QPMsimu.Vpercent_med, QPMsimu.Vpercent_low, QPMsimu.Vpercent_high)
        print()
    QPMsimu.a_med = np.median(a)
    QPMsimu.a_low = np.percentile(a, 2.5)
    QPMsimu.a_high = np.percentile(a, 97.5)
    QPMsimu.b_med = np.median(b)
    QPMsimu.b_low = np.percentile(b, 2.5)
    QPMsimu.b_high = np.percentile(b, 97.5)
    if rank ==0 and Verbose is True:
        print()
        print('        a med low high  = ', QPMsimu.a_med, QPMsimu.a_low, QPMsimu.a_high)
        print('        b med low high  = ', QPMsimu.b_med, QPMsimu.b_low, QPMsimu.b_high)
        print()

    inc_anom_median = np.zeros(nbins, dtype=float)
    inc_anom_low = np.zeros(nbins, dtype=float)
    inc_anom_high = np.zeros(nbins, dtype=float)
    for ibin in range(len(bins)-1):
        if not empty_bin[ibin]:
            inc_anom_median[ibin] = np.median(     inc_anom[:,ibin])
            if inc_anom_median[ibin] > 0:
                inc_anom_low[ibin]    = np.percentile( inc_anom[:,ibin], 2.5)
                inc_anom_high[ibin]   = np.percentile( inc_anom[:, ibin], 97.5)
            else:
                inc_anom_low[ibin]    = np.percentile( inc_anom[:,ibin], 97.5)
                inc_anom_high[ibin]   = np.percentile( inc_anom[:, ibin], 2.5)
            if rank ==0 and Verbose is True:
                print( "        %12.3f %12.3f %12.3f %12.3f " % (bin_lat[ibin], inc_anom_median[ibin], inc_anom_low[ibin], inc_anom_high[ibin]) )
    inc_ind_max = np.argmax(np.abs(inc_anom_median))
    QPMsimu.delta_Inc_med = inc_anom_median[inc_ind_max]
    QPMsimu.delta_Inc_low = inc_anom_low[inc_ind_max]
    QPMsimu.delta_Inc_high = inc_anom_high[inc_ind_max]
    if rank == 0 and Verbose is True:
        print()
        print('QPMsimu.delta_Inc_med = %10.2f QPMsimu.delta_Inc_low = %10.2f  QPMsimu.delta_Inc_high = %10.2f '\
        % (QPMsimu.delta_Inc_med, QPMsimu.delta_Inc_low, QPMsimu.delta_Inc_high))
	# to complete

    Verbose = False
    if rank == 0:
       Verbose = True
       compute_Delta_QPM(QPMsimu, QPMearth, Verbose=Verbose)

    QPM_results = []
    return QPM_results
#


import argparse
from .outliner import Outliner

def parser_arguments():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--file_path",
        "-fp",
        help="Path to the file you want to trace",
        required=True,
        nargs=1,
    )
    parser.add_argument(
        "--object_invoke",
        "-o",
        help="The invoking statement of the object",
        type=str,
        required=True,
        nargs=1,
    )
    parser.add_argument(
        "--mode",
        "-m",
        help="Type of display you want (detailed_data or tree)",
        choices=["tree", "detailed_data"],
        default="tree",
        nargs=1,
    )

    return parser.parse_args()

def main():
    args = parser_arguments()

    instance_class = Outliner(
        args.file_path,
        args.object_invoke,
        args.mode,
    )

    instance_class.run()


import json
import logging
import os

from uuidworldconverter.utils import logger

class Modify:

    def __init__(self, config, player_map):
        self.__config = config
        self.__uuid_map = player_map

    def modify_json(self, file):
        # if file_name change it's deactivated via config, it exits
        if not file["enable"]:
            return print(f'[{logger.INFO}] [{file["name"]}] Skipped, {file["name"]}: {file["enable"]}')
        file_path = os.path.join(os.getcwd(), self.__config["server_directory"], file["name"])
        # if file doesn't exist, returns with error print
        if not os.path.isfile(file_path):
            return print(f'[{logger.WARNING}] [{file["name"]}] File not found in directory: {file_path}')
        # open, loads & closes file_name in read mode
        read_file = open(file_path, 'r')
        file_json = json.load(read_file)
        read_file.close()
        # for each in file_json
        print(f'[{logger.INFO}] [{file["name"]}] Starting changing file: {file_path}')
        file_change = False
        for player in file_json:
            if player["uuid"] in self.__uuid_map:
                print(
                    f'[{logger.INFO}] [{file["name"]}] {self.__uuid_map[player["uuid"]][1]} : {player["uuid"]} -> {self.__uuid_map[player["uuid"]][0]}')
                player["uuid"] = self.__uuid_map[player["uuid"]][0]
                file_change = True
        # if something was changed
        if file_change:
            try:
                # opens file_name in write mode
                write_file = open(file_path, 'w')
                # save changes into the file in the disk, then closes it
                json.dump(file_json, write_file, indent=4)
                print(f'[{logger.INFO}] [{file["name"]}] Successfully written json to file: {file_path}')
                write_file.close()
            except Exception as e:
                print(f'[{logger.ERROR}] [{file["name"]}] Could not dump json: {file_path}')
                logging.exception(e)
        else:
            print(f'[{logger.WARNING}] [{file["name"]}] Nothing changed so file stays same: {file_path}')

    def modify_folder(self, folder):
        # if folder change it's deactivated via config it exits
        if not folder["enable"]:
            return print(f'[{logger.INFO}] [{folder["name"]}] Skipped, {folder["name"]}: {folder["enable"]}')
        folder_path = os.path.join(os.getcwd(), self.__config["server_directory"], self.__config["world_directory"],
                                   folder["name"])
        # if folder doesn't exist, returns with error print
        if not os.path.isdir(folder_path):
            return print(f'[{logger.ERROR}] [{folder["name"]}] Directory not found: {folder_path}')
        # for each in folder_path
        print(f'[{logger.INFO}] [{folder["name"]}] Starting changing folder files: {folder_path}')
        file_change = False
        for file in os.listdir(folder_path):
            file_split = str(file).split(".")
            file_uuid = file_split[0]
            if file_uuid in self.__uuid_map:
                old_name = os.path.abspath(os.path.join(folder_path, file))
                new_name = os.path.abspath(
                    os.path.join(folder_path, f'{self.__uuid_map[file_uuid][0]}.{file_split[1]}'))
                try:
                    # changes file_name
                    os.rename(old_name, new_name)
                    file_change = True
                    print(f'[{logger.INFO}] [{folder["name"]}] {self.__uuid_map[file_uuid][1]} : {file} -> {self.__uuid_map[file_uuid][0]}.{file_split[1]}')
                    print(f'[{logger.INFO}] [{folder["name"]}] oldpath: {old_name}')
                    print(f'[{logger.INFO}] [{folder["name"]}] newpath: {new_name}')
                except Exception as e:
                    logging.exception(e)
                    print(f'[{logger.ERROR}] [{folder["name"]}] Could not rename file: {file}')
        if not file_change:
            print(f'[{logger.WARNING}] [{folder["name"]}] Nothing changed so folder stays same: {folder_path}')


import os
ABSPATH = os.path.abspath('.')

from flask import Flask, redirect, url_for, request, render_template
app = Flask(__name__, template_folder=ABSPATH+'/html')

@app.route('/success/<name>')
def success(name):
    return 'Welcome %s' % name.title()

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        user = request.form['nm']
        return redirect(url_for('success', name=user))
    else:
        user = request.args.get('nm')
        return redirect(url_for('success',name=user))

#@app.route('/loginbypass/<user>')
#def loginbypass(user):
#    return redirect(url_for('success',name=user))

@app.route('/')
def user_page():
    return render_template('flask_3.html')

if __name__ == '__main__':
    app.run(debug = True)


import pandas as pd
from sklearn.metrics import confusion_matrix
from sklearn.cross_validation import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score

# Function to perform training with giniIndex.
def train_using_gini(X_train, X_test, y_train):

    # Creating the classifier object
    clf_gini = DecisionTreeClassifier(criterion = "gini",
            random_state = 100,max_depth=3, min_samples_leaf=5)

    # Performing training
    clf_gini.fit(X_train, y_train)
    return clf_gini

# Function to perform training with entropy.
def tarin_using_entropy(X_train, X_test, y_train):

    # Decision tree with entropy
    clf_entropy = DecisionTreeClassifier(
            criterion = "entropy", random_state = 100,
            max_depth = 3, min_samples_leaf = 5)

    # Performing training
    clf_entropy.fit(X_train, y_train)
    return clf_entropy

# Function to make predictions
def prediction(X_test, clf_object):

    # Predicton on test with giniIndex
    y_pred = clf_object.predict(X_test)
    #print("Predicted values:")
    #print(y_pred)
    return y_pred

# Function to calculate accuracy
def cal_accuracy(y_test, y_pred):

    print("Confusion Matrix: ",
        confusion_matrix(y_test, y_pred))

    print ("Accuracy : ",
    accuracy_score(y_test,y_pred)*100)

    #print("Report : ",
    #classification_report(y_test, y_pred))

# Driver code
def main():

    # Building Phase
    names=['sepal.length','sepal.width','petal.length','petal.width']
    dataset=pd.read_csv("iris.csv").values
    x=dataset[:,:4]
    y=dataset[:,4]
    x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.3)
    clf_gini = train_using_gini(x_train, x_test, y_train)
    clf_entropy = tarin_using_entropy(x_train, x_test, y_train)

    # Operational Phase
    print("Results Using Gini Index:")

    # Prediction using gini
    y_pred_gini = prediction(x_test, clf_gini)
    cal_accuracy(y_test, y_pred_gini)

    print("Results Using Entropy:")
    # Prediction using entropy
    y_pred_entropy = prediction(x_test, clf_entropy)
    cal_accuracy(y_test, y_pred_entropy)

    from sklearn.externals.six import StringIO
    from sklearn.tree import export_graphviz
    import pydotplus

    dot_data = StringIO()

    export_graphviz(clf_entropy, out_file=dot_data,feature_names=names,filled=True, rounded=True,special_characters=True)

    graph = pydotplus.graph_from_dot_data(dot_data.getvalue())
    graph.write_png('analysis_result.png')

# Calling main function
if __name__=="__main__":
    main()


from flask import Flask, request, jsonify
from http import HTTPStatus
from flask_cors import CORS
from sentence_transformers import SentenceTransformer, uti
import json
import numpy as np
import pymysql

def get_db_connection():
    return pymysql.connect(host='', user='', password='', db='', charset='utf8')

app = Flask(__name__)
CORS(app)

# model = SentenceTransformer()
# corpus_embeddings = np.load()

@app.route('/model', methods=['POST', 'OPTIONS'])
def model():
    if request.method == 'POST':
        data = request.get_json()
        query = data['query']
        hits = search_bi_encoder(query)

        db = get_db_connection()
        cursor = db.cursor()

        try:
            sql = "select * from coordinates where docid=" + str(hits[0]) + ";"
            cursor.execute(sql)
            rows = cursor.fetchall()

            for row in rows:
                docid = row[0]
                x = row[1]
                y = row[2]

                if docid == hits[0]:
                    result = {"x" : x, "y" : y, "status":HTTPStatus.OK}
        finally:
            cursor.close()
            db.close()
            return jsonify(result)
    return '', 204

def search_bi_encoder(query):
    model = SentenceTransformer()
    corpus_embeddings = np.load()
    question_embedding = model.encode(query, convert_to_tensor=True)
#    question_embedding = question_embedding.cuda()
    hits = util.semantic_search(question_embedding, corpus_embeddings, top_k=5)
    hits = hits[0]
    hits = sorted(hits, key=lambda x: x['score'], reverse=True)
    hits = [hit['corpus_id'] for hit in hits[:5]]

    return hits

if __name__ == '__main__':
    app.run('0.0.0.0', port=5000, debug=True)


from django.urls import include, path
from rest_framework import routers
from .views import *

router = routers.DefaultRouter()
router.register(r'users', CustomUserViewSet)
router.register(r'events', EventViewSet)
router.register(r'task-states', TaskStateViewSet)
router.register(r'event-tasks', EventTaskViewSet)
router.register(r'event-users', EventUserViewSet)
router.register(r'event-task-reports', EventTaskReportViewSet)

urlpatterns = [
    path('', include(router.urls)),
    path('token/', CustomTokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('tg-token/', TokenTgView.as_view(), name='tg_token'),
    path('update-tg-id/', UpdateTgIdView.as_view(), name='update-tg-id'),
]


import time
import requests
import webbrowser

def count_down(m, url):
    '''Count down to open a website'''
    for m in range(m, 0, -1):
        minute = m - 1
        if minute < 10:
            minute = '0' + str(minute)
        for s in range(59, 0, -1):
            if s < 10:
                s = '0' + str(s)
            print(f'{minute}:{s}', end=' \r')
            time.sleep(1)

    webbrowser.open(url)

def main():

    while True:
        m = input('请输入倒计时的分钟数: ')
        if m == '' or m.count(' ') >= 1:
            continue
        try:
            m = int(m)
        except Exception:
            print('请输入分钟数(正整数)')
            continue
        break
    while True:
        url = input('请输入到计时结束后要开启的网站: ')
        if url == '' or url.count(' ') >= 1:
            continue
        if url[:7] != 'http://' or url[:8] != 'https://':
            url = 'http://' + url
        try:
            requests.get(url)
        except Exception:
            print('您输入的url无法访问，请在输入有效的url!')
            continue
        break

    count_down(m, url)

if __name__ == '__main__':
    main()


import numpy as np
import opfactory
#from cy.sparsemat import matvec
from numba import jit,njit

@njit(fastmath=True)
def matvec(nrows,IA,JA,data,vec,outvec):
    """Sparse matrix-vector multiplication for small(ish) matrices
    """
    d_ind = 0
    for i in range(nrows):
        ncol = IA[i+1]-IA[i]
        for j in range(ncol):
            col_ind = JA[d_ind]
            outvec[i] = outvec[i] + data[d_ind]*vec[col_ind]
            d_ind += 1
    return outvec

class PBasis:
    """General class that handles creation of operators in the primitive basis
    and the application of operators on the single-particle functions. Each
    instance of this class handles the primitive basis for a single-mode and
    can handle combined modes.
    """

    def __init__(self, args, combined=False, sparse=False):
        """
        Parameters
        ----------
        args - list (of lists), arguments that describes the primitive basis
                (or primitive bases in a combined mode)
        combined - bool, whether or not a mode is a combined mode
        sparse - bool, use sparse matrix techniques or not
        """
        self.combined = combined
        if self.combined:
            self.params = list()
        self.params = {}
        self.params['basis'] = args[0].lower()
        self.sparse = sparse

        # set up parameters for basis
        if self.params['basis'] == 'ho':
            self.params['npbf']  = args[1]
            self.params['mass']  = args[2]
            self.params['omega'] = args[3]
            if len(args) == 5:
                self.combined = args[4]
            else:
                self.combined = False
            if self.combined:
                self.make_ops = opfactory.make_ho_ops_combined
                if not isinstance(self.params['mass'], list):
                    mlist = [args[2] for i in range(len(args[1]))]
                    self.params['mass'] = mlist
                if not isinstance(self.params['omega'], list):
                    omlist = [args[2] for i in range(len(args[1]))]
                    self.params['omega'] = omlist
            else:
                self.make_ops = opfactory.make_ho_ops
                #self.grid = make_ho_grid(self.params['npbf'])
        elif self.params['basis'] == 'sinc':
            self.params['npbf'] = args[1]
            self.params['qmin'] = args[2]
            self.params['qmax'] = args[3]
            self.params['dq']   = args[4]
            self.params['mass'] = args[5]
            if isinstance(self.params['npbf'], list):
                self.make_ops = opfactory.make_sinc_ops_combined
            else:
                self.make_ops = opfactory.make_sinc_ops
            self.grid = np.arange(qmin,qmax+dq,dq)
        elif self.params['basis'] == 'plane wave':
            if args[1]%2 == 0:
                self.params['npbf'] = args[1]+1
            else:
                self.params['npbf'] = args[1]
            self.params['nm']   = int((args[1]-1)/2)
            self.params['mass'] = args[2]
            if len(args) == 4:
                self.combined = args[3]
            else:
                self.combined = False
            if self.combined:
                raise NotImplementedError
            else:
                self.make_ops = opfactory.make_planewave_ops
        elif self.params['basis'] == 'plane wave dvr':
            raise NotImplementedError
            #if args[1]%2 == 0:
            #    self.params['npbf'] = args[1]+1
            #else:
            #    self.params['npbf'] = args[1]
            #self.params['nm']   = int((args[1]-1)/2)
            #self.params['mass'] = args[2]
            #if len(args) == 4:
            #    self.combined = args[3]
            #else:
            #    self.combined = False
            #if self.combined:
            #    raise NotImplementedError
            #else:
            #    self.make_ops = opfactory.make_planewave_ops
            #    #self.grid = np.arange(qmin,qmax+dq,dq)
        elif self.params['basis'] == 'radial':
            raise NotImplementedError
            #self.params['npbf'] = args[1]
            #self.params['dq']   = args[2]
            #self.params['mass'] = args[3]
        else:
            raise ValueError("Not a valid basis.")

    def make_operators(self, ops, matrix=None):
        """Creates matrices for all the relevant operators used in the
        calculation. These matrices are then stored in a dictionary called
        self.ops.

        Input
        -----
        ops - list of strings, all the operators that are used for this pbf
        """
        try:
            self.ops
        except:
            self.ops = {}
        if matrix is None:
            matrix = [None for i in range(len(ops))]
        for i,op in enumerate(ops):
            if not op in self.ops:
                if matrix[i] is None:
                    self.ops[op] = self.make_ops(self.params,op,sparse=self.sparse)
                else:
                    self.ops[op] = matrix[i]
            ## TODO make this for custom operators
            #if isinstance(op,str):
            #    self.ops[op] = self.make_ops(params,op)
            #else:
            #    ind = 'c%d'%(count)
            #    count += 1
            #    self.ops[op] = op.copy()

    def operate(self, spf, term):
        """Operate a single-body term on a single spf.
        """
        #return self.ops[term]@spf
        if self.sparse:
            op = self.ops[term]
            outvec = np.zeros(op.nrows, dtype=complex)
            return matvec(op.nrows,op.IA,op.JA,op.data,spf,outvec)
            #return matvec(self.ops[term], spf)
        else:
            return np.dot(self.ops[term], spf)

if __name__ == "__main__":

    # no mode combination
    pbf = PBasis(['ho',22,1.0,1.0])
    pbf.make_operators(['q','KE','q^2'])
    print(pbf.params['basis'])
    print(pbf.params['npbf'])
    print(pbf.params['mass'])
    print(pbf.params['omega'])
    opkeys = pbf.ops.keys()
    for op in opkeys:
        print(op)
        print(pbf.ops[op].shape)
    print('')
    print('')

    # mode combination
    pbf = PBasis(['ho',[6,6],1.0,1.0,True])
    pbf.make_operators(['(q)*(1)','(1)*(q)'])
    print(pbf.params['basis'])
    print(pbf.params['npbf'])
    print(pbf.params['mass'])
    print(pbf.params['omega'])
    opkeys = pbf.ops.keys()
    for op in opkeys:
        print(op)
        print(pbf.ops[op].shape)
    print('')
    print('')

    # mode combination
    pbf = PBasis(['ho',[6,6],[1.0,2.0],[1.0,2.0],True])
    pbf.make_operators(['(q)*(1)','(1)*(q)'])
    print(pbf.params['basis'])
    print(pbf.params['npbf'])
    print(pbf.params['mass'])
    print(pbf.params['omega'])
    opkeys = pbf.ops.keys()
    for op in opkeys:
        print(op)
        print(pbf.ops[op].shape)
    print('')
    print('')


import sys
from itertools import combinations

def solution(nums):
    answer = 0
    for x in combinations(nums,3):
        chPrime = x[0]+x[1]+x[2]
        for i in range(2,chPrime):
            if(chPrime%i==0): break
        else : answer+=1
    return answer

프로그래머스/lv1/12977. 소수 만들기/소수 만들기.py


from turtle import color
import torch
import numpy as np
import matplotlib.pyplot as plt
from tqdm import tqdm
import seaborn as sns
import pandas as pd
from torch.utils.data import Dataset, DataLoader

from data import ArielDataset
from data import DATA_DIR

def load_all_data(dataset):
  dataset_values = {}
  for key in dataset[0]['infos'].keys():
    dataset_values[key] = []

  dataset_values['data'] = []
  dataset_values['labels'] = []

  for data in tqdm(dataset):
    for key in data['infos'].keys():
      dataset_values[key].append(data['infos'][key])
    dataset_values['data'].append(data['data'].cpu().detach().numpy())
    dataset_values['labels'].append(data['labels'].cpu().detach().numpy())
  for key, values in dataset_values.items():
    dataset_values[key] = np.array(values)

  return dataset_values

def plot_dataset_infos(data):

  fig, axs = plt.subplots(1, 8)
  fig.suptitle('Ariel Dataset Analysis : Test set')

  keys = list(data.keys())
  keys = ['star_temp', 'star_logg', 'star_rad', 'star_mass', 'star_k_mag', 'period', 'sma', 'incl']
  # keys.remove('file_name')

  for i, key in enumerate(keys):
    bar = axs[i].bar(
      key,
      data[key].mean(),
      yerr=data[key].std(),
      align='center', alpha=0.5, ecolor='black', capsize=10
  )
    for idx, rect in enumerate(bar):
      height = rect.get_height()
      axs[i].text(rect.get_x() + rect.get_width()/2., 0.25*height,
              "Mean: {:.1f}\nMin: {:.1f}\nMax: {:.1f}\nStd: {:.1f}".format(data[key].mean(), data[key].min(), data[key].max(), data[key].std()),
              ha='center', va='bottom')

  # fig.tight_layout(pad=0.01)
  plt.show()

def plot_labels_distrib(data):
  mean = np.mean(data['labels'], axis=0)
  std = np.std(data['labels'], axis=0)
  arr = np.arange(55)
  print(mean.shape, std.shape)
  fig, ax = plt.subplots()
  bar = plt.bar(
    arr,
    mean,
    yerr=std,
    align='center', alpha=0.5, ecolor='black', capsize=10
  )
  for idx, rect in enumerate(bar):
    height = rect.get_height()
    ax.text(rect.get_x() + rect.get_width()/2., 0.1*height,
            "Min: {:.2f} Max: {:.2f}".format(data['labels'][:,idx].min(), data['labels'][:,idx].max()),
            ha='center', va='bottom', rotation=90)

  plt.xlabel('wavelength')
  plt.ylabel('mean value')
  plt.show()

def plot_light_curves(data, y):
  T, W = data.shape
  data_df = pd.DataFrame(data=data)
  y_df = pd.DataFrame(data=np.tile(y, (T, 1)))
  mean_data_df = data_df.mean(axis=1)
  std_data_df = data_df.std(axis=1)
  low = mean_data_df - std_data_df
  high = mean_data_df + std_data_df
  plt.errorbar(np.arange(T), mean_data_df, yerr=std_data_df, alpha=.5, fmt='-', linewidth=2, elinewidth=0.5, capsize=2, capthick=0.5)
  plt.fill_between(x=np.arange(T), y1=low, y2=high, alpha=.25)
  plt.ylim(low.min(), high.max())
  plt.title('Mean lightcurve. Mean Target : {:.4f}'.format(np.mean(y)))
  plt.show()

if __name__ == '__main__':
  sns.set_theme(style="whitegrid")

  print('Loading data ...')
  dataset = ArielDataset(DATA_DIR, 'train')
  dataloader = DataLoader(dataset, batch_size=1, shuffle=False, num_workers=0)
  print('Done.')
  for i_batch, sample_batched in enumerate(dataloader):
      X = sample_batched['data'].squeeze(0).permute(1, 0).cpu().detach().numpy()
      y = sample_batched['labels'].squeeze(0).cpu().detach().numpy()
      plot_light_curves(X[25:-25], y)


import functools
import pandas as pd
from torch.utils.data import DataLoader
import numpy as np
import pandas as pd
from sklearn.metrics import roc_auc_score
from sklearn.linear_model import LinearRegression
from tqdm.auto import tqdm

from context_generator.utils.misc import inf_iterator, BlackHole
from context_generator.utils.data import PaddingCollate
from context_generator.utils.transforms import get_transform
from context_generator.datasets import SkempiDataset

def per_complex_corr(df, pred_attr='ddG_pred', limit=10):
    corr_table = []
    for cplx in df['complex'].unique():
        df_cplx = df.query(f'complex == "{cplx}"')
        if len(df_cplx) < limit:
            continue
        corr_table.append({
            'complex': cplx,
            'pearson': df_cplx[['ddG', pred_attr]].corr('pearson').iloc[0,1],
            'spearman': df_cplx[['ddG', pred_attr]].corr('spearman').iloc[0,1],
        })
    corr_table = pd.DataFrame(corr_table)
    avg = corr_table[['pearson', 'spearman']].mean()
    return avg['pearson'] , avg['spearman']

class SkempiDatasetManager(object):

    def __init__(self, config, num_cvfolds, num_workers=4, logger=BlackHole()):
        super().__init__()
        self.config = config
        self.num_cvfolds = num_cvfolds
        self.train_iterators = []
        self.val_loaders = []
        self.logger = logger
        self.num_workers = num_workers
        for fold in range(num_cvfolds):
            train_iterator, val_loader = self.init_loaders(fold)
            self.train_iterators.append(train_iterator)
            self.val_loaders.append(val_loader)

    def init_loaders(self, fold):
        config = self.config
        dataset_ = functools.partial(
            SkempiDataset,
            csv_path = config.data.csv_path,
            pdb_dir = config.data.pdb_dir,
            cache_dir = config.data.cache_dir,
            num_cvfolds = self.num_cvfolds,
            cvfold_index = fold,
            transform = get_transform(config.data.transform)
        )
        train_dataset = dataset_(split='train')
        val_dataset = dataset_(split='val')

        train_cplx = set([e['complex'] for e in train_dataset.entries])
        val_cplx = set([e['complex'] for e in val_dataset.entries])
        leakage = train_cplx.intersection(val_cplx)
        assert len(leakage) == 0, f'data leakage {leakage}'

        train_loader = DataLoader(
            train_dataset,
            batch_size=config.train.batch_size,
            shuffle=True,
            collate_fn=PaddingCollate(),
            num_workers=self.num_workers
        )
        train_iterator = inf_iterator(train_loader)
        val_loader = DataLoader(
            val_dataset,
            batch_size=config.train.batch_size,
            shuffle=False,
            collate_fn=PaddingCollate(),
            num_workers=self.num_workers
        )
        self.logger.info('Fold %d: Train %d, Val %d' % (fold, len(train_dataset), len(val_dataset)))
        return train_iterator, val_loader

    def get_train_iterator(self, fold):
        return self.train_iterators[fold]

    def get_val_loader(self, fold):
        return self.val_loaders[fold]

def overall_correlations(df):
    pearson = df[['ddG', 'ddG_pred']].corr('pearson').iloc[0,1]
    spearman = df[['ddG', 'ddG_pred']].corr('spearman').iloc[0,1]
    return {
        'overall_pearson': pearson,
        'overall_spearman': spearman,
    }

def percomplex_correlations(df, return_details=False):
    corr_table = []
    for cplx in df['complex'].unique():
        df_cplx = df.query(f'complex == "{cplx}"')
        if len(df_cplx) < 10:
            continue
        corr_table.append({
            'complex': cplx,
            'pearson': df_cplx[['ddG', 'ddG_pred']].corr('pearson').iloc[0,1],
            'spearman': df_cplx[['ddG', 'ddG_pred']].corr('spearman').iloc[0,1],
        })
    corr_table = pd.DataFrame(corr_table)
    average = corr_table[['pearson', 'spearman']].mean()
    out = {
        'percomplex_pearson': average['pearson'],
        'percomplex_spearman': average['spearman'],
    }
    if return_details:
        return out, corr_table
    else:
        return out

def overall_auroc(df):
    score = roc_auc_score(
        (df['ddG'] > 0).to_numpy(),
        df['ddG_pred'].to_numpy()
    )
    return {
        'auroc': score,
    }

def overall_rmse_mae(df):
    true = df['ddG'].to_numpy()
    pred = df['ddG_pred'].to_numpy()[:, None]
    reg = LinearRegression().fit(pred, true)
    pred_corrected = reg.predict(pred)
    rmse = np.sqrt( ((true - pred_corrected) ** 2).mean() )
    mae = np.abs(true - pred_corrected).mean()
    return {
        'rmse': rmse,
        'mae': mae,
    }

def analyze_all_results(df):
    methods = df['method'].unique()
    funcs = [
        overall_correlations,
        overall_rmse_mae,
        overall_auroc,
        percomplex_correlations,
    ]
    analysis = []
    for method in tqdm(methods):
        df_this = df[df['method'] == method]
        result = {
            'method': method,
        }
        for f in funcs:
            result.update(f(df_this))
        analysis.append(result)
    analysis = pd.DataFrame(analysis)
    return analysis

def analyze_all_percomplex_correlations(df):
    methods = df['method'].unique()
    df_corr = []
    for method in tqdm(methods):
        df_this = df[df['method'] == method]
        _, df_corr_this = percomplex_correlations(df_this, return_details=True)
        df_corr_this['method'] = method
        df_corr.append(df_corr_this)
    df_corr = pd.concat(df_corr).reset_index()
    return df_corr

def eval_skempi(df_items, mode, ddg_cutoff=None):
    assert mode in ('all', 'single', 'multiple')
    if mode == 'single':
        df_items = df_items.query('num_muts == 1')
    elif mode == 'multiple':
        df_items = df_items.query('num_muts > 1')

    if ddg_cutoff is not None:
        df_items = df_items.query(f"ddG >= {-ddg_cutoff} and ddG <= {ddg_cutoff}")

    df_metrics = analyze_all_results(df_items)
    df_corr = analyze_all_percomplex_correlations(df_items)
    df_metrics['mode'] = mode
    return df_metrics

def eval_skempi_three_modes(results, ddg_cutoff=None):
    df_all = eval_skempi(results, mode='all', ddg_cutoff=ddg_cutoff)
    df_single = eval_skempi(results, mode='single', ddg_cutoff=ddg_cutoff)
    df_multiple = eval_skempi(results, mode='multiple', ddg_cutoff=ddg_cutoff)
    df_metrics = pd.concat([df_all, df_single, df_multiple], axis=0)
    df_metrics.reset_index(inplace=True, drop=True)
    return df_metrics


from flask import Flask, render_template, request, redirect, url_for
import json

app = Flask(__name__)

board = []

@app.route('/')
def index():
    return render_template('Board.html', rows = board)

@app.route('/create', methods = ['POST'])
def create():
    # 1번을 해보세요.
    name = request.form['name']
    context = request.form['context']
    board.append([name, context])
    # 2번을 해보세요.
    return json.dumps({"status":200, "result": {"id": len(board)}})

if __name__ == '__main__':
    app.run(debug=True)


#!/usr/bin/python3
from yaml import load, dump, CLoader as Loader, CDumper as Dumper
from typing import List
from os import listdir, path
from sys import argv
from pathlib import Path
import logging

logger = logging.getLogger(__name__)
handler = logging.StreamHandler()
formatter = logging.Formatter(
        '%(asctime)s %(levelname)-8s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.INFO)

def getAvailableNetworkInterfaces() -> List[str]:
    ifaces = listdir(path.join('/', 'sys', 'class', 'net'))
    return ifaces

def configureApi(interfaces: List[str]):
    '''
        Generates a suitable config for the vSTING API
            interface: list of interfaces. The interface leading to the robot must be specified first.
        Example:
            interfaces:     ['enps30', 'enp4s0']
    '''
    with open(path.join('..', 'configs', 'api-config-base.yml'), 'r') as file:
        config = load(file, Loader=Loader)

    config['interfaces']['robot'] = interfaces[0]
    config['interfaces']['operator'] = interfaces[1]
    config['combox']['cli']['working_directory'] = path.join(Path.home(), *'vsting/combox_monitor/dist/cli'.split('/'))
    # Write generated config to file
    with open(path.join(Path.home(), 'vsting', 'api','api-config.yml'), 'w') as file:
        file.write(dump(config, Dumper=Dumper, sort_keys=False))
        logger.info("updated api config.")

def configureCombox(interfaces: List[str]):
    '''
        Generates a suitable config for the combox monitor daemon
            interface: list of interfaces. The interface leading to the robot must be specified first.
        Example:
            interfaces:     ['enps30', 'enp4s0']
    '''
    # Read base yml config
    with open(path.join('..', 'configs', 'combox-config-base.yml'), 'r') as file:
        config = load(file, Loader=Loader)
    # Generate interface_tags configuration
    iface_tags = {'vsting-br': {'technology': 'Bridge'}}
    iface_tags = {
        **iface_tags,
        **{
            iface: {'technology': 'Robot' if idx == 0 else 'Operator'}
            for idx, iface in enumerate(interfaces)
        }
    }
    # Generate traffic interfaces configuration
    traffic_ifaces = interfaces
    # Set generated args in config
    config["interface_tags"] = iface_tags
    config["traffic"]["interfaces"] = traffic_ifaces
    # Write generated config to file
    with open(path.join(Path.home(), 'vsting', 'combox_monitor','combox-config-vsting.yml'), 'w') as file:
        file.write(dump(config, Dumper=Dumper, sort_keys=False))
        logger.info("updated config with interface monitoring configuration.")

def updateConfigs(interfaces: List[str]):

    logger.info('updating configuration files ...')
    if len(interfaces) != 2:
        logger.fatal(f'two network interfaces must be provided for configuration')
        exit(1)

    available_ifaces = getAvailableNetworkInterfaces()

    # check if all passed in interfaces exist.
    iface_check = [ iface in available_ifaces for iface in interfaces]
    if not all(iface_check):
        unavailable_ifaces = [interfaces[idx] for idx, check in enumerate(iface_check) if check is False]
        logger.fatal(f'{" and ".join(unavailable_ifaces)} {"are" if len(unavailable_ifaces) > 1 else "is"} not available')
        exit(1)

    # generate api config
    configureApi(interfaces)

    # generate combox config
    configureCombox(interfaces)

    logger.info('update of configuration files completed.')

if __name__ == '__main__':
    updateConfigs(argv[1:])