From e546c89ec1ebebd428afde319d4c7c9ac011aff6 Mon Sep 17 00:00:00 2001 From: Knut-Frode Dagestad Date: Mon, 18 Dec 2023 17:33:10 +0100 Subject: [PATCH 1/2] values input to set_config can now be a dictionary, where keys are appended to main key. This allows for multiple config settings with a single call, e.g. several constan environment parameters --- opendrift/config.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/opendrift/config.py b/opendrift/config.py index 0b53dc69e..904bde37a 100644 --- a/opendrift/config.py +++ b/opendrift/config.py @@ -60,6 +60,11 @@ def get_configspec(self, prefix='', level=[1, 2, 3]): return configspec def set_config(self, key, value): + if isinstance(value, dict): # Recursive call with items in dictionary + for subkey,subvalue in value.items(): + self.set_config(f'{key}:{subkey}', subvalue) + logger.info(f'set_config(\'{key}:{subkey}\', {subvalue})') + return if not key in self._config: self.list_config() raise ValueError('No config setting named %s' % key) From 2bca199ffd33a93f2d135894d577dacd0b6bf51a Mon Sep 17 00:00:00 2001 From: Knut-Frode Dagestad Date: Mon, 18 Dec 2023 17:34:07 +0100 Subject: [PATCH 2/2] For Norwegian oils, max water fractions from Sintef (new json file) will override max value from NOAA. --- .../adios/get_max_water_fraction_for_oils.py | 198 ++++++++++++++++++ .../openoil/adios/max_water_fraction.json | 1 + opendrift/models/openoil/openoil.py | 90 ++++---- tests/models/openoil/test_adios_oil.py | 29 +-- tests/models/test_physics.py | 4 +- 5 files changed, 272 insertions(+), 50 deletions(-) create mode 100644 opendrift/models/openoil/adios/get_max_water_fraction_for_oils.py create mode 100644 opendrift/models/openoil/adios/max_water_fraction.json diff --git a/opendrift/models/openoil/adios/get_max_water_fraction_for_oils.py b/opendrift/models/openoil/adios/get_max_water_fraction_for_oils.py new file mode 100644 index 000000000..c3b84732c --- /dev/null +++ b/opendrift/models/openoil/adios/get_max_water_fraction_for_oils.py @@ -0,0 +1,198 @@ +def get_water_fractions(): + import difflib + import json + import numpy as np + import matplotlib.pyplot as plt + import pandas as pd + from opendrift.models.openoil import OpenOil + + oot = OpenOil(location='Norway').oiltypes + print(oot) + + o = pd.read_excel('oljedatabase_alle-25.10.2023.xlsx') + oljer = o['Oljetype'].unique().astype(list) + oljer = oljer[0:-1] + print(oljer) + + if False: + for ot in oot: # Loop to generate suggestions for mapping + matches = difflib.get_close_matches(ot.title(), oljer, 1, cutoff=.5) + if len(matches) > 0: + matches = matches[0] + else: + matches = '' + print(f'\'{ot}\': \'{matches}\',') + + mapping = { + 'AASGARD A 2003': 'Åsgard 2003', + 'AASTA HANSTEEN BLEND 2020': 'Aasta Hansteen Blend 2020', + 'ALTA 2016': 'Alta 2016', + 'ALVE 2010': 'Alve 2014 *(2017)', + 'ALVE 2014': 'Alve 2014 *(2017)', + 'ALVHEIM KNELER 2009': 'Kneler (Alvheim) 2009', + 'ATLA KONDENSAT 2013': 'Atla kondensat 2013', + 'AVALDSNES 2012': 'Avaldsnes (Johan Sverdrup) 2012', + 'BALDER 2002': 'Balder Blend 2010', + 'BALDER BLEND 2010': 'Balder Blend 2010', + 'BOYLA CRUDE 2016': 'Bøyla 2016', + 'BRAGE 2013': 'Brage 2013', + 'BRASSE 2018': 'Brasse 2018', + 'BREAM 2011': 'Bream 2011', + 'BREIDABLIKK 2023': 'Breidablikk', + 'BRYNHILD CRUDE 2015': 'Brynhild 2015', + 'CAURUS 2011': 'Caurus 2011', + 'DRAUGEN 2008': 'Draugen 2008', + 'DRIVIS 2017': 'Drivis 2017', + 'DUGONG 2022': 'Dugong 2022', + 'DUVA 2021': 'Duva 2021', + 'DVALIN 2020': 'Dvalin kondensat 2020', + 'EKOFISK 2002': 'Ekofisk blend 2002', + 'EKOFISK BLEND 2002': 'Ekofisk blend 2002', + 'EKOFISK BLEND 2015': 'Ekofisk J 2002', + 'EKOFISK J 2015': 'Ekofisk J 2015', + 'ELDFISK 2002': 'Eldfisk B 2015', + 'ELDFISK B 2015': 'Eldfisk B 2015', + 'ELDFISK BLEND 2015': 'Eldfisk B 2015', + 'ELDFISK KOMPLEKS 2015': 'Eldfisk Kompleks 2015', + 'ELLI 1999': 'Elli 1999', + 'ELLI SOUTH 1999': 'Elli South 1999', + 'EMBLA 2002': 'Embla 2002', + 'FENJA (PIL) 2015': 'Fenja (Pil) 2015', + 'FOGELBERG CONDENSATE 2021': 'Fogelberg Kondensat 2021', + 'FORSETI 2002': 'Forseti 2002', + 'FOSSEKALL 2013': 'Fossekall 2013', + 'FRAM 2013': 'Fram 2013', + 'FROSK 2020': 'Frosk 2020', + 'FROY 1996': 'Frøy 1996', + 'GARANTIANA 2013': 'Garantiana 2013', + 'GAUPE 2011': 'Gaupe 2011', + 'GINA KROG CRUDE 2018': 'Gina Krog 2018', + 'GJOA 2011': 'Gjøa 2011', + 'GLITNE 2002': 'Glitne 2002', + 'GOLIAT BLEND 50/50 2008': 'Goliat Blend 2008', + 'GOLIAT BLEND 70/30 2008': 'Goliat Blend 2008', + 'GOLIAT KOBBE 2008': 'Goliat Kobbe 2008 *(2010)', + 'GOLIAT REALGRUNNEN 2001': 'Goliat Realgrunnen 2003', + 'GOLIAT REALGRUNNEN 2008': 'Goliat Realgrunnen 2003', + 'GRANE 1997': 'Grane 1997', + 'GROSBEAK 2012': 'Grosbeak 2012', + 'GUDRUN 2012': 'Gudrun 2012', + 'GUDRUN 2019': 'Gudrun 2019', + 'GULLFAKS A BLEND 2010': 'Gullfaks A 2010', + 'GULLFAKS C BLEND 2010': 'Gullfaks C 2010', + 'GULLFAKS SOR 1996': 'Gullfaks Sør 1996', + 'GYDA 2002': 'Gyda 2002', + 'HAVIS 2013': 'Havis 2013', + 'HEIDRUN AaRE 2004': 'Heidrun Åre 2004', + 'HEIDRUN EXPORT BLEND 2004': 'Heidrun Åre 2004', + 'HEIDRUN TILJE 2004': 'Heidrun Åre 2004', + 'HULDRA KONDENSAT 1998': 'Huldra 1998', + 'IRIS CONDENSATE 2020': 'Iris 2020', + 'IVAR AASEN 2012': 'Draupne (Ivar Aasen) 2012', + 'JORDBAER 2011': 'Jordbær 2011', + 'KRISTIN 2006': 'Kristin 2006', + 'KVITEBJORN 2009': 'Kvitebjørn 2009', + 'KVITEBJORN 2019': 'Kvitebjørn 2019', + 'LAVRANS 1997': 'Lavrans 1997', + 'LILLE PRINSEN 2022': 'Lille Prinsen 2022', + 'LILLEFRIGG KONDENSAT 1996': 'Lillefrigg 1996', + 'LINERLE 2005': 'Linerle 2005', + 'LUNO 2011': 'Luno (Edvard Grieg) 2011', + 'LUNO II 2014': 'Solveig (Luno II) 2014', + 'MARIA 2013': 'Maria 2013', + 'MARTIN LINGE CONDENSATE 2016': 'Martin Linge kondensat 2016', + 'MARTIN LINGE CRUDE 2016': 'Martin Linge olje 2016', + 'MARULK 2014': 'Marulk 2014', + 'MORVIN 2008': 'Morvin 2008', + 'NJORD 1997': 'Njord 1997* (2023)', + 'NJORD 2002': 'Njord 2003', + 'NJORD 2003': 'Njord 2003', + 'NORNE 2010': 'Norne 2018', + 'NORNE BLEND 2010': 'Norne Blend 2010', + 'NORNE CRUDE 2017': 'Norne 2018', + 'ODA 2019': 'Oda 2019', + 'ORMEN LANGE KONDENSAT 2008': 'Ormen Lange 2008', + 'OSEBERG A 2013': 'Oseberg A 2013', + 'OSEBERG BLEND 2007': 'Oseberg Blend 2007', + 'OSEBERG C 1995': 'Oseberg C 1999', + 'OSEBERG C 2013': 'Oseberg A 2013', + 'OSEBERG OST 1998': 'Oseberg C 1999', + 'OSEBERG OST 2013': 'Oseberg Øst 2013', + 'OSEBERG SOR 2000': 'Oseberg Sør 2001', + 'OSEBERG SOR 2013': 'Oseberg Sør 2013', + 'OSELVAR 2012': 'Oselvar 2012', + 'RINGHORNE 2002': 'Ringhorne 2002', + 'SF NORD BRENT 2021': 'SF Nord Brent 2021', + 'SKARFJELL 2014': 'Skarfjell 2014', + 'SKARV 2004': 'Skarv olje 2004', + 'SKARV KONDENSAT 2014': 'Skarv kondensat 2014', + 'SKOGUL 2020': 'Skogul 2020', + 'SKRUGARD 2012': 'Skrugard 2012', + 'SLEIPNER KONDENSAT 2002': 'leipner 2002', + 'SLEIPNER VEST 1998': 'Sleipner Vest 1998', + 'SMORBUKK 2003': 'Smørbukk Sør 2003', + 'SMORBUKK KONDENSAT 2003': 'Smørbukk kondensat 2003', + 'SMORBUKK SOR 2003': 'Smørbukk Sør 2003', + 'SNOHVIT KONDENSAT 2001': 'Snøhvit 2001', + 'SNORRE B 2004': 'Snorre B 2004', + 'SNORRE TLP 2004': 'Snorre TLP 2004', + 'STAER 2010': 'Stær 2010', + 'STATFJORD A 2001': 'Statfjord A 2001', + 'STATFJORD B 2001': 'Statfjord A 2001', + 'STATFJORD C 2001': 'Statfjord C 2001 *(2021)', + 'SVALE 2010': 'Svale 2010', + 'SVALIN 2014': 'Svalin 2014', + 'SYGNA BRENT 2021': 'Sygna Brent 2021', + 'TAMBAR 2002': 'Tambar 2002', + 'TAU 1999': 'Tau 1999', + 'TOR 2002': 'Tambar 2002', + 'TOR II 2022': 'TOR ll 2022', + 'TORDIS 2002': 'Iris 2020', + 'TRESTAKK 2008': 'Trestakk 2008', + 'TROLL, STATOIL': 'Troll 1999', + 'TRYM KONDENSAT 2011': 'Trym 2011', + 'TYRIHANS NORD 2004': 'Tyrihans Nord 2004', + 'TYRIHANS SOR 2004': 'Tyrihans Sør 2004', + 'ULA 1999': 'Ula 1999', + 'UTGARD CONDENSATE 2021': 'Utgard kondensat 2021', + 'VALE 2001': 'Vale 2001', + 'VALE 2014': 'Vale 2014', + 'VALHALL 2002': 'Valhall 2002', + 'VALHALL 2021': 'Valhall 2021', + 'VARG 2000': 'Varg 2000', + 'VEGA CONDENSATE 2015': 'Vega 2016', + 'VESLEFRIKK 2012': 'Veslefrikk 2012', + 'VILJE 2009': 'Vilje 2009', + 'VISUND 2009': 'Visund 2009', + 'VISUND CRUDE OIL 2020': 'Visund Crude Oil 2020', + 'VISUND SOR CONDENSATE 2020': 'Visund Sør Kondensat 2020', + 'VOLUND 2010': 'Volund 2010', + 'VOLVE 2006': 'Volve 2006', + 'WISTING 2015': 'Wisting 2015', + 'WISTING CENTRAL 2017': 'Wisting Central 2017', + 'YME 2023': 'Yme 2023' + } + + + #o = o.loc[o['Oljetype'] == 'Yme 2023'] + print(o) + print(o.columns) + print(o.groupby(['Oljetype', 'Temperatur (°C)'])['Vanninnhold'].max().to_string()) + + oilmax = { # Some known limits not in NOFO excel file + 'MARINE GAS OIL 500 ppm S 2017': + {'temperatures': [15], 'max_water_fraction': [.1]}, + 'OFELIA 2023': + {'temperatures': [5, 15], 'max_water_fraction': [.4, .7]}, + } + + for ot in mapping: + d = o.loc[o['Oljetype'] == mapping[ot]].groupby(['Temperatur (°C)'])['Vanninnhold'].max() + oilmax[ot] = {'temperatures': list(d.keys().values), + 'max_water_fraction': list(d.values)} + print(oilmax) + with open('max_water_fraction.json', 'w') as json_file: + json.dump(oilmax , json_file) + +if __name__ == '__main__': + get_water_fractions() diff --git a/opendrift/models/openoil/adios/max_water_fraction.json b/opendrift/models/openoil/adios/max_water_fraction.json new file mode 100644 index 000000000..48c09e84b --- /dev/null +++ b/opendrift/models/openoil/adios/max_water_fraction.json @@ -0,0 +1 @@ +{"MARINE GAS OIL 500 ppm S 2017": {"temperatures": [15], "max_water_fraction": [0.1]}, "OFELIA 2023": {"temperatures": [5, 15], "max_water_fraction": [0.4, 0.7]}, "AASGARD A 2003": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.46, 0.51]}, "AASTA HANSTEEN BLEND 2020": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.0, 0.0]}, "ALTA 2016": {"temperatures": [5.0, 10.0], "max_water_fraction": [0.8, 0.8]}, "ALVE 2010": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.89, 0.89]}, "ALVE 2014": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.89, 0.89]}, "ALVHEIM KNELER 2009": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.66, 0.78]}, "ATLA KONDENSAT 2013": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.0, 0.0]}, "AVALDSNES 2012": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.63, 0.68]}, "BALDER 2002": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.64, 0.7]}, "BALDER BLEND 2010": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.64, 0.7]}, "BOYLA CRUDE 2016": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.72, 0.77]}, "BRAGE 2013": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.8, 0.8]}, "BRASSE 2018": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.756, 0.778]}, "BREAM 2011": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.61, 0.75]}, "BREIDABLIKK 2023": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.621, 0.726]}, "BRYNHILD CRUDE 2015": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.71, 0.75]}, "CAURUS 2011": {"temperatures": [5.0, 10.0], "max_water_fraction": [0.37, 0.37]}, "DRAUGEN 2008": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.7, 0.7]}, "DRIVIS 2017": {"temperatures": [5.0, 10.0], "max_water_fraction": [0.79, 0.8]}, "DUGONG 2022": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.787, 0.799]}, "DUVA 2021": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.317, 0.648]}, "DVALIN 2020": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.66, 0.758]}, "EKOFISK 2002": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.7, 0.83]}, "EKOFISK BLEND 2002": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.7, 0.83]}, "EKOFISK BLEND 2015": {"temperatures": [], "max_water_fraction": []}, "EKOFISK J 2015": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.75, 0.78]}, "ELDFISK 2002": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.73, 0.79]}, "ELDFISK B 2015": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.73, 0.79]}, "ELDFISK BLEND 2015": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.73, 0.79]}, "ELDFISK KOMPLEKS 2015": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.77, 0.8]}, "ELLI 1999": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.5, 0.6]}, "ELLI SOUTH 1999": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.5, 0.6]}, "EMBLA 2002": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.62, 0.67]}, "FENJA (PIL) 2015": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.477, 0.75]}, "FOGELBERG CONDENSATE 2021": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.725, 0.755]}, "FORSETI 2002": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.7, 0.77]}, "FOSSEKALL 2013": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.45, 0.82]}, "FRAM 2013": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.8, 0.8]}, "FROSK 2020": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.703, 0.8]}, "FROY 1996": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.64, 0.65]}, "GARANTIANA 2013": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.38, 0.7]}, "GAUPE 2011": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.75, 0.75]}, "GINA KROG CRUDE 2018": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.696, 0.686]}, "GJOA 2011": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.73, 0.54]}, "GLITNE 2002": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.56, 0.75]}, "GOLIAT BLEND 50/50 2008": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.8, 0.8]}, "GOLIAT BLEND 70/30 2008": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.8, 0.8]}, "GOLIAT KOBBE 2008": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.75, 0.75]}, "GOLIAT REALGRUNNEN 2001": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.7, 0.7]}, "GOLIAT REALGRUNNEN 2008": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.7, 0.7]}, "GRANE 1997": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.56, 0.65]}, "GROSBEAK 2012": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.75, 0.77]}, "GUDRUN 2012": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.7, 0.7]}, "GUDRUN 2019": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.75, 0.8]}, "GULLFAKS A BLEND 2010": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.8, 0.8]}, "GULLFAKS C BLEND 2010": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.74, 0.78]}, "GULLFAKS SOR 1996": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.8, 0.8]}, "GYDA 2002": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.8, 0.8]}, "HAVIS 2013": {"temperatures": [5.0, 10.0], "max_water_fraction": [0.72, 0.74]}, "HEIDRUN AaRE 2004": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.735, 0.75]}, "HEIDRUN EXPORT BLEND 2004": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.735, 0.75]}, "HEIDRUN TILJE 2004": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.735, 0.75]}, "HULDRA KONDENSAT 1998": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.4, 0.4]}, "IRIS CONDENSATE 2020": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.48, 0.529]}, "IVAR AASEN 2012": {"temperatures": [5.0, 10.0], "max_water_fraction": [0.8, 0.8]}, "JORDBAER 2011": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.84, 0.84]}, "KRISTIN 2006": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.7, 0.71]}, "KVITEBJORN 2009": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.2, 0.4]}, "KVITEBJORN 2019": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.0, 0.0]}, "LAVRANS 1997": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.0, 0.0]}, "LILLE PRINSEN 2022": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.726, 0.756]}, "LILLEFRIGG KONDENSAT 1996": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.69, 0.7]}, "LINERLE 2005": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.57, 0.69]}, "LUNO 2011": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.74, 0.77]}, "LUNO II 2014": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.68, 0.73]}, "MARIA 2013": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.8, 0.8]}, "MARTIN LINGE CONDENSATE 2016": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.17, 0.19]}, "MARTIN LINGE CRUDE 2016": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.72, 0.78]}, "MARULK 2014": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.05, 0.05]}, "MORVIN 2008": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.74, 0.75]}, "NJORD 1997": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.7, 0.7]}, "NJORD 2002": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.8, 0.8]}, "NJORD 2003": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.8, 0.8]}, "NORNE 2010": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.5, 0.79]}, "NORNE BLEND 2010": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.735, 0.8]}, "NORNE CRUDE 2017": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.5, 0.79]}, "ODA 2019": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.789, 0.8]}, "ORMEN LANGE KONDENSAT 2008": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.0, 0.0]}, "OSEBERG A 2013": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.72, 0.76]}, "OSEBERG BLEND 2007": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.78, 0.79]}, "OSEBERG C 1995": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.77, 0.79]}, "OSEBERG C 2013": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.72, 0.76]}, "OSEBERG OST 1998": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.77, 0.79]}, "OSEBERG OST 2013": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.71, 0.79]}, "OSEBERG SOR 2000": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.6, 0.65]}, "OSEBERG SOR 2013": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.7, 0.8]}, "OSELVAR 2012": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.72, 0.74]}, "RINGHORNE 2002": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.73, 0.8]}, "SF NORD BRENT 2021": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.734, 0.778]}, "SKARFJELL 2014": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.728, 0.8]}, "SKARV 2004": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.7, 0.7]}, "SKARV KONDENSAT 2014": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.0, 0.0]}, "SKOGUL 2020": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.645, 0.73]}, "SKRUGARD 2012": {"temperatures": [5.0, 10.0], "max_water_fraction": [0.79, 0.8]}, "SLEIPNER KONDENSAT 2002": {"temperatures": [], "max_water_fraction": []}, "SLEIPNER VEST 1998": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.59, 0.6]}, "SMORBUKK 2003": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.8, 0.8]}, "SMORBUKK KONDENSAT 2003": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.29, 0.32]}, "SMORBUKK SOR 2003": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.8, 0.8]}, "SNOHVIT KONDENSAT 2001": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.0, 0.0]}, "SNORRE B 2004": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.79, 0.8]}, "SNORRE TLP 2004": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.8, 0.8]}, "STAER 2010": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.731, 0.8]}, "STATFJORD A 2001": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.66, 0.76]}, "STATFJORD B 2001": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.66, 0.76]}, "STATFJORD C 2001": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.7, 0.7]}, "SVALE 2010": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.053, 0.75]}, "SVALIN 2014": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.75, 0.81]}, "SYGNA BRENT 2021": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.732, 0.782]}, "TAMBAR 2002": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.8, 0.8]}, "TAU 1999": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.5, 0.6]}, "TOR 2002": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.8, 0.8]}, "TOR II 2022": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.752, 0.8]}, "TORDIS 2002": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.48, 0.529]}, "TRESTAKK 2008": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.8, 0.8]}, "TROLL, STATOIL": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.74, 0.75]}, "TRYM KONDENSAT 2011": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.0, 0.0]}, "TYRIHANS NORD 2004": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.74, 0.8]}, "TYRIHANS SOR 2004": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.62, 0.74]}, "ULA 1999": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.79, 0.81]}, "UTGARD CONDENSATE 2021": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.0, 0.0]}, "VALE 2001": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.7, 0.6]}, "VALE 2014": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.69, 0.75]}, "VALHALL 2002": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.69, 0.8]}, "VALHALL 2021": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.72, 0.75]}, "VARG 2000": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.5, 0.6]}, "VEGA CONDENSATE 2015": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.8, 0.8]}, "VESLEFRIKK 2012": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.79, 0.8]}, "VILJE 2009": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.72, 0.77]}, "VISUND 2009": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.8, 0.8]}, "VISUND CRUDE OIL 2020": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.8, 0.8]}, "VISUND SOR CONDENSATE 2020": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.278, 0.287]}, "VOLUND 2010": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.76, 0.49]}, "VOLVE 2006": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.62, 0.75]}, "WISTING 2015": {"temperatures": [1.0, 5.0], "max_water_fraction": [0.57, 0.57]}, "WISTING CENTRAL 2017": {"temperatures": [2.0, 5.0], "max_water_fraction": [0.6, 0.59]}, "YME 2023": {"temperatures": [5.0, 15.0], "max_water_fraction": [0.731, 0.798]}} diff --git a/opendrift/models/openoil/openoil.py b/opendrift/models/openoil/openoil.py index fdc3e6da2..2e6a867b9 100644 --- a/opendrift/models/openoil/openoil.py +++ b/opendrift/models/openoil/openoil.py @@ -84,6 +84,8 @@ import pyproj import matplotlib.pyplot as plt import logging +import json +from importlib import resources logger = logging.getLogger(__name__) @@ -313,12 +315,6 @@ class OpenOil(OceanDrift): 'SLEIPNER CONDENSATE, STATOIL', 'STATFJORD BLEND, STATOIL', 'VARG, STATOIL'] - # Workaround as ADIOS oil library uses - # max water fraction of 0.9 for all crude oils - max_water_fraction = { - 'MARINE GAS OIL 500 ppm S 2017': 0.1, - 'FENJA (PIL) 2015': .75 - } def __init__(self, weathering_model='noaa', *args, **kwargs): self.oil_weathering_model = weathering_model @@ -335,6 +331,9 @@ def __init__(self, weathering_model='noaa', *args, **kwargs): other_oiltypes = [o for o in self.oiltypes if o[0:7] != 'GENERIC'] self.oiltypes = sorted([o for o in generic_oiltypes]) + sorted([o for o in other_oiltypes]) self.oiltypes = [ot for ot in self.oiltypes if ot not in self.duplicate_oils] + + # For Norwegian oils, max water fraction from Sintef is overriding NOAA value + self.max_water_fraction = None else: raise ValueError('Weathering model unknown: ' + weathering_model) @@ -653,6 +652,21 @@ def prepare_run(self): self.oiltype.oil_water_surface_tension() logger.info('Oil-water surface tension is %f Nm' % self.oil_water_interfacial_tension) + try: + max_water_fractions = json.loads( + resources.read_text('opendrift.models.openoil.adios', 'max_water_fraction.json')) + if self.oil_name in max_water_fractions: + self.max_water_fraction = max_water_fractions[self.oil_name] + T = self.max_water_fraction['temperatures'] + wf = self.max_water_fraction['max_water_fraction'] + logger.info(f'Using max water fractions {wf} for temperatures {T} for oiltype {self.oil_name}') + logger.info('Corresponding max water fraction from GNOME is ' + f'{self.oiltype.gnome_oil["emulsion_water_fraction_max"]}') + else: + logger.info(f'Max water fraction not available for {self.oil_name}, using default') + except Exception as e: + logger.warning('Could not load max water content file') + print(e) def oil_weathering_noaa(self): '''Oil weathering scheme adopted from NOAA PyGNOME model: @@ -792,28 +806,12 @@ def evaporation_noaa(self): def emulsification_noaa(self): ############################################# - # Emulsification (surface only?) + # Emulsification (surface only) ############################################# logger.debug(' Calculating emulsification - NOAA') emul_time = self.oiltype.bulltime emul_constant = self.oiltype.bullwinkle - # max water content fraction - get from database - Y_max = self.oiltype.emulsion_water_fraction_max - if self.oil_name in self.max_water_fraction: - max_water_fraction = self.max_water_fraction[self.oil_name] - logger.debug( - 'Overriding max water fraxtion with value %f instead of default %f' - % (max_water_fraction, Y_max)) - Y_max = max_water_fraction - # emulsion - if Y_max <= 0: - logger.debug('Oil does not emulsify, returning.') - return - # Constants for droplets - drop_min = 1.0e-6 - drop_max = 1.0e-5 - S_max = (6. / drop_min) * (Y_max / (1.0 - Y_max)) - S_min = (6. / drop_max) * (Y_max / (1.0 - Y_max)) + # Emulsify... fraction_evaporated = self.elements.mass_evaporated / ( self.elements.mass_evaporated + self.elements.mass_oil) @@ -827,6 +825,34 @@ def emulsification_noaa(self): logger.debug(' Emulsification not yet started') return + # max water content fraction - get from database + Y_max = np.atleast_1d(self.oiltype.emulsion_water_fraction_max) + if self.max_water_fraction is not None: + wf = self.max_water_fraction['max_water_fraction'] + wft = self.max_water_fraction['temperatures'] + if len(wf) == 1: + wf = [wf, wf] + wft = [wft, wft] + swt = self.environment.sea_water_temperature[start_emulsion] - 273.15 # to Celcius + weights = (wft[1] - swt) / (wft[1] - wft[0]) + weights[swt>wft[1]] = 0 + weights[swt<=wft[0]] = 1 + max_water_fraction_sintef = weights*wf[0] + (1-weights)*wf[1] + + if (Y_max - max_water_fraction_sintef).min() > 0: + logger.debug( + f'Overriding max water fraction {Y_max} with linear fit to SINTEF max values:' + f' T: {wft}, Fraction: {wf}') + Y_max = np.array(np.minimum(Y_max, max_water_fraction_sintef)) + # emulsion + if Y_max.max() <= 0: + logger.debug('Oil does not emulsify, returning.') + return + # Constants for droplets + drop_min = 1.0e-6 + drop_max = 1.0e-5 + S_max = (6. / drop_min) * (Y_max / (1.0 - Y_max)) + S_min = (6. / drop_max) * (Y_max / (1.0 - Y_max)) if self.oiltype.bulltime > 0: # User has set value start_time = self.oiltype.bulltime * np.ones(len(start_emulsion)) else: @@ -835,23 +861,17 @@ def emulsification_noaa(self): start_time[self.elements.age_seconds[start_emulsion] >= 0] = self.elements.bulltime[start_emulsion] # Update droplet interfacial area - k_emul = noaa.water_uptake_coefficient( - self.oiltype, - self.wind_speed()[start_emulsion]) + k_emul = noaa.water_uptake_coefficient(self.oiltype, self.wind_speed()[start_emulsion]) self.elements.interfacial_area[start_emulsion] = \ self.elements.interfacial_area[start_emulsion] + \ - (k_emul*self.time_step.total_seconds()* - np.exp((-k_emul/S_max)*( + (k_emul*self.time_step.total_seconds()* np.exp((-k_emul/S_max)*( self.elements.age_seconds[start_emulsion] - start_time))) - self.elements.interfacial_area[ - self.elements.interfacial_area > S_max] = S_max + self.elements.interfacial_area[start_emulsion] = np.minimum(self.elements.interfacial_area[start_emulsion], S_max) # Update water fraction self.elements.water_fraction[start_emulsion] = ( - self.elements.interfacial_area[start_emulsion] * drop_max / - (6.0 + + self.elements.interfacial_area[start_emulsion] * drop_max / (6.0 + (self.elements.interfacial_area[start_emulsion] * drop_max))) - self.elements.water_fraction[self.elements.interfacial_area >= ( - (6.0 / drop_max) * (Y_max / (1.0 - Y_max)))] = Y_max + self.elements.water_fraction[start_emulsion] = np.minimum(self.elements.water_fraction[start_emulsion], Y_max) def update_terminal_velocity(self, Tprofiles=None, diff --git a/tests/models/openoil/test_adios_oil.py b/tests/models/openoil/test_adios_oil.py index be868c213..1e31ed163 100644 --- a/tests/models/openoil/test_adios_oil.py +++ b/tests/models/openoil/test_adios_oil.py @@ -13,20 +13,23 @@ def aasgard(): return f def test_max_water_fraction(): - from opendrift.models.openoil import OpenOil - oiltype='FENJA (PIL) 2015' - for wf, expected in zip([.5, .9], [.48, .84]): + # Check that max water fraction is saturated to max values from Sintef model + # Max water_fraction 0.317 for SST=0 from Sintef, for SST=10,20 from NOAA + for sst,expected_fraction in zip([0, 10, 20], [0.317, 0.443, 0.443]): o = OpenOil(loglevel=50) - o.max_water_fraction[oiltype] = wf - o.set_config('environment:constant:land_binary_mask', 0) - o.set_config('environment:constant:x_sea_water_velocity', 0) - o.set_config('environment:constant:y_sea_water_velocity', 0) - o.set_config('environment:constant:x_wind', 10) - o.set_config('environment:constant:y_wind', 10) - o.seed_elements(lon=0, lat=60, time=datetime.now(), number=1000, oiltype=oiltype) - o.run(duration=timedelta(hours=24)) - wfa = o.history['water_fraction'].mean() - assert np.isclose(wfa, expected, atol=.01) + o.set_config('environment:constant', + { + 'x_wind': 10, + 'y_wind': 0, + 'x_sea_water_velocity': 0, + 'y_sea_water_velocity': 0, + 'sea_water_temperature': sst, + 'land_binary_mask': 0 + }) + o.seed_elements(lon=3, lat=60, time=datetime.now(), number=100, + oil_type='DUVA 2021') + o.run(duration=timedelta(hours=6)) + assert np.isclose(o.elements.water_fraction.max(), expected_fraction, atol=.001) def test_open_aasgard(aasgard): print(aasgard) diff --git a/tests/models/test_physics.py b/tests/models/test_physics.py index 994f78e1d..c0d6fc1e9 100644 --- a/tests/models/test_physics.py +++ b/tests/models/test_physics.py @@ -157,7 +157,7 @@ def test_vertical_mixing_plantoil_windonly(self): o.run(duration=timedelta(hours=2), time_step_output=900, time_step=900) #o.plot_vertical_distribution() - self.assertAlmostEqual(o.elements.z.min(), -49.33, 1) + self.assertAlmostEqual(o.elements.z.min(), -49.52, 1) ####################################################### @@ -206,7 +206,7 @@ def test_verticalmixing_schemes(self): elif scheme == 'windspeed_Sundby1983': self.assertAlmostEqual(o.elements.z.min(), -51.75, 1) elif scheme == 'constant': - self.assertAlmostEqual(o.elements.z.min(), -3.62, 1) + self.assertAlmostEqual(o.elements.z.min(), -3.57, 1) def test_parameterised_stokes(self): o = OpenOil(loglevel=30)