# This file is part of OpenDrift.
#
# OpenDrift is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 2
#
# OpenDrift is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with OpenDrift.  If not, see <https://www.gnu.org/licenses/>.
#
# Copyright 2021, Gaute Hope, MET Norway
"""
Interface to the ADIOS oil database.
"""
import logging
logger = logging.getLogger(__name__)
import numpy as np
from typing import List
from adios_db.models.oil.oil import Oil as AdiosOil
from adios_db.computation import gnome_oil
from adios_db.computation import physical_properties
from adios_db.computation.estimations import oil_water_surface_tension_from_api
[docs]
class NotFullOil(Exception):
    pass 
[docs]
def __require_gnome_oil__(f):
    def w(self, *args, **kwargs):
        if self.gnome_oil is None:
            raise NotFullOil()
        return f(self, *args, **kwargs)
    return w 
[docs]
class OpendriftOil:
    id: str
    type: str
    name: str
    API: float
    gnome_suitable: bool
    labels: List[str]
    location: str
    model_completeness: float
    product_type: str
    sample_date: str
    data: dict
    oil: AdiosOil
    gnome_oil: dict
    def __init__(self, o):
        self.data = o
        data = o
        meta = o['metadata']
        self.id = o['oil_id']
        self.name = o['metadata']['name']
        #data = o['data']
        #meta = data['attributes']['metadata']
        #self.id = data['_id']
        #self.name = meta['name']
        logger.debug(f'Parsing Oil: {self.id} / {self.name}')
        #self.oil = AdiosOil.from_py_json(data['attributes'])
        self.oil = AdiosOil.from_py_json(data)
        self.oil.validate()
        if not self.oil.metadata.gnome_suitable:
            logger.error(f'{self.id} / {self.name}: is not GNOME suitable')
        else:
            self.gnome_oil = gnome_oil.make_gnome_oil(self.oil)
[docs]
    def __repr__(self):
        return f"[<adios.Oil> {self.id}] {self.name}" 
[docs]
    def json(self):
        import json
        return json.dumps(self.data) 
[docs]
    def make_full(self) -> 'OpendriftOil':
        return self 
[docs]
    def valid(self):
        """
        Check whether this oil can be used in Opendrift simulations.
        """
        return self.oil.metadata.gnome_suitable 
[docs]
    @__require_gnome_oil__
    def density_at_temp(self, t, unit='K') -> float:
        """
        Return density at temperature (in Kelvin by default).
        """
        return physical_properties.Density(self.oil).at_temp(t, unit) 
[docs]
    @__require_gnome_oil__
    def kvis_at_temp(self, t, unit='K') -> float:
        return physical_properties.KinematicViscosity(self.oil).at_temp(
            t, temp_units=unit) 
    @property
    @__require_gnome_oil__
    def mass_fraction(self) -> np.ndarray:
        return np.asarray(self.gnome_oil['mass_fraction'])
[docs]
    @__require_gnome_oil__
    def oil_water_surface_tension(self) -> float:
        return oil_water_surface_tension_from_api(self.gnome_oil['api']) 
    @property
    @__require_gnome_oil__
    def bulltime(self) -> float:
        bulltime = self.gnome_oil['bullwinkle_time']
        if bulltime is None:
            return -999.  # legacy from old oil_library
        else:
            return bulltime
    @property
    @__require_gnome_oil__
    def bullwinkle(self) -> float:
        return self.gnome_oil['bullwinkle_fraction']
    @property
    @__require_gnome_oil__
    def emulsion_water_fraction_max(self) -> float:
        return self.gnome_oil['emulsion_water_fraction_max']
[docs]
    @__require_gnome_oil__
    def vapor_pressure(self, temp) -> float:
        '''
        Calculate vapor pressure. This method is taken from the old oil_library.
        Args:
            temp: temperature in Kelvin.
        Returns:
            Array of vapor pressures for each component. Pascal.
        '''
        atmos_pressure = 101325.0
        boiling_point = np.asarray(self.gnome_oil['boiling_point'])
        D_Zb = 0.97
        R_cal = 1.987  # calories
        D_S = 8.75 + 1.987 * np.log(boiling_point)
        C_2i = 0.19 * boiling_point - 18
        var = 1. / (boiling_point - C_2i) - 1. / (temp - C_2i)
        ln_Pi_Po = (D_S * (boiling_point - C_2i)**2 /
                    (D_Zb * R_cal * boiling_point) * var)
        Pi = np.exp(ln_Pi_Po) * atmos_pressure
        return Pi 
    @property
    @__require_gnome_oil__
    def molecular_weight(self):
        return np.asarray(self.gnome_oil['molecular_weight'])
    @property
    @__require_gnome_oil__
    def k0y(self) -> float:
        return 2.024e-06  # Constant for all oils, should be checked 
        #return self.gnome_oil['k0y']