#############################################################################
# uti_math module: misc. mathematical utilities / functions
# v 0.04
# Authors: O.C., Maksim Rakitin, Rebecca Ann Coles
#############################################################################
from __future__ import print_function #Python 2.7 compatibility
from array import *
from math import *
from copy import *
import random
#import sys
#import os
#****************************************************************************
[docs]
def interp_1d(_x, _x_min, _x_step, _nx, _ar_f, _ord=3, _ix_per=1, _ix_ofst=0):
"""
Interpolate 1D function value tabulated on equidistant mesh, using polynomial interpolation
:param _x: argument at which function value should be calculated
:param _x_min: minimal argument value of the tabulated function
:param _x_step: step of mesh at which function is tabulated
:param _nx: number of points in mesh at which function is tabulated
:param _ar_f: tabulated function list or array
:param _ord: order of polynomial interpolation (1- linear, 2- quadratic, 3- cubic)
:param _ix_per: argument index period of function data alignment (e.g. to interpolate one component of complex data, or in one dimension of multi-dimensional data)
:param _ix_ofst: argument index offset of function data alignment
:return: function value found by polynomial interpolation
"""
if(_ord == 1):
i0 = int(trunc((_x - _x_min)/_x_step + 1.e-09))
if(i0 < 0):
i0 = 0
elif(i0 >= _nx - 1):
i0 = _nx - 2
i1 = i0 + 1
f0 = _ar_f[i0*_ix_per + _ix_ofst]
f1 = _ar_f[i1*_ix_per + _ix_ofst]
t = (_x - (_x_min + _x_step*i0))/_x_step
return f0 + (f1 - f0)*t
elif(_ord == 2):
i0 = int(round((_x - _x_min)/_x_step))
if(i0 < 1):
i0 = 1
elif(i0 >= _nx - 1):
i0 = _nx - 2
im1 = i0 - 1
i1 = i0 + 1
t = (_x - (_x_min + _x_step*i0))/_x_step
a0 = _ar_f[i0*_ix_per + _ix_ofst]
fm1 = _ar_f[im1*_ix_per + _ix_ofst]
f1 = _ar_f[i1*_ix_per + _ix_ofst]
a1 = 0.5*(f1 - fm1)
a2 = 0.5*(fm1 + f1 - 2*a0)
return a0 + t*(a1 + t*a2)
elif(_ord == 3):
i0 = int(trunc((_x - _x_min)/_x_step + 1.e-09))
if(i0 < 1):
i0 = 1
elif(i0 >= _nx - 2):
i0 = _nx - 3
im1 = i0 - 1
i1 = i0 + 1
i2 = i0 + 2
t = (_x - (_x_min + _x_step*i0))/_x_step
a0 = _ar_f[i0*_ix_per + _ix_ofst]
fm1 = _ar_f[im1*_ix_per + _ix_ofst]
f1 = _ar_f[i1*_ix_per + _ix_ofst]
f2 = _ar_f[i2*_ix_per + _ix_ofst]
a1 = -0.5*a0 + f1 - f2/6. - fm1/3.
a2 = -a0 + 0.5*(f1 + fm1)
a3 = 0.5*(a0 - f1) + (f2 - fm1)/6.
return a0 + t*(a1 + t*(a2 + t*a3))
return 0
#****************************************************************************
#def interp_1d_lin_var(_x, _ar_x, _ar_f):
[docs]
def interp_1d_var(_x, _ar_x, _ar_f, _ord=3):
"""
Interpolate linearly 1D function value tabulated on non-equidistant (irregular) mesh
:param _x: argument at which function value should be calculated
:param _ar_x: array or list of increasing argument values, at which the function is tabulated
:param _ar_f: array or list of tabulated values corresponding to arguments in _ar_x
:param _ord: order of polynomial interpolation (1- linear, 2- quadratic, 3- cubic)
:return: function value found by polynomial interpolation
"""
sErrBadArrays = 'Incorrect/incompatible lengths of argument and function value arrays.'
nx = len(_ar_x)
if(nx <= 0): raise Exception(sErrBadArrays)
nf = len(_ar_f)
if(nf <= 0): raise Exception(sErrBadArrays)
if(nx > nf): nx = nf
if(nx < 2): raise Exception(sErrBadArrays)
nx_mi_1 = nx - 1
if(_x <= _ar_x[0]): return _ar_f[0]
elif(_x >= _ar_x[nx_mi_1]): return _ar_f[nx_mi_1]
if(_ord > nx_mi_1): _ord = nx_mi_1
i0 = 0
for i in range(1, nx):
if(_x < _ar_x[i]):
i0 = i - 1
break
if(_ord == 1):
#return interp_1d(_x, _ar_x[i0], _ar_x[i0+1] - _ar_x[i0], 2, [_ar_f[i0], _ar_f[i0+1]], 1)
x0 = _ar_x[i0]
step = _ar_x[i0+1] - x0
t = (_x - x0)/step
f0 = _ar_f[i0]
return f0 + (_ar_f[i0+1] - f0)*t
elif(_ord == 2):
im1 = i0 - 1
ip1 = i0 + 1
#nm1 = nx - 1
if(ip1 < 0):
im1 = 0
i0 = 1
ip1 = 2
elif(ip1 > nx_mi_1):
im1 = nx - 3
i0 = nx - 2
ip1 = nx_mi_1
xm1 = _ar_x[im1]
x0 = _ar_x[i0]
xp1 = _ar_x[ip1]
dxm1 = abs(_x - xm1)
dx0 = abs(_x - x0)
dxp1 = abs(_x - xp1)
if(dxm1 < dx0):
if(im1 > 0):
im1 -= 1
i0 -= 1
ip1 -= 1
elif(dxp1 < dx0):
if(ip1 < nx_mi_1):
im1 += 1
i0 += 1
ip1 += 1
x0 = _ar_x[i0]
dxm1 = _ar_x[im1] - x0
dxp1 = _ar_x[ip1] - x0
fm1 = _ar_f[im1]
f0 = _ar_f[i0]
fp1 = _ar_f[ip1]
invD = 1./(dxm1* dxp1*(dxm1 - dxp1))
a = ((dxm1 - dxp1)*f0 + dxp1*fm1 - dxm1*fp1)*invD
b = (dxp1*dxp1*(f0 - fm1) + dxm1*dxm1*(fp1 - f0))*invD
dx = _x - x0
return (a*dx + b)*dx + f0
elif(_ord == 3):
im1 = i0 - 1
ip1 = i0 + 1
ip2 = i0 + 2
if(im1 < 0):
im1 = 0
i0 = 1
ip1 = 2
ip2 = 3
elif(ip2 > nx_mi_1):
im1 = nx - 4
i0 = nx - 3
ip1 = nx - 2
ip2 = nx_mi_1
x0 = _ar_x[i0]
dxm1 = _ar_x[im1] - x0
dxp1 = _ar_x[ip1] - x0
dxp2 = _ar_x[ip2] - x0
fm1 = _ar_f[im1]
f0 = _ar_f[i0]
fp1 = _ar_f[ip1]
fp2 = _ar_f[ip2]
#print(_x - x0, dxm1, dxp1, dxp2)
#print(fm1, f0, fp1, fp2)
invD = 1./(dxm1*dxp1*dxp2*(dxm1 - dxp1)*(dxm1 - dxp2)*(dxp1 - dxp2))
invD1 = 1./(dxm1*dxp1*dxp2)
dxm1e2 = dxm1*dxm1
dxm1e3 = dxm1e2*dxm1
dxp1e2 = dxp1*dxp1
dxp1e3 = dxp1e2*dxp1
dxp2e2 = dxp2*dxp2
dxp2e3 = dxp2e2*dxp2
a1 = (-dxp1e2*(dxp1 - dxp2)*dxp2e2*(f0 - fm1) + dxm1e2*(dxp2e3*(fp1 - f0) + dxp1e3*(f0 - fp2)) + dxm1e3*(dxp2e2*(f0 - fp1) + dxp1e2*(fp2 - f0)))*invD
a2 = ((dxm1 + dxp1 + dxp2)*f0)*invD1 + (dxm1*dxp2*(dxm1e2 - dxp2e2)*fp1 + dxp1e3*(dxm1*fp2 - dxp2*fm1) + dxp1*(dxp2e3*fm1 - dxm1e3*fp2))*invD
a3 = -f0*invD1 + (dxm1*dxp2*(dxp2 - dxm1)*fp1 + dxp1e2*(dxp2*fm1 - dxm1*fp2) + dxp1*(dxm1e2*fp2 - dxp2e2*fm1))*invD
dx = _x - x0
return ((a3*dx + a2)*dx + a1)*dx + f0
#****************************************************************************
[docs]
def interp_2d(_x, _y, _x_min, _x_step, _nx, _y_min, _y_step, _ny, _ar_f, _ord=3, _ix_per=1, _ix_ofst=0):
"""
Interpolate 2D function value tabulated on equidistant rectangular mesh and represented by C-aligned flat array, using polynomial interpolation
:param _x: first argument at which function value should be calculated
:param _y: second argument at which function value should be calculated
:param _x_min: minimal value of the first argument of the tabulated function
:param _x_step: step of the first argument at which function is tabulated
:param _nx: number of points vs first argument at which function is tabulated
:param _y_min: minimal value of the second argument of the tabulated function
:param _y_step: step of the second argument at which function is tabulated
:param _ny: number of points vs second argument at which function is tabulated
:param _ar_f: function tabulated on 2D mesh, aligned as "flat" C-type list or array (first argument is changing most frequently)
:param _ord: "order" of polynomial interpolation (1- bi-linear (on 4 points), 2- "bi-quadratic" (on 6 points), 3- "bi-cubic" (on 12 points))
:param _ix_per: period of first argument index of the function data alignment (e.g. to interpolate one component of complex data, or in one dimension of multi-dimensional data)
:param _ix_ofst: offset of the first argument index in function data alignment
:return: function value found by 2D polynomial interpolation
"""
if(_ord == 1): #bi-linear interpolation based on 4 points
ix0 = int(trunc((_x - _x_min)/_x_step + 1.e-09))
if(ix0 < 0):
ix0 = 0
elif(ix0 >= _nx - 1):
ix0 = _nx - 2
ix1 = ix0 + 1
tx = (_x - (_x_min + _x_step*ix0))/_x_step
iy0 = int(trunc((_y - _y_min)/_y_step + 1.e-09))
if(iy0 < 0):
iy0 = 0
elif(iy0 >= _ny - 1):
iy0 = _ny - 2
iy1 = iy0 + 1
ty = (_y - (_y_min + _y_step*iy0))/_y_step
nx_ix_per = _nx*_ix_per
iy0_nx_ix_per = iy0*nx_ix_per
iy1_nx_ix_per = iy1*nx_ix_per
ix0_ix_per_p_ix_ofst = ix0*_ix_per + _ix_ofst
ix1_ix_per_p_ix_ofst = ix1*_ix_per + _ix_ofst
a00 = _ar_f[iy0_nx_ix_per + ix0_ix_per_p_ix_ofst]
f10 = _ar_f[iy0_nx_ix_per + ix1_ix_per_p_ix_ofst]
f01 = _ar_f[iy1_nx_ix_per + ix0_ix_per_p_ix_ofst]
f11 = _ar_f[iy1_nx_ix_per + ix1_ix_per_p_ix_ofst]
a10 = f10 - a00
a01 = f01 - a00
a11 = a00 - f01 - f10 + f11
return a00 + tx*(a10 + ty*a11) + ty*a01
elif(_ord == 2): #bi-quadratic interpolation based on 6 points
ix0 = int(round((_x - _x_min)/_x_step))
if(ix0 < 1):
ix0 = 1
elif(ix0 >= _nx - 1):
ix0 = _nx - 2
ixm1 = ix0 - 1
ix1 = ix0 + 1
tx = (_x - (_x_min + _x_step*ix0))/_x_step
iy0 = int(round((_y - _y_min)/_y_step))
if(iy0 < 1):
iy0 = 1
elif(iy0 >= _ny - 1):
iy0 = _ny - 2
iym1 = iy0 - 1
iy1 = iy0 + 1
ty = (_y - (_y_min + _y_step*iy0))/_y_step
nx_ix_per = _nx*_ix_per
iym1_nx_ix_per = iym1*nx_ix_per
iy0_nx_ix_per = iy0*nx_ix_per
iy1_nx_ix_per = iy1*nx_ix_per
ixm1_ix_per_p_ix_ofst = ixm1*_ix_per + _ix_ofst
ix0_ix_per_p_ix_ofst = ix0*_ix_per + _ix_ofst
ix1_ix_per_p_ix_ofst = ix1*_ix_per + _ix_ofst
fm10 = _ar_f[iy0_nx_ix_per + ixm1_ix_per_p_ix_ofst]
a00 = _ar_f[iy0_nx_ix_per + ix0_ix_per_p_ix_ofst]
f10 = _ar_f[iy0_nx_ix_per + ix1_ix_per_p_ix_ofst]
f0m1 = _ar_f[iym1_nx_ix_per + ix0_ix_per_p_ix_ofst]
f01 = _ar_f[iy1_nx_ix_per + ix0_ix_per_p_ix_ofst]
f11 = _ar_f[iy1_nx_ix_per + ix1_ix_per_p_ix_ofst]
a10 = 0.5*(f10 - fm10)
a01 = 0.5*(f01 - f0m1)
a11 = a00 - f01 - f10 + f11
a20 = 0.5*(f10 + fm10) - a00
a02 = 0.5*(f01 + f0m1) - a00
return a00 + tx*(a10 + tx*a20 + ty*a11) + ty*(a01 + ty*a02)
elif(_ord == 3): #bi-cubic interpolation based on 12 points
ix0 = int(trunc((_x - _x_min)/_x_step + 1.e-09))
if(ix0 < 1):
ix0 = 1
elif(ix0 >= _nx - 2):
ix0 = _nx - 3
ixm1 = ix0 - 1
ix1 = ix0 + 1
ix2 = ix0 + 2
tx = (_x - (_x_min + _x_step*ix0))/_x_step
iy0 = int(trunc((_y - _y_min)/_y_step + 1.e-09))
if(iy0 < 1):
iy0 = 1
elif(iy0 >= _ny - 2):
iy0 = _ny - 3
iym1 = iy0 - 1
iy1 = iy0 + 1
iy2 = iy0 + 2
ty = (_y - (_y_min + _y_step*iy0))/_y_step
nx_ix_per = _nx*_ix_per
iym1_nx_ix_per = iym1*nx_ix_per
iy0_nx_ix_per = iy0*nx_ix_per
iy1_nx_ix_per = iy1*nx_ix_per
iy2_nx_ix_per = iy2*nx_ix_per
ixm1_ix_per_p_ix_ofst = ixm1*_ix_per + _ix_ofst
ix0_ix_per_p_ix_ofst = ix0*_ix_per + _ix_ofst
ix1_ix_per_p_ix_ofst = ix1*_ix_per + _ix_ofst
ix2_ix_per_p_ix_ofst = ix2*_ix_per + _ix_ofst
f0m1 = _ar_f[iym1_nx_ix_per + ix0_ix_per_p_ix_ofst]
f1m1 = _ar_f[iym1_nx_ix_per + ix1_ix_per_p_ix_ofst]
fm10 = _ar_f[iy0_nx_ix_per + ixm1_ix_per_p_ix_ofst]
a00 = _ar_f[iy0_nx_ix_per + ix0_ix_per_p_ix_ofst]
f10 = _ar_f[iy0_nx_ix_per + ix1_ix_per_p_ix_ofst]
f20 = _ar_f[iy0_nx_ix_per + ix2_ix_per_p_ix_ofst]
fm11 = _ar_f[iy1_nx_ix_per + ixm1_ix_per_p_ix_ofst]
f01 = _ar_f[iy1_nx_ix_per + ix0_ix_per_p_ix_ofst]
f11 = _ar_f[iy1_nx_ix_per + ix1_ix_per_p_ix_ofst]
f21 = _ar_f[iy1_nx_ix_per + ix2_ix_per_p_ix_ofst]
f02 = _ar_f[iy2_nx_ix_per + ix0_ix_per_p_ix_ofst]
f12 = _ar_f[iy2_nx_ix_per + ix1_ix_per_p_ix_ofst]
a10 = -0.5*a00 + f10 - f20/6 - fm10/3
a01 = -0.5*a00 + f01 - f02/6 - f0m1/3
a11 = -0.5*(f01 + f10) + (f02 - f12 + f20 - f21)/6 + (f0m1 - f1m1 + fm10 - fm11)/3 + f11
a20 = -a00 + 0.5*(f10 + fm10)
a02 = -a00 + 0.5*(f01 + f0m1)
a21 = a00 - f01 + 0.5*(f11 - f10 - fm10 + fm11)
a12 = a00 - f10 + 0.5*(f11 - f01 - f0m1 + f1m1)
a30 = 0.5*(a00 - f10) + (f20 - fm10)/6
a03 = 0.5*(a00 - f01) + (f02 - f0m1)/6
a31 = 0.5*(f01 + f10 - f11 - a00) + (f21 + fm10 - f20 - fm11)/6
a13 = 0.5*(f10 - f11 - a00 + f01) + (f0m1 + f12 - f02 - f1m1)/6
return a00 + tx*(a10 + tx*(a20 + tx*(a30 + ty*a31) + ty*a21) + ty*a11) + ty*(a01 + ty*(a02 + ty*(a03 + tx*a13) + tx*a12))
return 0
#****************************************************************************
[docs]
def num_round(_x, _ndig=8):
## if(_x == 0.): return _x
## sgn = 1.
## if(_x < 0.):
## _x = -_x
## sgn = -1
## order = round(log10(_x))
## fact = 10**order
## roundNum = round(_x/fact, _ndig)
## res = roundNum*fact*sgn
res = round(_x, _ndig)
return res
#****************************************************************************
[docs]
def find_ar_max(_ar, _ib=0, _ie=-1, _min=False):
"""
Finds array (or list) maximum (or minimum), index and value
:param _ar: array (or list) to find max. or min.
:param _ib: array index to start search
:param _ie: array index to finish search
:param _min: switch specifying that minimum (rather than maximum) has to be searched
"""
strErIncorArray = 'Incorrect input array.'
if(_ar is None): raise Exception(strErIncorArray)
nTot = len(_ar)
if(nTot <= 0): raise Exception(strErIncorArray)
iBeg = _ib
if(_ib < 0): iBeg = 0
iEnd = _ie
if((iEnd == -1) or (_ie >= nTot)): iEnd = nTot - 1
if(iEnd < iBeg): raise Exception('Incorrect definition of start and end indexes.')
curExtr = _ar[0]
curInd = 0
for i in range(iBeg, iEnd + 1, 1):
curVal = _ar[i]
if(_min == True): curVal *= -1
if(curExtr < curVal):
curExtr = curVal
curInd = i
return curExtr, curInd
#****************************************************************************
[docs]
def integ_array(_ar, _h, _dupl=False):
"""
Integrates array (or list), eventually making a copy of it before the integration
:param _ar: array to integrate
:param _h: step size
:param _dupl: duplicate the magnetic field object or not
"""
ar = None
if(_dupl): ar = deepcopy(_ar)
else: ar = _ar
hd2 = 0.5*_h
auxInt = 0
lenAr = len(_ar)
for i in range(lenAr - 1):
ar_i = _ar[i] #in case if array has to be integrated in place
ar[i] = auxInt
auxInt += hd2*(ar_i + _ar[i + 1])
ar[lenAr - 1] = auxInt
return ar
#****************************************************************************
[docs]
def integ_ar_2d(_ar, _ar_align, _x_grid, _y_grid, _x_lim=None, _y_lim=None):
"""
Integrates 2d array (or list) within given limits
:param _ar: input array to integrate
:param _ar_align: input array alignment (1- _ar is 1d array with C-type data alignment, 2- _ar is 2d array)
:param _x_grid: list/array specifying grid vs one dimensions (_x_grid[0] is start, _x_grid[1] is end, _x_grid[2] is number of points)
:param _y_grid: list/array specifying grid vs another dimensions (_y_grid[0] is start, _y_grid[1] is end, _y_grid[2] is number of points)
:param _x_lim: list/array specifying inegration limits vs one dimensions (_x_lim[0] is start, _x_lim[1] is end)
:param _y_lim: list/array specifying inegration limits vs another dimensions (_y_lim[0] is start, _y_lim[1] is end)
"""
if(_x_lim is not None):
#print(_x_lim[0], _x_lim[1])
if(_x_lim[0] >= _x_lim[1]): return 0.
if(_y_lim is not None):
#print(_y_lim[0], _y_lim[1])
if(_y_lim[0] >= _y_lim[1]): return 0.
#print(_x_grid); print(_x_lim)
#print(_y_grid); print(_y_lim)
xStart = _x_grid[0]; xEnd = _x_grid[1]; nx = _x_grid[2]
xStep = (xEnd - xStart)/(nx - 1)
yStart = _y_grid[0]; yEnd = _y_grid[1]; ny = _y_grid[2]
yStep = (yEnd - yStart)/(ny - 1)
if((xStep == 0) or (yStep == 0)): return 0.
x_min = xStart; x_max = xEnd; nxInteg = 0
if(_x_lim is not None):
x_min = _x_lim[0]
x_max = _x_lim[1]
if(len(_x_lim) > 2): nxInteg = int(round(_x_lim[2]))
y_min = yStart; y_max = yEnd; nyInteg = 0
if(_y_lim is not None):
y_min = _y_lim[0]
y_max = _y_lim[1]
if(len(_y_lim) > 2): nyInteg = int(round(_y_lim[2]))
if((nxInteg > 2) and (nyInteg > 2) and (_ar_align == 1)): #perform integration using 2D interpolation
arAuxIntegWave2Dx = array('d', [0]*nxInteg)
if(x_min < xStart): x_min = xStart
if(x_max > xEnd): x_max = xEnd
xStepInteg = (x_max - x_min)/(nxInteg - 1)
arAuxIntegWave2Dy = array('d', [0]*nyInteg)
if(y_min < yStart): y_min = yStart
if(y_max > yEnd): y_max = yEnd
yStepInteg = (y_max - y_min)/(nyInteg - 1)
yy = y_min
for iy in range(nyInteg):
xx = x_min
for ix in range(nxInteg):
arAuxIntegWave2Dx[ix] = interp_2d(xx, yy, xStart, xStep, nx, yStart, yStep, ny, _ar, _ord=2)
xx += xStepInteg
arAux = integ_array(arAuxIntegWave2Dx, xStepInteg)
arAuxIntegWave2Dy[iy] = arAux[nxInteg - 1]
yy += yStepInteg
arAux = integ_array(arAuxIntegWave2Dy, yStepInteg)
resInteg = arAux[nyInteg - 1]
return resInteg
ixStart = int((x_min - xStart)/xStep + 1.e-12) #0.e-12)
if(ixStart < 0): ixStart = 0
else:
if(ixStart >= nx): ixStart = nx - 1
#print('ixStart=', ixStart)
iyStart = int((y_min - yStart)/yStep + 1.e-12) #0.e-12)
if(iyStart < 0): iyStart = 0
else:
if(iyStart >= ny): iyStart = ny - 1
#print('iyStart=', iyStart)
ixEnd = int((x_max - xStart)/xStep - 1.e-06) + 1
if(ixEnd < 0): ixEnd = 0
else:
if(ixEnd >= nx): ixEnd = nx - 1
#print('ixStart=', ixStart, 'ixEnd=', ixEnd)
iyEnd = int((y_max - yStart)/yStep - 1.e-06) + 1
if(iyEnd < 0): iyEnd = 0
else:
if(iyEnd >= ny): iyEnd = ny - 1
#print('iyStart=', iyStart, 'iyEnd=', iyEnd)
if((ixStart >= ixEnd) or (iyStart >= iyEnd)): return 0.
nxi = ixEnd - ixStart + 1
##make/O/N=(nxi) wAuxIntegWave2Dx
##SetScale/P x, xStart + ixStart*xStep, xStep, "", wAuxIntegWave2Dx
arAuxIntegWave2Dx = array('d', [0]*nxi)
nyi = iyEnd - iyStart + 1
##make/O/N=(nyi) wAuxIntegWave2Dy
##SetScale/P x, yStart + iyStart*yStep, yStep, "", wAuxIntegWave2Dy
arAuxIntegWave2Dy = array('d', [0]*nyi)
##for(iy = 0; iy < nyi; iy += 1)
## wAuxIntegWave2Dx = w2d[ixStart + p][iyStart + iy]
## integrate/T wAuxIntegWave2Dx
## wAuxIntegWave2Dy[iy] = wAuxIntegWave2Dx(x_max) - wAuxIntegWave2Dx(x_min)
##endfor
iyAbs_nx = 0
ar_iyAbs = None
for iy in range(nyi):
iyAbs = iy + iyStart
if(_ar_align == 1): iyAbs_nx = iyAbs*nx
else: ar_iyAbs = _ar[iyAbs]
for ix in range(nxi):
ixAbs = ix + ixStart
if(_ar_align == 1): arAuxIntegWave2Dx[ix] = _ar[iyAbs_nx + ixAbs]
else: arAuxIntegWave2Dx[ix] = ar_iyAbs[ixAbs]
#print(xStep, arAuxIntegWave2Dx)
arAux = integ_array(arAuxIntegWave2Dx, xStep)
arAuxIntegWave2Dy[iy] = arAux[nxi - 1]
##integrate/T wAuxIntegWave2Dy
##variable res = wAuxIntegWave2Dy(y_max) - wAuxIntegWave2Dy(y_min)
arAux = integ_array(arAuxIntegWave2Dy, yStep)
resInteg = arAux[nyi - 1]
return resInteg
#****************************************************************************
[docs]
def matr_prod(_A, _B):
"""
Multiplies matrix _A by matrix or by vector _B
"""
# Matrix multiplication
B0 = _B[0]
lenB = len(_B)
lenA = len(_A)
if(len(_A[0]) != lenB): # Check matrix dimensions
Exception('Matrices have wrong dimensions')
if(isinstance(B0, list) or isinstance(B0, array) or isinstance(B0, tuple)): #_B is matrix
lenB0 = len(B0)
C = [[0 for row in range(lenB0)] for col in range(lenA)]
for i in range(lenA):
for j in range(lenB0):
for k in range(lenB):
C[i][j] += _A[i][k]*_B[k][j]
else: #_B is vector
C = [0 for row in range(lenB)]
for i in range(lenA):
for k in range(lenB):
C[i] += _A[i][k]*_B[k]
return C
#****************************************************************************
[docs]
def matr_transp(_A):
"""
Returns transposed matrix
"""
lenA = len(_A)
lenA0 = len(_A[0])
return [[_A[i][j] for i in range(lenA)] for j in range(lenA0)]
#****************************************************************************
[docs]
def matr_print(_A):
"""
Prints matrix _A
"""
for i in range(len(_A)):
print(_A[i])
#****************************************************************************
[docs]
def matr3x3_det(_M):
S0 = _M[0]; S1 = _M[1]; S2 = _M[2]
return S0[0]*S1[1]*S2[2] + S0[1]*S1[2]*S2[0] + S0[2]*S1[0]*S2[1] - S0[2]*S1[1]*S2[0] - S0[0]*S1[2]*S2[1] - S0[1]*S1[0]*S2[2]
[docs]
def matr_3x3_det(_M):
return matr3x3_det(_M)
#****************************************************************************
[docs]
def matr3x3_inv(_M):
S0 = _M[0]; S1 = _M[1]; S2 = _M[2]
invDet = 1./(S0[0]*S1[1]*S2[2] + S0[1]*S1[2]*S2[0] + S0[2]*S1[0]*S2[1] - S0[2]*S1[1]*S2[0] - S0[0]*S1[2]*S2[1] - S0[1]*S1[0]*S2[2])
S0i = [invDet*(S1[1]*S2[2] - S1[2]*S2[1]), invDet*(-S0[1]*S2[2] + S0[2]*S2[1]), invDet*(S0[1]*S1[2] - S0[2]*S1[1])]
S1i = [invDet*(-S1[0]*S2[2] + S1[2]*S2[0]), invDet*(S0[0]*S2[2] - S0[2]*S2[0]), invDet*(-S0[0]*S1[2] + S0[2]*S1[0])]
S2i = [invDet*(S1[0]*S2[1] - S1[1]*S2[0]), invDet*(-S0[0]*S2[1] + S0[1]*S2[0]), invDet*(S0[0]*S1[1] - S0[1]*S1[0])]
return [S0i, S1i, S2i]
[docs]
def matr_3x3_inv(_M):
return matr3x3_inv(_M)
#****************************************************************************
[docs]
def vect_prod_s(_V1, _V2):
"""
Returns scalar product of vectors V1 and V2
"""
sizeV1 = len(_V1)
sizeV2 = len(_V2)
sizeV = sizeV1 if(sizeV1 < sizeV2) else sizeV2
res = 0
for i in range(sizeV): res += _V1[i]*_V2[i]
return res
#****************************************************************************
[docs]
def vect3_prod_v(_V1, _V2):
"""
Returns vector product of 3d vectors V1 and V2
"""
return [_V1[1]*_V2[2] - _V1[2]*_V2[1], _V1[2]*_V2[0] - _V1[0]*_V2[2], _V1[0]*_V2[1] - _V1[1]*_V2[0]]
#****************************************************************************
[docs]
def vect_norm(_V):
"""
Returns vector norm (/length)
"""
return sqrt(sum(n**2 for n in _V))
#****************************************************************************
[docs]
def vect_normalize(_V):
"""
Normalizes vector (in place, i.e. without creating new vector)
"""
invNorm = 1./vect_norm(_V)
for i in range(len(_V)): _V[i] *= invNorm
return _V
#****************************************************************************
[docs]
def vect_mult(_V, _a):
"""
Multiplies vector _V (in place) by number _a
"""
for i in range(len(_V)): _V[i] *= _a
return _V
#****************************************************************************
[docs]
def trf_rotation(_V, _ang, _P):
"""
Sets up matrix and vector describing rotation about axis _V passing through a point _P about an angle _ang
:param _V: vector (array of 3 Cartesian coordinates) defining rotation axis
:param _ang: rotation angle [rad]
:param _P: point (array of 3 Cartesian coordinates) rotation axis passes through
:returns list containing the 3x3 matrix and 3-element vector
"""
normFact = 1./sqrt(_V[0]*_V[0] + _V[1]*_V[1] + _V[2]*_V[2])
axVect = [normFact*_V[0], normFact*_V[1], normFact*_V[2]]
VxVx = axVect[0]*axVect[0]
VyVy = axVect[1]*axVect[1]
VzVz = axVect[2]*axVect[2]
cosAng = cos(_ang)
sinAng = sin(_ang)
one_m_cos = 1. - cosAng
one_m_cosVxVy = one_m_cos*axVect[0]*axVect[1]
one_m_cosVxVz = one_m_cos*axVect[0]*axVect[2]
one_m_cosVyVz = one_m_cos*axVect[1]*axVect[2]
sinVx = sinAng*axVect[0]
sinVy = sinAng*axVect[1]
sinVz = sinAng*axVect[2]
st0 = [VxVx + cosAng*(VyVy + VzVz), one_m_cosVxVy - sinVz, one_m_cosVxVz + sinVy]
st1 = [one_m_cosVxVy + sinVz, VyVy + cosAng*(VxVx + VzVz), one_m_cosVyVz - sinVx]
st2 = [one_m_cosVxVz - sinVy, one_m_cosVyVz + sinVx, VzVz + cosAng*(VxVx + VyVy)]
M = [st0, st1, st2]
st00 = [1. - st0[0], -st0[1], -st0[2]]
st01 = [-st1[0], 1. - st1[1], -st1[2]]
st02 = [-st2[0], -st2[0], 1. - st2[2]]
M0 = [st00, st01, st02]
V = matr_prod(M0, _P)
return [M, V]
#****************************************************************************
[docs]
def fwhm(x, y, shift=0.5, return_as_dict=False): # MR21032017
"""The function searches x-values (roots) where y=0 (after normalization to values between 0 and 1 and shifting the
values down by 0.5 (default value)) based on linear interpolation, and calculates full width at half maximum (FWHM).
:param x: an array of x values.
:param y: an array of y values.
:param shift: an optional shift to be used in the process of normalization (between 0 and 1).
:param return_as_dict: if to return a dict with 'fwhm' and 'x_range'
:return: a value of the FWHM or dictionary consisting of 'fwhm' and 'x_range'
"""
def is_positive(num):
return True if num > 0 else False
# Normalize values first:
#y = (y - min(y)) / (max(y) - min(y)) - shift # roots are at Y=0
#OC18112017 (making it work with standard Python lists / arrays)
minY = min(y)
maxY = max(y)
if(maxY == minY): raise Exception('FWHM can not be calculated')
mult = 1./(maxY - minY)
lenY = len(y)
for i in range(lenY): y[i] = (y[i] - minY)*mult - shift
positive = is_positive(y[0])
list_of_roots = []
#for i in range(len(y)):
for i in range(lenY):
current_positive = is_positive(y[i])
if current_positive != positive:
list_of_roots.append(x[i - 1] + (x[i] - x[i - 1]) / (abs(y[i]) + abs(y[i - 1])) * abs(y[i - 1]))
positive = not positive
if len(list_of_roots) >= 2:
if not return_as_dict:
return abs(list_of_roots[-1] - list_of_roots[0])
else:
return {
'fwhm': abs(list_of_roots[-1] - list_of_roots[0]),
'x_range': list_of_roots,
}
else:
raise Exception('Number of roots is less than 2!')
#****************************************************************************
#def fwhm_scipy(x, y): #MR27092016
# """Computing FWHM (Full width at half maximum)"""
# try:
# from scipy.interpolate import UnivariateSpline
# spline = UnivariateSpline(x, y, s=0)
# r1, r2 = spline.roots() # find the roots
# return r2 - r1 # return the difference (full width)
# except ImportError:
# return fwhm(x, y)
#****************************************************************************
[docs]
def get_dist_uni(_min, _max): #RAC30032020
'''Select point using a uniform distribution
:param _min: minimum possible value.
:param _max: maximum possible value.
'''
### Load package Numpy
#try:
# import numpy as np
#except:
# print('NumPy can not be loaded. You may need to install numpy. If you are using pip, you can use the following ' +
# "command to install it: \npip install numpy")
#if int(_min) == int(_max):
# return _max
#else:
# return np.random.randint(_min, _max)
return random.uniform(_min, _max) #OC24052020
#****************************************************************************
#def get_dist_norm(_min, _max, _scale=1.0): #OC24052020
[docs]
def get_dist_norm(_min, _max, _scale=1.0, _size=None): #RAC30032020
'''Select point using a normal (Gaussian) distribution
:param _min: minimum possible value.
:param _max: maximum possible value.
:param _scale = 1.0: Standard deviation (spread or “width”) of the distribution.
:param _size = None: (int or tuple of ints)
Output shape. If the given shape is, e.g., (m, n, k), then m * n * k
samples are drawn. If size is None (default), a single value is returned
if loc and scale are both scalars. Otherwise, np.broadcast(loc, scale).size
samples are drawn.
The probability density for the Gaussian distribution is:
p(x) = \frac{1}{\sqrt{ 2 \pi \sigma^2 }} e^{ - \frac{ (x - \mu)^2 } {2 \sigma^2} }
Where \mu is the mean and \sigma the standard deviation. The square of the
standard deviation, \sigma^2, is called the variance. The function has its peak
at the mean, and its “spread” increases with the standard deviation
(the function reaches 0.607 times its maximum at x + \sigma and x - \sigma [2]).
This implies that numpy.random.normal is more likely to return samples lying
close to the mean, rather than those far away.
'''
### Load package Numpy
#try:
# import numpy as np
#except:
# print('NumPy can not be loaded. You may need to install numpy. If you are using pip, you can use the following ' +
# "command to install it: \npip install numpy")
#try:
# _max >= _min
#except:
# print("Maximum distribution value is less than minimum distribution value")
#Get mean (“centre location”) of the distribution.
_loc = (_max+_min)/2 #OC24052020
#_loc = (_max-_min)/2
#print('_min=', _min, '_max=', _max, '_scale=', _scale) #DEBUG
#Get intiger from normal (Gaussian) distribution centered at _loc
_norm_var = 0
while _norm_var <= _min or _norm_var >= _max:
#OC24052020
_norm_var = random.gauss(_loc, _scale) #scale=_scale, size=_size)
#_norm_var = int(np.random.normal(loc=_loc, scale=_scale, size=_size))
#print(' _norm_var=', _norm_var) #DEBUG
return _norm_var
#****************************************************************************
[docs]
def get_dist_schultz(_min, _max, poly_index=0.3): #RAC30032020
'''elect point using a Flory–Schulz distribution
:param _min: minimum possible value.
:param _max: maximum possible value.
:param poly_index = 0.3: particles of varied sizes in the dispersed
phase of a disperse system.
The Flory–Schulz distribution is a discrete probability distribution named
after Paul Flory and Günter Victor Schulz that describes the relative ratios
of polymers of different length that occur in an ideal step-growth
polymerization process.
Calculate the Schulz distribution for polydisperse systems. When the
polydispersity is small, the Schulz distribution tends to a Gaussian.
Flory–Schulz Distribution:
y = y(0) + Ae^((x(c)-x)/w) * (x/x(c))^(x(c)/w)
Where:
y(0) = minimum (_min).
A = amplitude (_max-_min).
x(c) = center of distribution (mean of x_range).
w = width of distribution ((1 - poly_index^2) / (poly_index^2)).
'''
### Load package Numpy
try:
import numpy as np
except:
print('NumPy can not be loaded. You may need to install numpy. If you are using pip, you can use the following ' +
"command to install it: \npip install numpy")
#'Width' of the distribution
width = int((1 - np.power(poly_index, 2)) / np.power(poly_index, 2))
#Get a range of values
x_range = np.linspace(1,500)
#Get Mean of Distribution
mean = np.mean(x_range)
#Get Schulz Distribution
schulz = (_min + (_max-_min) * (np.exp((mean-x_range)/width)) * np.power((x_range/mean),(mean/width)))
return int(np.random.choice(schulz))