Exemple #1
0
def validate_asd(**kwargs):
    """
    Validation of all input parameters that go into std_step_clampon, std_step_emerson and sand_rate functions;
    """
    for i in ['v_m', 'GLR', 'GOR']:
        if i in kwargs:
            if kwargs[i] is None:
                raise exc.FunctionInputFail('No calculation is done due to missing {}'.format(i))
            if not isinstance(kwargs[i], (float, int)):
                raise exc.FunctionInputFail('{} is not a number'.format(i))
            if not kwargs[i] >= 0:
                logger.warning('The model has got negative value(s) of {} and returned nan.'.format(i))
                return True
Exemple #2
0
def sand_rate(raw, zero, step, exp=1):
    '''
    ASD sand rate calculation
    :param raw: raw value from ASD
    :param zero: Background noise zero value (no sand production)
    :param step: Sand noise
    :param exp: Linearity exponent (calibration constant)
    :return: Sand rate [g/s]
    '''

    for key, value in {'raw': raw, 'zero': zero, 'step': step, 'exp': exp}.items():
        if value is None:
            raise exc.FunctionInputFail('No calculation is done due to missing {}'.format(key))

    if raw > zero:
        try:
            Qs = (raw - zero)**exp / step
        except ZeroDivisionError:
            logger.warning('Step value equal to zero, sand rate set to NaN')
            Qs = np.nan
        else:
            if Qs < 0:
                logger.warning('Negative step. Sand rate set to NaN')
                Qs = np.nan
    else:
        Qs = 0

    return Qs
def validate_fluid_props(**kwargs):
    """
        Validation of all input parameters that go into fluid properties models
    """
    for i in [
            'P', 'T', 'Qo', 'Qw', 'Qg', 'Z', 'D', 'rho_o', 'rho_w', 'MW',
            'mu_o', 'mu_w', 'mu_g'
    ]:
        if i in kwargs:
            if kwargs[i] is None:
                raise exc.FunctionInputFail(
                    'No fluid properties are calculated due to missing {}'.
                    format(i))
            if not isinstance(kwargs[i], (float, int)):
                raise exc.FunctionInputFail('{} is not a number'.format(i))
            if not kwargs[i] >= 0:
                logger.warning(
                    'The model has got negative value(s) of {} and returned nan.'
                    .format(i))
                return True
Exemple #4
0
def validate_inputs(**kwargs):

    for i in ['rho_m', 'rho_l', 'mu_m', 'mu_l', 'd_p', 'e', 'rho_p', 'D']:
        if i in kwargs:
            if not isinstance(kwargs[i], (float, int)) or np.isnan(kwargs[i]):
                raise exc.FunctionInputFail('{} is not a number'.format(i))
            if not kwargs[i] >= 0:
                logger.warning(
                    'The model got negative value(s) of {} and returned nan.'.
                    format(i))
                return True

    if 'angle' in kwargs:
        if not isinstance(kwargs['angle'],
                          (float, int)) or np.isnan(kwargs['angle']):
            raise exc.FunctionInputFail('angle is not a number')
        if (kwargs['angle'] < 0) or (kwargs['angle'] > 89):
            raise exc.FunctionInputFail(
                'The inclination has to be positive and below 90 deg.')

    return
Exemple #5
0
def validate_inputs(**kwargs):
    """
    Validation of all input parameters that go into probe models;
    Besides validating for illegal data input, model parameters are limited within RP-O501 boundaries:
    ----------------------------------------------------------------------------
    Model parameter                  ---   Lower boundary   ---   Upper boundary
    ----------------------------------------------------------------------------
    Measured erosion rate (E_meas)   ---        0           ---              ---
    Mix velocity (v_m)               ---        5           ---              ---
    ----------------------------------------------------------------------------
    """

    for i in kwargs:
        if i in ['E_meas', 'v_m']:
            if not isinstance(kwargs[i], (float, int)):
                raise exc.FunctionInputFail('{} is not a number'.format(i))
            if kwargs[i] < 0:
                raise exc.FunctionInputFail('{} cannot be negative.'.format(i))
            if i == 'v_m':
                if kwargs[i] < 5:
                    logger.warning('Mix velocity, v_m = {} m/s, is too low (< 5 m/s) '
                                   'to trust the quantification model.'.format(kwargs[i]))
Exemple #6
0
def F(a_rad, angle_dependency):
    """
    Angle dependency function, reference to DNVGL RP-O501, August 2015 (3.3)
    :param a_rad: impact angle [radians]
    :param angle_dependency: angle dependency (ductile or brittle)
    :return: ductility factor
    """
    if angle_dependency == 'ductile':
        A, B, C, k = .6, 7.2, 20, .6
        return A * (np.sin(a_rad) + B * (np.sin(a_rad) - np.sin(a_rad) ** 2))**k * (1 - np.exp(-C * a_rad))
    elif angle_dependency == 'brittle':
        return 2 * a_rad / np.pi
    else:
        raise exc.FunctionInputFail('Angle dependency {} is not defined.'.format(angle_dependency))
