def __init__(self, *args, **kwargs): # parse parameters correctly param = parseParameters(args, kwargs) # set default parameters self.name = 'NakaRushton Distribution' self.param = { 'sigma': 1.0, 'kappa': 2.0, 's': 1.0, 'n': 2.0, 'p': 2.0, 'gamma': 2.0, 'delta': 0.0 } if param != None: for k in param.keys(): self.param[k] = float(param[k]) if param is None or 's' not in param.keys(): self.param['s'] = .5 * (gammafunc(1.0 / self.param['p']) / gammafunc(3.0 / self.param['p']))**( self.param['p'] / 2.0) if self.param['delta'] == 0.0: self.primary = [ 'sigma' ] # gamma would also be possible here, but stays out for backwards compatibility reasons else: self.primary = ['sigma', 'kappa', 'gamma', 'delta']
def dldtheta(self,dat): """ Evaluates the gradient of the Gamma function with respect to the primary parameters. :param dat: Data on which the gradient should be evaluated. :type dat: DataModule.Data :returns: The gradient :rtype: numpy.array """ m = dat.numex() grad = zeros((len(self.primary),m)) s = self.param['s'] p = self.param['p'] u = array([self.param['a'],self.param['b']]) cab = .5 + 0.5*sign(u)*gammainc(1/p,abs(u)**p /s) for ind,key in enumerate(self.primary): if key == 's': U = -.5*sign(u)*(abs(u) *exp(-abs(u)**p/s))/(s**(1/p+1)*gammafunc(1/p)) grad[ind,:] = -1.0/p/s + abs(squeeze(dat.X))**p/s**2 - (U[1]-U[0])/(cab[1]-cab[0]) if key == 'p': f = lambda x: 1/x df = lambda x: -1/x**2 g = lambda x,k: abs(u[k])**x/s dg = lambda x,k: abs(u[k])**x*log(abs(u[k]))/s dIncGamma = array([totalDerivativeOfIncGamma(p,f,lambda v: g(v,0),df,lambda v: dg(v,0)),\ totalDerivativeOfIncGamma(p,f,lambda v: g(v,1),df,lambda v: dg(v,1))]) U = .5*sign(u) * (dIncGamma/float(gammafunc(1/p)) + gammainc(1/p,abs(u)**p/s)*digamma(1/p)/p**2) grad[ind,:] = 1/p + 1/p**2*log(s) + digamma(1/p)*1/p**2 - abs(squeeze(dat.X))**p*log(abs(squeeze(dat.X)))/s - (U[1]-U[0])/(cab[1]-cab[0]) return grad
def pmu(mu, params): mu0 = params[0] lamb = params[1] alpha = params[2] beta = params[3] return (lamb / (2 * np.pi))**0.5 * beta**alpha * gammafunc( alpha + 0.5) / gammafunc(alpha) * (beta + 0.5 * lamb * (mu - mu0)**2)**(-(alpha + 0.5))
def __init__(self, *args,**kwargs): # parse parameters correctly param = parseParameters(args,kwargs) # set default parameters self.name = 'NakaRushton Distribution' self.param = {'sigma':1.0,'kappa':2.0,'s':1.0,'n':2.0,'p':2.0,'gamma':2.0,'delta':0.0} if param != None: for k in param.keys(): self.param[k] = float(param[k]) if param is None or 's' not in param.keys(): self.param['s'] = .5*(gammafunc(1.0/self.param['p'])/gammafunc(3.0/self.param['p']))**(self.param['p']/2.0) if self.param['delta'] == 0.0: self.primary = ['sigma'] # gamma would also be possible here, but stays out for backwards compatibility reasons else: self.primary = ['sigma','kappa','gamma','delta']
def gs_val_meso(gs, t, ca, k1_interp, k2_interp, cp_interp, psi_lcrit_interp, psi_x, VPDinterp, psi_63, w_exp, Kmax, psi_sat, gamma, b, d_r, z_r, RAI, lai, lam): psi_crit = psi_lcrit_interp(psi_x) VPD = VPDinterp(t) k1 = k1_interp(t) k2 = k2_interp(t) cp = cp_interp(t) x = (psi_x / psi_sat)**-(1 / b) psi_r = 1.6 * gs * VPD / gSR_val(x, gamma, b, d_r, z_r, RAI, lai) + psi_x # res = root(lambda psi_l: 1.6 * gs * VPD - Kmax * (psi_63 / w_exp) * # (gammaincc(1 / w_exp, (psi_r / psi_63) ** w_exp) - gammaincc(1 / w_exp, (psi_l / psi_63) ** w_exp)), # psi_r + 0.1, method='hybr') # psi_l = res.get('x') psi_l_temp = gammainccinv( 1 / w_exp, -1.6 * gs * VPD * w_exp / (gammafunc(1 / w_exp) * Kmax * psi_63) + gammaincc(1 / w_exp, (psi_r / psi_63)**w_exp)) psi_l = psi_63 * psi_l_temp**(1 / w_exp) phi = 1 - psi_l / psi_crit part1 = dAdgs(t, gs, ca, k1_interp, k2_interp, cp_interp, phi) part2 = dAdB(t, gs, ca, k1_interp, k2_interp, cp_interp, phi) *\ dB_dgs(cp, k2, psi_l, psi_crit, psi_x, psi_r, VPD, psi_63, w_exp, Kmax, psi_sat, gamma, b, d_r, z_r, RAI, lai) B = (cp + k2) / phi return part1 + part2 - 1.6 * lam * VPD * np.sqrt( (B + ca - cp)**2 * gs**2 + 2 * (B - ca + cp) * gs * k1 + k1**2)
def dldtheta(self, dat): """ Evaluates the gradient of the Gamma function with respect to the primary parameters. :param dat: Data on which the gradient should be evaluated. :type dat: DataModule.Data :returns: The gradient :rtype: numpy.array """ m = dat.numex() grad = zeros((len(self.primary), m)) s = self.param['s'] p = self.param['p'] u = array([self.param['a'], self.param['b']]) cab = .5 + 0.5 * sign(u) * gammainc(1 / p, abs(u)**p / s) for ind, key in enumerate(self.primary): if key == 's': U = -.5 * sign(u) * (abs(u) * exp(-abs(u)**p / s)) / ( s**(1 / p + 1) * gammafunc(1 / p)) grad[ind, :] = -1.0 / p / s + abs(squeeze( dat.X))**p / s**2 - (U[1] - U[0]) / (cab[1] - cab[0]) if key == 'p': f = lambda x: 1 / x df = lambda x: -1 / x**2 g = lambda x, k: abs(u[k])**x / s dg = lambda x, k: abs(u[k])**x * log(abs(u[k])) / s dIncGamma = array([totalDerivativeOfIncGamma(p,f,lambda v: g(v,0),df,lambda v: dg(v,0)),\ totalDerivativeOfIncGamma(p,f,lambda v: g(v,1),df,lambda v: dg(v,1))]) U = .5 * sign(u) * ( dIncGamma / float(gammafunc(1 / p)) + gammainc(1 / p, abs(u)**p / s) * digamma(1 / p) / p**2) grad[ind, :] = 1 / p + 1 / p**2 * log(s) + digamma( 1 / p) * 1 / p**2 - abs(squeeze(dat.X))**p * log( abs(squeeze( dat.X))) / s - (U[1] - U[0]) / (cab[1] - cab[0]) return grad
def totalDerivativeOfIncGamma(x, a, b, da, db): """ Computes the total derivative for the (non-normalized) incomplete gamma function, i.e. d/dx gamma(a(x))*gammainc(a(x),b(x)) :param x: Positions where the function is to be computed. :type x: numpy.ndarray :param a: function handle for a :type a: python function :param b: function handle for b :type b: python function :param da: function handle for da/dx :type da: python function :param db: function handle for db/dx :type db: python function :returns: derivative values :rtype: numpy.ndarray """ return digamma(a(x))*gammafunc(a(x))*da(x) + exp(-b(x))*b(x)**(a(x)-1)*db(x) \ - (meijerg([[],[1,1],],[[0,0,a(x)],[]],b(x)) + log(b(x))*gammaincc(a(x),b(x))*gammafunc(a(x))) * da(x)
def psil_crit_val(psi_l, psi_x, psi_sat, gamma, b, d_r, z_r, RAI, lai, Kmax, psi_63, w_exp, frac=0.05): import warnings x = (psi_x / psi_sat)**(-1 / b) soil_root = gSR_val(x, gamma, b, d_r, z_r, RAI, lai) # with warnings.catch_warnings(): # warnings.filterwarnings('error', category=RuntimeWarning) # try: # res_psir = root(lambda psi_r: Kmax * psi_63 * gammafunc(1 / w_exp) / w_exp * # (gammaincc(1 / w_exp, (psi_r / psi_63) ** w_exp) - # gammaincc(1 / w_exp, (psi_l / psi_63) ** w_exp)) - # soil_root * (psi_r - psi_x), # (psi_l + psi_x) / 2, # method='hybr') # psi_r = res_psir.get('x') # except RuntimeWarning: # import pdb; pdb.set_trace() # import sys # sys.exit() res_psir = root( lambda psi_r: Kmax * psi_63 * gammafunc(1 / w_exp) / w_exp * (gammaincc(1 / w_exp, (psi_r / psi_63)**w_exp) - gammaincc( 1 / w_exp, (psi_l / psi_63)**w_exp)) - soil_root * (psi_r - psi_x), (psi_l + psi_x) / 2, method='hybr') psi_r = res_psir.get('x') print('psil_crit_val status:' + res_psir.message) X_part1 = grl_val(psi_r, psi_63, w_exp, Kmax) + soil_root X_part2 = grl_val(psi_x, psi_63, w_exp, Kmax) + soil_root X = frac * grl_val(psi_x, psi_63, w_exp, Kmax) / Kmax * X_part1 / X_part2 # import pdb; pdb.set_trace() return psi_l - psi_63 * (-np.log(X))**(1 / w_exp)
def plant_cond_integral(psi_l, psi_r, psi_63, w_exp, Kmax, reversible=0): """ :param psi_r: root water potential in MPa :param psi_l: leaf water potential in MPa :param psi_63: Weibull parameter in MPa :param w_exp: Weibull exponent :param Kmax: Saturated plant LEAF area-average conductance in mol/m2/MPa/d :return: Unsaturated plant LEAF area-average conductance in mol/m2/MPa/d """ cond_pot = gammafunc(1 / w_exp) * Kmax * psi_63 / w_exp *\ (gammaincc(1 / w_exp, (psi_r / psi_63) ** w_exp) - gammaincc(1 / w_exp, (psi_l / psi_63) ** w_exp)) if reversible: return cond_pot else: cond_pot = np.minimum.accumulate(cond_pot) return cond_pot
def gs_val_profit(gs, t, ca, k1_interp, k2_interp, cp_interp, trans_max_interp, k_max_interp, k_crit_interp, psi_x, VPDinterp, psi_63, w_exp, Kmax, psi_sat, gamma, b, d_r, z_r, RAI, lai): trans_max = trans_max_interp(psi_x) k_max = k_max_interp(psi_x) k_crit = k_crit_interp(psi_x) VPD = VPDinterp(t) k1 = k1_interp(t) k2 = k2_interp(t) cp = cp_interp(t) a = 1.6 x = (psi_x / psi_sat)**-(1 / b) psi_r = a * gs * VPD / gSR_val(x, gamma, b, d_r, z_r, RAI, lai) + psi_x psi_l_temp = gammainccinv( 1 / w_exp, -1.6 * gs * VPD * w_exp / (gammafunc(1 / w_exp) * Kmax * psi_63) + gammaincc(1 / w_exp, (psi_r / psi_63)**w_exp)) psi_l = psi_63 * psi_l_temp**(1 / w_exp) grl_psil = grl_val(psi_l, psi_63, w_exp, Kmax) grl_psir = grl_val(psi_r, psi_63, w_exp, Kmax) gSR = gSR_val(x, gamma, b, d_r, z_r, RAI, lai) dEdpsil = grl_psil * gSR / (grl_psir + gSR) gs_max = trans_max / a / VPD Amax = A_here(gs_max, ca, k1, k2, cp) d2Edpsil2 = dEdpsil * (-(w_exp / psi_63) * (psi_l / psi_63)**(w_exp - 1) + grl_psir * (w_exp / psi_63) * (psi_r / psi_63)**(w_exp - 1) * grl_psil / (grl_psir + gSR)**2) part1 = dAdgs(t, gs, ca, k1_interp, k2_interp, cp_interp, 1) * dEdpsil / (a * VPD) part2 = - Amax * d2Edpsil2 / (k_max - k_crit) *\ np.sqrt((k2 + ca) ** 2 * gs ** 2 + 2 * (k2 - ca + 2 * cp) * gs * k1 + k1 ** 2) return part2 - part1
def igf(a, x): """ Incomplete gamma function """ return gammainc(a, x) * gammafunc(a)
def cigf(a, x): """ Complementary incomplete gamma function """ return gammaincc(a, x) * gammafunc(a)
def minersum_weibull(q, h, sn, v0, td=None, scf=1., th=None): """ Fatigue damage (Palmgren-Miner sum) calculation based on (2-parameter) Weibull stress cycle distribution and S-N curve. Ref. DNV-RP-C03 (2016) eq. F.12-1. Parameters ---------- q: float Weibull scale parameter (in 2-parameter distribution). h: float Weibull shape parameter (in 2-parameter distribution). sn: dict or SNCurve Dictionary with S-N curve parameters, alternatively an SNCurve instance. If dict, expected attributes are: 'm1', 'm2', 'a1' (or 'loga1'), 'nswitch'. v0: float, Cycle rate [1/s]. td: float, optional Duration [s] (or design life, in seconds). Default is 31536000 (no. of seconds in a year, or 365 days). scf: float, optional Stress concentration factor to be applied on stress ranges. th: float, optional Thickness [mm] for thickness correction. If specified, reference thickness and thickness exponent must be defined for the S-N curve given. Returns ------- float Fatigue damage (Palmgren-Miner sum). Raises ------ ValueError: If thickness is given but thickness correction not specified for S-N curve. """ def cigf(a, x): """ Complementary incomplete gamma function """ return gammaincc(a, x) * gammafunc(a) def igf(a, x): """ Incomplete gamma function """ return gammainc(a, x) * gammafunc(a) if not isinstance(sn, SNCurve): sn = SNCurve("", **sn) if td is None: td = 3600. * 24 * 365 if th is not None: try: # include thickness correction in SCF scf *= sn.thickness_correction(th) except ValueError: raise # todo: verify implementation of thickness correction # scale Weibull scale parameter by SCF (incl. thickness correction if specified) q *= scf if sn.bilinear is True: # gamma functions g1 = cigf(1 + sn.m1 / h, (sn.sswitch / q)**h) # complementary incomplete gamma function g2 = igf(1 + sn.m2 / h, (sn.sswitch / q)**h) # incomplete gamma function # fatigue damage (for specified duration) d = v0 * td * (q**sn.m1 / sn.a1 * g1 + q**sn.m2 / sn.a2 * g2) else: # single slope S-N curve, fatigue damage for specified duration d = v0 * td * (q**sn.m1 / sn.a1) * gammafunc(1 + sn.m1 / h) return d
def calculate_P_Wm(self, ppb, sigma_t, b_field=0., n_bunches=None): """ If n_bunches is not specified, ppb has to be either a numpy array with dimensions time_steps x n_bunches or n_bunches. If n_bunches is specified, ppb can be either a number or an arry with dimensions of timesteps sigma_t is either a float or a np array of dimensions time_steps or timp_steps * n_bunches Do not specify n_bunches explicitly and implicitly at the same time! """ if n_bunches is None: if not hasattr(ppb, '__iter__'): raise ValueError('N_bunches has to be specified somehow!') else: n_bunches = 1. else: if hasattr(ppb, '__iter__'): raise ValueError( 'N_bunches should not be specified if a ppb array is provided!' ) rho_B0_T = self.copper_rho_Ohm_m( self.temperature_K) # 0.014 *1e-8 for 20 K if not hasattr(b_field, '__iter__') and b_field == 0.: rho_B_T = rho_B0_T else: rho_B0_273 = self.copper_rho_Ohm_m(273.) #elias rho_B_T = rho_B_T = rho_B0_T * ( 1 + 10**(1.055 * np.log10(b_field * (rho_B0_273 / rho_B0_T)) - 2.69)) #philipp #try: # rho_B0_4 = self.copper_rho_Ohm_m(4.) #except ValueError: # rho_B0_4 = 3.*self.copper_rho_Ohm_m(4.2) - 2.*self.copper_rho_Ohm_m(4.3) #rho_B_T = rho_B0_T * (1. + 10.**(-2.69) * (b_field*rho_B0_273/rho_B0_4)**1.055) #benoit/nicolas #rho_B_T = rho_B0_T *(1.0048 + 0.0038*b_field*self.copper_rho_Ohm_m(300.)/self.copper_rho_Ohm_m(20.)) #For 20K!!! #rho_B_T = 2.4e-10 *(1.0048 + 0.0038*b_field*70.) #print rho_B_T per_bunch_factor = ppb**2 * sigma_t**(-1.5) if hasattr(ppb, '__iter__') and len(ppb.shape) == 2: per_bunch_factor = np.sum(per_bunch_factor, axis=1) P_no_weld = 1. / self.circumference_m * gammafunc( 0.75) * n_bunches / self.chamb_radius_m * ( qe / 2. / np.pi)**2 * np.sqrt( c * rho_B_T * Z0_vac / 2.) * per_bunch_factor if self.weld_Thickness_m is not None: weld_Factor = 1. + np.sqrt( self.weld_Rho_Ohm_m / rho_B_T) * self.weld_Thickness_m / ( 2. * np.pi * self.chamb_radius_m) #weld_Factor = 1.4 # benoit assumption else: weld_Factor = 1. return P_no_weld * weld_Factor