Source code for opendrift.models.openoil.adios.models.oil.cleanup.add_labels

'''
Add labels to a record -- for those that we can use some automatic criteria for

For a given product type:
    some labels can be determined from API and/or Viscosity ranges.

The criteria follows the ASTM (and others) standards, where we can
'''
from math import inf

from ..product_type import types_to_labels
from ....computation.physical_properties import KinematicViscosity

# # Here are all the Product Types:
# ('Crude Oil NOS',
#  'Tight Oil',
#  'Condensate',
#  'Bitumen Blend',
#  'Refined Product NOS',
#  'Fuel Oil NOS',
#  'Distillate Fuel Oil',
#  'Residual Fuel Oil',
#  'Refinery Intermediate',
#  'Solvent',
#  'Bio-fuel Oil',
#  'Bio-Petro Fuel Oil',
#  'Natural Plant Oil',
#  'Lube Oil',
#  'Dielectric Oil',
#  'Other')

# # These are the current labels that aren't mapped yet:

# 'MDO', 'Vacuum Gas Oil'

# these are the labels with no criteria for density or viscosity
# e.g, if it's a "Crude Oil NOS", it's a 'Crude Oil'
synonyms_for_product_types = {
    'Crude Oil', 'Shale Oil', 'Fracking Oil', 'Fuel Oil', 'Residual Fuel', 'Distillate Fuel',
    'Refined Product', 'Condensate', 'Transformer Oil'
}
# If it's an exact match, then it's definitely a synonym
for pt, labels in types_to_labels.labels.items():
    for label in labels:
        if label == pt:
            # print(f"adding: {label}")
            synonyms_for_product_types.add(label)

# these are labels that are synonymous to other labels
synonyms_for_labels = {
    'Heavy Fuel Oil': ['HFO', 'No. 6 Fuel Oil', 'Bunker C'],
    'Kerosene': ['Jet Fuel'],
    'No. 2 Fuel Oil': ['Diesel', 'Home Heating Oil'],
}

no_criteria = {
    "api_min": -inf,
    "api_max": inf,
    "kvis_min": -inf,
    "kvis_max": inf,
    'kvis_temp': 15
}
label_map = {label: no_criteria for label in synonyms_for_product_types}

# this maps the labels according to API and kinematic viscosity (cSt at given temp in C) ranges.
label_map.update({
    # 'example_label': {"api_min": -inf, "api_max": inf, "kvis_min": -inf, "kvis_max": inf, 'kvis_temp': None},
    'Condensate': {
        "api_min": 50,
        "api_max": inf,
        "kvis_min": -inf,
        "kvis_max": inf,
        'kvis_temp': 15
    },
    'Light Crude': {
        "api_min": 35,
        "api_max": 50,
        "kvis_min": -inf,
        "kvis_max": inf,
        'kvis_temp': 15
    },
    'Medium Crude': {
        "api_min": 20,
        "api_max": 35,
        "kvis_min": -inf,
        "kvis_max": inf,
        'kvis_temp': 15
    },
    'Heavy Crude': {
        "api_min": -inf,
        "api_max": 20,
        "kvis_min": -inf,
        "kvis_max": inf,
        'kvis_temp': 15
    },
    'Group V': {
        "api_min": -inf,
        "api_max": 10.0,
        "kvis_min": -inf,
        "kvis_max": inf,
        'kvis_temp': 15
    },
    'Heavy Fuel Oil': {
        "api_min": -inf,
        "api_max": 15.0,
        "kvis_min": 200,
        "kvis_max": inf,
        'kvis_temp': 50
    },

    # pretty much made this up ... non-newtonian
    'Bitumen': {
        "api_min": -inf,
        "api_max": 10,
        "kvis_min": 1000,
        "kvis_max": inf,
        'kvis_temp': 40
    },
    # I went through all the bitumen records that we have, and most of them do not have
    # kinematic viscosity measurements. However, they all have dynamic viscosity
    # measurements which were > 10^6 cP at 0C and > 10^5 cP at 15C.
    # I propose changing the criteria to reflect these measurements.

    #***** The following need more verification
    # Refined light products
    'No. 2 Fuel Oil': {
        "api_min": 30,
        "api_max": 39,
        "kvis_min": 2.5,
        "kvis_max": 4,
        'kvis_temp': 38
    },
    'Kerosene': {
        "api_min": 47.6,
        "api_max": 67.8,
        "kvis_min": -inf,
        "kvis_max": inf,
        'kvis_temp': 38
    },
    'Aviation Gas': {
        "api_min": 47.6,
        "api_max": 70.8,
        "kvis_min": -inf,
        "kvis_max": inf,
        'kvis_temp': 38
    },
    'Gasoline': {
        "api_min": 59.7,
        "api_max": 76.6,
        "kvis_min": -inf,
        "kvis_max": 2.5,
        'kvis_temp': 38
    },

    # Intermediate Fuel Oils
    'MDO': {
        "api_min": 30,
        "api_max": 42,
        "kvis_min": -inf,
        "kvis_max": 11,
        'kvis_temp': 40
    },
    'IFO': {
        "api_min": 15,
        "api_max": 30,
        "kvis_min": 4,
        "kvis_max": 200,
        'kvis_temp': 38
    },
    #IFO needs an additional limitation to not be tied to biodiesel
})