Exemple #7
0
def material_properties(material):
    """
    Function to deal with material properties, reference to table 3-1 in DNVGL RP-O501, August 2015
    :param material: Material. For a full list of materials run: materials()
    :return: rho_t (material density), K (material constant), n (material exponent), angle_dependency
    """

    properties = {'carbon_steel': (7800, 2e-9, 2.6, 'ductile'),
                  'duplex': (7850, 2e-9, 2.6, 'ductile'),
                  'ss316': (8000, 2e-9, 2.6, 'ductile'),
                  'inconel': (8440, 2e-9, 2.6, 'ductile'),
                  'grp_epoxy': (1800, 3e-10, 3.6, 'ductile'),
                  'grp_vinyl_ester': (1800, 6e-10, 3.6, 'ductile'),
                  'hdpe': (1150, 3.5e-9, 2.9, 'ductile'),
                  'aluminium': (2700, 5.8e-9, 2.3, 'ductile'),
                  'dc_05_tungsten': (15250, 1.1e-10, 2.3, 'brittle'),
                  'cs_10_tungsten': (14800, 3.2e-10, 2.2, 'brittle'),
                  'cr_37_tungsten': (14600, 8.8e-11, 2.5, 'brittle'),
                  '95_alu_oxide': (3700, 6.8e-8, 2, 'brittle'),
                  '99_alu_oxide': (3700, 9.5e-7, 1.2, 'brittle'),
                  'psz_ceramic_zirconia': (5700, 4.1e-9, 2.5, 'brittle'),
                  'ZrO2-Y3_ceramic_zirconia': (6070, 4e-11, 2.7, 'brittle'),
                  'SiC_silicon_carbide': (3100, 6.5e-9, 1.9, 'brittle'),
                  'Si3N4_silicon_nitride': (3200, 2e-10, 2, 'brittle'),
                  'TiB2_titanium_diboride': (4250, 9.3e-9, 1.9, 'brittle'),
                  'B4C_boron_carbide': (2500, 3e-8, .9, 'brittle'),
                  'SiSiC_ceramic_carbide': (3100, 7.4e-11, 2.7, 'brittle')}

    if material == 'list':
        return list(properties.keys())

    if material not in properties:
        raise exc.FunctionInputFail('The material {} is not defined. For a full list of materials run materials()'
                                    .format(material))

    rho_t = properties[material][0]
    K = properties[material][1]
    n = properties[material][2]
    angle_dependency = properties[material][3]

    return rho_t, K, n, angle_dependency
Exemple #8
0
def welded_joint(v_m, rho_m, D, d_p, h, alpha=60, location='downstream', material='duplex'):
    '''
    Particle erosion in welded joints, model reference to DNVGL RP-O501, August 2015
    :param v_m: Mix velocity [m/s]
    :param rho_m: Mix density [kg/m3]
    :param D: Pipe diameter [m]
    :param d_p: Particle diameter [mm]
    :param h: height of the weld [m]
    :param alpha: particle impact angle [degrees], default = 60
    :param location: Erosion calculation locations 'downstream' or 'upstream' of weld, default = 'downstream'
    :param material: Material exposed to erosion, default = 'duplex' (duplex steel). For others, run: materials()
    :return: E_up: Relative erosion at flow facing part of weld [mm/ton]
    :return: E_down: Relative erosion downstream of weld [mm/ton]
    '''

    # Input validation
    kwargs = {'v_m': v_m, 'rho_m': rho_m, 'D': D, 'd_p': d_p, 'h': h, 'alpha': alpha}
    if validate_inputs(**kwargs):
        return np.nan

    if (alpha < 0) or (alpha > 90):
            logger.warning('Particle impact angle [degrees], alpha, is outside RP-O501 model boundaries (0-90 deg).')

    rho_t, K, n, ad = material_properties(material)

    A_pipe = np.pi * D**2 / 4
    a_rad = np.deg2rad(alpha)
    C_unit = 3.15e10  # Conversion factor from m/s to mm/year (4.24)
    C2 = 10**6 * d_p / 1000 / (30 * rho_m**.5)  # Particle size and fluid density correction factor (4.25)
    if C2 >= 1:
        C2 = 1
    if location == 'downstream':
        E_down = 3.3e-2 * (7.5e-4 + h) * v_m**n * D**(-2) * (1e6/C_unit)
        return E_down
    elif location == 'upstream':
        E_up = K * F(a_rad, ad) * v_m ** n * np.sin(a_rad) / (rho_t * A_pipe) * C2 * 10**6
        return E_up
    else:
        raise exc.FunctionInputFail('Location must be either downstream or upstream. {} is passed.'.format(location))
Exemple #9
0
def validate_inputs(**kwargs):
    """
    Validation of all input parameters that go into erosion models;
    Besides validating for illegal data input, model parameters are limited within RP-O501 boundaries:
    -------------------------------------------------------------------------
    Model parameter               ---   Lower boundary   ---   Upper boundary
    -------------------------------------------------------------------------
    Mix velocity                  ---        0           ---        200   ---
    Mix density                   ---        1           ---        1500  ---
    Mix viscosity                 ---        1e-6        ---        1e-2  ---
    Particle concentration [ppmV] ---        0           ---        500   ---
    Particle diameter             ---        0.02        ---        5     ---
    Pipe inner diameter(D)        ---        0.01        ---        1     ---
    Particle impact angle         ---        0           ---        90    ---
    Bend radius                   ---        0.5         ---        50    ---
    Manifold diameter             ---        D           ---              ---
    Height of the weld            ---        0           ---        D     ---
    Radius of choke gallery (Rc)  ---        0           ---              ---
    Gap cage and choke body (gap) ---        0           ---        Rc    ---
    Height of gallery (H)         ---        0           ---              ---
    -------------------------------------------------------------------------
    Geometry factor can only be 1, 2, 3 or 4
    """

    if not 'rho_p' in kwargs:
        kwargs['rho_p'] = 2650

    for i in ['v_m', 'rho_m', 'mu_m', 'Q_s']:
        if i in kwargs:
            if not isinstance(kwargs[i], (float, int)) or np.isnan(kwargs[i]):
                raise exc.FunctionInputFail('{} is not a number'.format(i))
            if not kwargs[i] >= 0:
                logger.warning('The model has got negative value(s) of {} and returned nan.'.format(i))
                return True

    if 'v_m' in kwargs:
        if kwargs['v_m'] > 200:
            logger.warning('Mix velocity, v_m, is outside RP-O501 model boundaries (0-200 m/s).')
    if 'rho_m' in kwargs:
        if (kwargs['rho_m'] < 1) or (kwargs['rho_m'] > 1500):
            logger.warning('Mix density, rho_m, is outside RP-O501 model boundaries (1-1500 kg/m3).')
    if 'mu_m' in kwargs:
        if (kwargs['mu_m'] < 1e-6) or (kwargs['mu_m'] > 1e-2):
            logger.warning(
                'Mix viscosity, mu_m, is outside RP-O501 model boundaries (1e-6 - 1e-2 kg/ms).')

    if ('Q_s' in kwargs) and ('rho_p' in kwargs) and ('v_m' in kwargs) and ('D' in kwargs):
        ppmV = kwargs['Q_s'] / (kwargs['rho_p'] * kwargs['v_m'] * np.pi/4*kwargs['D']**2) * 1e3  # (4.20 in RP-O501)
        if (ppmV < 0) or (ppmV > 500):
            logger.warning('The particle concentration is outside RP-O501 model boundaries ( 0-500 ppmV).')

    for j in ['R', 'GF', 'D', 'd_p', 'h', 'Dm', 'D1', 'D2', 'R_c', 'gap', 'H', 'alpha', 'At']:
        if j in kwargs:
            if not isinstance(kwargs[j], (int, float)) or np.isnan(kwargs[j]):
                raise exc.FunctionInputFail('{} is not a number'.format(j))

    for k in ['D', 'D1', 'D2']:
        if k in kwargs:
            if (kwargs[k] < 0.01) or (kwargs[k] > 1):
                logger.warning('Pipe inner diameter, {}, is outside RP-O501 model boundaries (0.01 - 1 m).'.format(k))
            if not kwargs[k] > 0:
                raise exc.FunctionInputFail(' Pipe inner diameter, {}, must be positive'.format(k))

    if 'd_p' in kwargs:
        if (kwargs['d_p'] < 0.02) or (kwargs['d_p'] > 5):
            logger.warning('Particle diameter, d_p, is outside RP-O501 model boundaries (0.02 - 5 mm).')
        if kwargs['d_p'] < 0:
            exc.FunctionInputFail('Particle diameter cannot be negative')
    if 'GF' in kwargs:
        if kwargs['GF'] not in [1, 2, 3, 4]:
            logger.warning('Geometry factor, GF, can only be 1, 2, 3 or 4')

    # bend/choke gallery
    if 'R' in kwargs:
        if (kwargs['R'] < 0.5) or (kwargs['R'] > 50):
            logger.warning('Bend radius, R, is outside RP-O501 model boundaries.')

    # manifold
    if 'Dm' in kwargs:
        if kwargs['Dm'] < kwargs['D']:
            logger.warning('Manifold diameter, Dm, is expected to be bigger than branch pipe diameter, D')

    # welded joint
    if 'h' in kwargs:
        if (kwargs['h'] < 0) or (kwargs['h'] > kwargs['D']):
            logger.warning('Height of the weld, h, must positive number not exceeding pipe inner diameter size, D')

    # choke gallery
    for l in ['R_c', 'gap', 'H']:
        if l in kwargs:
            if not kwargs[l] > 0:
                raise exc.FunctionInputFail('{} has to be larger than 0'.format(l))
    if 'R_c' in kwargs and 'gap' in kwargs:
        if kwargs['gap'] > kwargs['R_c']:
            raise exc.FunctionInputFail('The gap between the cage and choke body is larger than the radius')

    # Nozzlevalve wall
    if 'model' in kwargs:
        if kwargs['model'] == 'nozzlevalve_wall' and kwargs['d_p'] > 0.6:
            logger.warning('Particle diameter, d_p, is higher than CFD-study boundary (0.6 mm).')