for label, synonyms in synonyms_for_labels.items():
    label_map.update({syn: label_map[label] for syn in synonyms})


[docs]def get_suggested_labels(oil): """ get the labels suggested for this oil :param oil: the oil object to get the labels for :type oil: Oil object """ labels = set() pt = oil.metadata.product_type # everything gets its product type as a label as well # unless it has no product type # if pt: # labels.add(pt) if pt == "Other": # we don't want any labels auto added for Other return sorted(labels) try: for label in types_to_labels.left[oil.metadata.product_type]: if is_label(oil, label): labels.add(label) except KeyError: pass # sorting so they'll be in consistent order return sorted(labels)
[docs]def add_labels_to_oil(oil): """ add labels to the passed in oil. this adds labels, but does not remove any existing ones :param oil: the oil object to add labels to :type oil: Oil object """ labels = set(oil.metadata.labels) for label in types_to_labels.left['oil.metadata.product_type']: if is_label(oil, label): labels.add(label) oil.metadata.labels = sorted(labels)
[docs]def is_label(oil, label): try: data = label_map[label] except KeyError: return False api = oil.metadata.API # check API: if ((data['api_min'] != -inf) or (data['api_max'] != inf)): if api is None: is_label = False else: is_label = True if data['api_min'] <= api < data['api_max'] else False else: is_label = True if is_label and ((data['kvis_min'] != -inf) or (data['kvis_max'] != inf)): # check viscosity limits try: KV = KinematicViscosity(oil) kvis = KV.at_temp(temp=data['kvis_temp'], kvis_units='cSt', temp_units='C') is_label = True if data['kvis_min'] <= kvis < data['kvis_max'] else False except (ZeroDivisionError, ValueError): # if it can't do this, we don't apply the label is_label = True return is_label
# def link_oil_to_labels(oil): # ''' # Here, we have a single oil and we would like to link it to one or more # labels based on its properties. # ''' # try: # labels = oil['metadata']['labels'] # except TypeError: # labels = oil.metadata.labels # except KeyError: # labels = [] # sample = OilEstimation(oil).get_sample() # if sample is None: # logger.warn('oil: {} has no fresh sample. Skipping Categorization.' # .format(oil['oil_id'])) # return # if is_crude_light(oil): # labels.extend(['Crude Oil', 'Light Crude']) # if is_crude_medium(oil): # labels.extend(['Crude Oil', 'Medium Crude']) # if is_crude_heavy(oil): # labels.extend(['Crude Oil', 'Heavy Crude']) # if is_refined_fuel_oil_1(oil, sample): # labels.extend(['Refined Product', # 'Light', # 'No. 1 Fuel Oil', # 'Gasoline', # 'Kerosene']) # if is_refined_fuel_oil_2(oil, sample): # labels.extend(['Refined Product', # 'No. 2 Fuel Oil', # 'Diesel', # 'Heating Oil']) # if is_refined_ifo(oil, sample): # labels.extend(['Refined Product', # 'Intermediate', # 'Fuel Oil']) # if is_refined_fuel_oil_6(oil, sample): # labels.extend(['Refined Product', # 'Heavy', # 'Fuel Oil 6', # 'HFO', # 'Bunker', # 'Group V']) # if is_generic(oil): # labels.extend(['Other', 'Generic']) # if len(labels) == 0: # labels.extend(['Other']) # try: # oil['metadata']['labels'] = labels # except TypeError: # oil.metadata.labels = labels # def is_crude(oil): # try: # return ('product_type' in oil['metadata'] and # oil['metadata']['product_type'] is not None and # oil['metadata']['product_type'].lower() == 'crude') # except KeyError: # return (hasattr(oil.metadata, 'product_type') and # oil.metadata.product_type is not None and # oil.metadata.product_type.lower() == 'crude') # def is_refined(oil): # try: # return ('product_type' in oil['metadata'] and # oil['metadata']['product_type'] is not None and # oil['metadata']['product_type'].lower() == 'refined') # except KeyError: # return (hasattr(oil.metadata, 'product_type') and # oil.metadata.product_type is not None and # oil.metadata.product_type.lower() == 'refined') # def api_min(oil, oil_api): # api = oil['metadata'].get('API', None) # return api is not None and api > oil_api # def api_max(oil, oil_api): # api = oil['metadata'].get('API', None) # return api is not None and api < oil_api # def is_crude_light(oil): # return is_crude(oil) and api_min(oil, 31.1) # def is_crude_medium(oil): # return is_crude(oil) and api_max(oil, 31.1) and api_min(oil, 22.3) # def is_crude_heavy(oil): # return is_crude(oil) and api_max(oil, 22.3) # def is_refined_light_products(oil): # ''' # Category Name: # - Light Products # Parent: # - Refined # Sample Oils: # - Cooper Basin Light Naphtha # - kerosene # - JP-4 # - avgas # Density Criteria: # - API >= 35 # Kinematic Viscosity Criteria: # - v > 0.0 cSt @ 38 degrees Celcius # ''' # raise NotImplementedError # def is_refined_fuel_oil_1(oil, sample): # ''' # Category Name: # - Fuel oil #1/gasoline/kerosene # Sample Oils: # - gasoline # - kerosene # - JP-4 # - avgas # Density Criteria: # - API >= 35 # Kinematic Viscosity Criteria: # - v <= 2.5 cSt @ 38 degrees Celcius # ''' # return (is_refined(oil) and # api_min(oil, 35.0) and # is_within_viscosity_range(sample, kvis_max=2.5)) # def is_refined_fuel_oil_2(oil, sample): # ''' # Category Name: # - Fuel oil #2/Diesel/Heating Oil # Sample Oils: # - Diesel # - Heating Oil # - No. 2 Distillate # Density Criteria: # - 30 <= API < 39 # Kinematic Viscosity Criteria: # - 2.5 < v <= 4.0 cSt @ 38 degrees Celcius # ''' # return (is_refined(oil) and # api_min(oil, 30.0) and # api_max(oil, 39.0) and # is_within_viscosity_range(sample, kvis_min=2.5, kvis_max=4.0)) # def is_refined_ifo(oil, sample): # ''' # Category Name: # - Intermediate Fuel Oil # Sample Oils: # - IFO 180 # - Fuel Oil #4 # - Marine Diesel # Density Criteria: # - 15 <= API < 30 # Kinematic Viscosity Criteria: # - 4.0 < v < 200.0 cSt @ 38 degrees Celcius # ''' # return (is_refined(oil) and # api_min(oil, 15.0) and # api_max(oil, 30.0) and # is_within_viscosity_range(sample, kvis_min=4.0, kvis_max=200.0)) # def is_refined_fuel_oil_6(oil, sample): # ''' # Category Name: # - Fuel Oil #6/Bunker/Heavy Fuel Oil/Group V # Sample Oils: # - Bunker C # - Residual Oil # Density Criteria: # - API < 15 # Kinematic Viscosity Criteria: # - 200.0 <= v cSt @ 50 degrees Celcius # ''' # return (is_refined(oil) and # api_max(oil, 15.0) and # is_within_viscosity_range(sample, kvis_min=200.0)) # def is_generic(oil): # ''' # Category Name: # - Other->Generic # Criteria: # - Any oils that have been generically generated. These are found # in the OilLibTest data file. Basically these oils have a name # that is prefixed with '*GENERIC'. # ''' # try: # ret = oil['metadata'].get('name', None) # except AttributeError: # ret = oil.metadata.name # if ret is not None: # return ret.startswith('*GENERIC') # else: # return None # def is_within_viscosity_range(oil_sample, kvis_min=None, kvis_max=None): # category_temp = 273.15 + 38 # viscosity = oil_sample.kvis_at_temp(category_temp) # if viscosity is None: # return False # if kvis_min is not None and kvis_max is not None: # return (viscosity > kvis_min) and (viscosity <= kvis_max) # elif kvis_min is not None: # return viscosity > kvis_min # elif kvis_max is not None: # return viscosity <= kvis_max # else: # return True # Data from: https://www.sigma2c.com/16api_gravity.html # Specific Gravity API # Naphtha light 0.66-0.70 # Naphtha medium 0.70-0.75 # Naphtha heavy 0.75-0.80 # Crude oil 0.80-0.97 # Aviation gasoline 0.70-0.78....47.6--70.6 # Kerosene 0.71-0.79 47.6--67.8 # Gasoline 0.68-0.74 59.7--76.6 # Gas oil 0.78-0.86 33.0--49.9 # Diesel oil 0.82-0.90 25.7--41.1 # Lubricating oil 0.82-0.92 # Fuel oil 0.92-0.99 # Asphaslitc bitumen 1.00-1.10