Merge pull request #3188 from vkip/fix-time-vector-py
Fix time stamp output from time_vector.py so it is Ecl compatible
This commit is contained in:
commit
120ef99510
@ -1,6 +1,6 @@
|
||||
import datetime
|
||||
|
||||
from operator import attrgetter
|
||||
|
||||
try:
|
||||
from StringIO import StringIO
|
||||
except ImportError:
|
||||
@ -9,65 +9,71 @@ except ImportError:
|
||||
from opm.io.parser import Parser
|
||||
|
||||
# This is from the TimeMap.cpp implementation in opm
|
||||
ecl_month = {"JAN" : 1,
|
||||
"FEB" : 2,
|
||||
"MAR" : 3,
|
||||
"APR" : 4,
|
||||
"MAI" : 5,
|
||||
"MAY" : 5,
|
||||
"JUN" : 6,
|
||||
"JLY" : 7,
|
||||
"JUL" : 7,
|
||||
"AUG" : 8,
|
||||
"SEP" : 9,
|
||||
"OCT" : 10,
|
||||
"OKT" : 10,
|
||||
"NOV" : 11,
|
||||
"DEC" : 12,
|
||||
"DES" : 12}
|
||||
ecl_month = {
|
||||
"JAN": 1,
|
||||
"FEB": 2,
|
||||
"MAR": 3,
|
||||
"APR": 4,
|
||||
"MAI": 5,
|
||||
"MAY": 5,
|
||||
"JUN": 6,
|
||||
"JLY": 7,
|
||||
"JUL": 7,
|
||||
"AUG": 8,
|
||||
"SEP": 9,
|
||||
"OCT": 10,
|
||||
"OKT": 10,
|
||||
"NOV": 11,
|
||||
"DEC": 12,
|
||||
"DES": 12,
|
||||
}
|
||||
|
||||
inv_ecl_month = {
|
||||
1: "JAN",
|
||||
2: "FEB",
|
||||
3: "MAR",
|
||||
4: "APR",
|
||||
5: "MAY",
|
||||
6: "JUN",
|
||||
7: "JUL",
|
||||
8: "AUG",
|
||||
9: "SEP",
|
||||
10: "OCT",
|
||||
11: "NOV",
|
||||
12: "DEC",
|
||||
}
|
||||
|
||||
inv_ecl_month = {1 : "JAN",
|
||||
2 : "FEB",
|
||||
3 : "MAR",
|
||||
4 : "APR",
|
||||
5 : "MAY",
|
||||
6 : "JUN",
|
||||
7 : "JUL",
|
||||
8 : "AUG",
|
||||
9 : "SEP",
|
||||
10: "OCT",
|
||||
11 : "NOV",
|
||||
12 : "DEC"}
|
||||
|
||||
def _make_datetime(dates_record):
|
||||
day = dates_record[0].get_int(0)
|
||||
month = dates_record[1].get_str(0)
|
||||
year = dates_record[2].get_int(0)
|
||||
|
||||
|
||||
date_dt = datetime.datetime(year, ecl_month[month], day)
|
||||
if len(dates_record) < 4:
|
||||
return date_dt
|
||||
else:
|
||||
time_str = dates_record[3].get_str(0)
|
||||
time_list = time_str.split(':')
|
||||
time_list = time_str.split(":")
|
||||
hour = minute = second = microsecond = 0
|
||||
hour = int(time_list[0])
|
||||
if len(time_list)>1:
|
||||
if len(time_list) > 1:
|
||||
minute = int(time_list[1])
|
||||
if len(time_list)>2:
|
||||
sec_list = time_list[2].split('.')
|
||||
if len(time_list) > 2:
|
||||
sec_list = time_list[2].split(".")
|
||||
second = int(sec_list[0])
|
||||
if len(sec_list)>1:
|
||||
if len(sec_list) > 1:
|
||||
ms_str = sec_list[1].strip()
|
||||
npad = 6-len(ms_str)
|
||||
npad = 6 - len(ms_str)
|
||||
ms_str += "".join(["0" for i in range(npad)])
|
||||
microsecond = int(ms_str)
|
||||
|
||||
return datetime.datetime(year, ecl_month[month], day, hour, minute, second, microsecond)
|
||||
|
||||
return datetime.datetime(
|
||||
year, ecl_month[month], day, hour, minute, second, microsecond
|
||||
)
|
||||
|
||||
|
||||
class TimeStep(object):
|
||||
|
||||
def __init__(self, dt, keywords):
|
||||
"""The TimeStep class consist of a list of keywords and a corresponding date.
|
||||
|
||||
@ -84,7 +90,6 @@ class TimeStep(object):
|
||||
self.tstep = None
|
||||
self.is_start = False
|
||||
|
||||
|
||||
@classmethod
|
||||
def create_first(cls, dt):
|
||||
ts = cls(dt, [])
|
||||
@ -94,8 +99,6 @@ class TimeStep(object):
|
||||
def add_keyword(self, kw):
|
||||
self.keywords.append(kw)
|
||||
|
||||
|
||||
|
||||
def __len__(self):
|
||||
return len(self.keywords)
|
||||
|
||||
@ -105,7 +108,6 @@ class TimeStep(object):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def __str__(self):
|
||||
string = StringIO()
|
||||
|
||||
@ -114,12 +116,39 @@ class TimeStep(object):
|
||||
month = self.dt.month
|
||||
year = self.dt.year
|
||||
if not self.dt.time():
|
||||
string.write("DATES\n {day} '{month}' {year}/\n/\n\n".format( day=day, month = inv_ecl_month[month], year=year))
|
||||
string.write(
|
||||
"DATES\n {day} '{month}' {year}/\n/\n\n".format(
|
||||
day=day, month=inv_ecl_month[month], year=year
|
||||
)
|
||||
)
|
||||
else:
|
||||
hour = self.dt.hour
|
||||
minute = self.dt.minute
|
||||
second = self.dt.second + self.dt.microsecond*1.0e-6
|
||||
string.write("DATES\n {day} '{month}' {year} {hour:02d}:{minute:02d}:{second:2.6f} /\n/\n\n".format( day=day, month = inv_ecl_month[month], year=year, hour=hour, minute=minute, second=second))
|
||||
second = self.dt.second + self.dt.microsecond * 1.0e-6
|
||||
if self.dt.microsecond > 0:
|
||||
string.write(
|
||||
"DATES\n {day} '{month}' {year}"
|
||||
" {hour:02d}:{minute:02d}:{second:07.4f} /\n/\n\n".format(
|
||||
day=day,
|
||||
month=inv_ecl_month[month],
|
||||
year=year,
|
||||
hour=hour,
|
||||
minute=minute,
|
||||
second=second,
|
||||
)
|
||||
)
|
||||
else:
|
||||
string.write(
|
||||
"DATES\n {day} '{month}' {year}"
|
||||
" {hour:02d}:{minute:02d}:{second:02d} /\n/\n\n".format(
|
||||
day=day,
|
||||
month=inv_ecl_month[month],
|
||||
year=year,
|
||||
hour=hour,
|
||||
minute=minute,
|
||||
second=int(second),
|
||||
)
|
||||
)
|
||||
|
||||
for kw in self.keywords:
|
||||
string.write(str(kw))
|
||||
@ -129,108 +158,111 @@ class TimeStep(object):
|
||||
|
||||
|
||||
class TimeVector(object):
|
||||
|
||||
def __init__(self, start_date, base_string = None, base_file = None):
|
||||
def __init__(self, start_date, base_string=None, base_file=None):
|
||||
"""The TimeVector class is a simple vector class with DATES/TSTEP blocks.
|
||||
|
||||
The TimeVector class is a basic building block for tools designed to
|
||||
update schedule files. A schedule file consists of a list of keywords
|
||||
related to the dynamic properties of the field, like opening and
|
||||
closing wells, specifiying rates and so on. The temporal advancement of
|
||||
the simulator is controlled by DATES and TSTEP keywords. A typical
|
||||
schedule section can look like this:
|
||||
The TimeVector class is a basic building block for tools designed to
|
||||
update schedule files. A schedule file consists of a list of keywords
|
||||
related to the dynamic properties of the field, like opening and
|
||||
closing wells, specifiying rates and so on. The temporal advancement of
|
||||
the simulator is controlled by DATES and TSTEP keywords. A typical
|
||||
schedule section can look like this:
|
||||
|
||||
--- Step 1 -----------------------
|
||||
--- Step 1 -----------------------
|
||||
|
||||
WELSPECS
|
||||
'C1' 'G1' 10 10 10 'OIL' /
|
||||
/
|
||||
WELSPECS
|
||||
'C1' 'G1' 10 10 10 'OIL' /
|
||||
/
|
||||
|
||||
COMPDAT
|
||||
'C1' 15 20 10 16 'OPEN' /
|
||||
'C1' 15 21 16 16 'OPEN' /
|
||||
/
|
||||
COMPDAT
|
||||
'C1' 15 20 10 16 'OPEN' /
|
||||
'C1' 15 21 16 16 'OPEN' /
|
||||
/
|
||||
|
||||
WCONHIST
|
||||
'C1' 'OPEN' 'ORAT' 1000 /
|
||||
/
|
||||
WCONHIST
|
||||
'C1' 'OPEN' 'ORAT' 1000 /
|
||||
/
|
||||
|
||||
--- Step 2 ----------------------
|
||||
--- Step 2 ----------------------
|
||||
|
||||
DATES
|
||||
10 'MAY' 2016 /
|
||||
/
|
||||
DATES
|
||||
10 'MAY' 2016 /
|
||||
/
|
||||
|
||||
WCONHIST
|
||||
'C1' 'OPEN' 'ORAT' 2000 /
|
||||
/
|
||||
WCONHIST
|
||||
'C1' 'OPEN' 'ORAT' 2000 /
|
||||
/
|
||||
|
||||
--- Step 3 ----------------------
|
||||
--- Step 3 ----------------------
|
||||
|
||||
TSTEP
|
||||
10 /
|
||||
TSTEP
|
||||
10 /
|
||||
|
||||
WELSPECS
|
||||
'W2' 'G1' 5 5 5 'OIL' /
|
||||
/
|
||||
WELSPECS
|
||||
'W2' 'G1' 5 5 5 'OIL' /
|
||||
/
|
||||
|
||||
COMPDAT
|
||||
'W2' 10 10 7 10 'OPEN' /
|
||||
/
|
||||
COMPDAT
|
||||
'W2' 10 10 7 10 'OPEN' /
|
||||
/
|
||||
|
||||
WCONHIST
|
||||
'C1' 'OPEN' 'ORAT' 3000 /
|
||||
'W2' 'OPEN' 'ORAT' 1500 /
|
||||
/
|
||||
WCONHIST
|
||||
'C1' 'OPEN' 'ORAT' 3000 /
|
||||
'W2' 'OPEN' 'ORAT' 1500 /
|
||||
/
|
||||
|
||||
--- Step 4 ----------------------
|
||||
--- Step 4 ----------------------
|
||||
|
||||
DATES
|
||||
30 'MAY' 2016 /
|
||||
/
|
||||
DATES
|
||||
30 'MAY' 2016 /
|
||||
/
|
||||
|
||||
As indicated above the DATES and TSTEP keywords act as delimiters in
|
||||
the schedule file. In the TimeVector class the fundamental unit is
|
||||
TimeStep instance which consists of a list of keywords, and a
|
||||
terminating DATES or TSTEP keyword, the example above would correspond
|
||||
to a TimeVector with three TimeStep instances.
|
||||
As indicated above the DATES and TSTEP keywords act as delimiters in
|
||||
the schedule file. In the TimeVector class the fundamental unit is
|
||||
TimeStep instance which consists of a list of keywords, and a
|
||||
terminating DATES or TSTEP keyword, the example above would correspond
|
||||
to a TimeVector with three TimeStep instances.
|
||||
|
||||
Basic usage example:
|
||||
Basic usage example:
|
||||
|
||||
#!/usr/bin/env python
|
||||
from opm.tools import TimeVector
|
||||
#!/usr/bin/env python
|
||||
from opm.tools import TimeVector
|
||||
|
||||
# Create vector and load history.
|
||||
tv = TimeVector( start )
|
||||
tv.load("history.sch")
|
||||
# Create vector and load history.
|
||||
tv = TimeVector( start )
|
||||
tv.load("history.sch")
|
||||
|
||||
|
||||
# Load predictions from another file
|
||||
tv.load("prediction.sch")
|
||||
# Load predictions from another file
|
||||
tv.load("prediction.sch")
|
||||
|
||||
|
||||
# Insert the definition of one particular well at
|
||||
# a specifed date.
|
||||
tv.load("extra_wll.sch", date = datetime.datetime(2018,10,1))
|
||||
# Insert the definition of one particular well at
|
||||
# a specifed date.
|
||||
tv.load("extra_wll.sch", date = datetime.datetime(2018,10,1))
|
||||
|
||||
|
||||
# Check if we have a certain timestep:
|
||||
if datetime.datetime(2017,1,1) in tv:
|
||||
print("We have it!")
|
||||
else:
|
||||
print("No such date")
|
||||
# Check if we have a certain timestep:
|
||||
if datetime.datetime(2017,1,1) in tv:
|
||||
print("We have it!")
|
||||
else:
|
||||
print("No such date")
|
||||
|
||||
|
||||
# Dump the updated schedule content to a file:
|
||||
with open("schedule","w") as f:
|
||||
f.write(str(tv))
|
||||
# Dump the updated schedule content to a file:
|
||||
with open("schedule","w") as f:
|
||||
f.write(str(tv))
|
||||
|
||||
|
||||
"""
|
||||
if base_string and base_file:
|
||||
raise ValueError("Can only supply one of base_string and base_file arguments")
|
||||
raise ValueError(
|
||||
"Can only supply one of base_string and base_file arguments"
|
||||
)
|
||||
|
||||
self.start_date = datetime.datetime( start_date.year, start_date.month, start_date.day)
|
||||
self.start_date = datetime.datetime(
|
||||
start_date.year, start_date.month, start_date.day
|
||||
)
|
||||
self.time_steps_dict = {}
|
||||
self.time_steps_list = []
|
||||
|
||||
@ -246,7 +278,6 @@ class TimeVector(object):
|
||||
deck = Parser().parse_string(base_string)
|
||||
self._add_deck(deck, start_dt)
|
||||
|
||||
|
||||
def __len__(self):
|
||||
"""
|
||||
The number of timesteps in the vector.
|
||||
@ -261,21 +292,22 @@ class TimeVector(object):
|
||||
dt = datetime.datetime(dt.year, dt.month, dt.day)
|
||||
return dt in self.time_steps_dict
|
||||
|
||||
|
||||
def __getitem__(self, index):
|
||||
"""Will look up a timestep in the vector.
|
||||
|
||||
The index argument can either be an integer or a datetime.date/datetime instance.
|
||||
The index argument can either be an integer or a datetime.date/datetime
|
||||
instance.
|
||||
|
||||
"""
|
||||
if isinstance(index,int):
|
||||
if isinstance(index, int):
|
||||
return self.time_steps_list[index]
|
||||
else:
|
||||
if not isinstance(index,datetime.datetime) and isinstance(index,datetime.date):
|
||||
if not isinstance(index, datetime.datetime) and isinstance(
|
||||
index, datetime.date
|
||||
):
|
||||
index = datetime.datetime(index.year, index.month, index.day)
|
||||
|
||||
return self.time_steps_dict[index]
|
||||
|
||||
return self.time_steps_dict[index]
|
||||
|
||||
def _add_dates_block(self, ts):
|
||||
self.time_steps_dict[ts.dt] = ts
|
||||
@ -283,12 +315,11 @@ class TimeVector(object):
|
||||
|
||||
def delete(self, dt):
|
||||
del self.time_steps_dict[dt]
|
||||
for (index,ts) in enumerate(self.time_steps_list):
|
||||
for (index, ts) in enumerate(self.time_steps_list):
|
||||
if ts.dt == dt:
|
||||
del self.time_steps_list[index]
|
||||
break
|
||||
|
||||
|
||||
def add_keywords(self, dt, keywords):
|
||||
if dt < self.start_date:
|
||||
raise ValueError("Invalid datetime argument: {}".format(dt))
|
||||
@ -300,18 +331,23 @@ class TimeVector(object):
|
||||
else:
|
||||
ts = TimeStep(dt, keywords)
|
||||
self._add_dates_block(ts)
|
||||
self.time_steps_list.sort( key = attrgetter("dt"))
|
||||
|
||||
self.time_steps_list.sort(key=attrgetter("dt"))
|
||||
|
||||
def _add_deck(self, deck, start_date):
|
||||
first_kw = deck[0]
|
||||
if start_date is None:
|
||||
if first_kw.name != "DATES":
|
||||
raise ValueError("When loading you must *either* specify date - or file must start with DATES keyword")
|
||||
raise ValueError(
|
||||
"When loading you must *either* specify date - or file must start"
|
||||
" with DATES keyword"
|
||||
)
|
||||
dt = _make_datetime(first_kw[len(first_kw) - 1])
|
||||
else:
|
||||
if first_kw.name == "DATES":
|
||||
raise ValueError("When loading you must *either* specify date - or file must start with DATES keyword")
|
||||
raise ValueError(
|
||||
"When loading you must *either* specify date - or file must start"
|
||||
" with DATES keyword"
|
||||
)
|
||||
dt = start_date
|
||||
|
||||
keywords = []
|
||||
@ -320,24 +356,24 @@ class TimeVector(object):
|
||||
if kw.name == "DATES":
|
||||
self.add_keywords(dt, keywords)
|
||||
|
||||
for index in range(len(kw)-1):
|
||||
for index in range(len(kw) - 1):
|
||||
dt = _make_datetime(kw[index])
|
||||
self.add_keywords(dt, [])
|
||||
|
||||
dt = _make_datetime(kw[len(kw)-1])
|
||||
dt = _make_datetime(kw[len(kw) - 1])
|
||||
|
||||
keywords = []
|
||||
continue
|
||||
|
||||
#if kw.name == "TSTEP":
|
||||
#raise ValueError("Must block the ranges with active TSTEP - getting a DATES in there is ERROR")
|
||||
# if kw.name == "TSTEP":
|
||||
# raise ValueError("Must block the ranges with active TSTEP -
|
||||
# getting a DATES in there is ERROR")
|
||||
|
||||
keywords.append(kw)
|
||||
|
||||
self.add_keywords(dt, keywords)
|
||||
|
||||
|
||||
def load(self, filename, date = None):
|
||||
def load(self, filename, date=None):
|
||||
"""Will parse a Schedule file and add the keywords to the current TimeVector.
|
||||
|
||||
You can call the load() method repeatedly, the different timesteps will
|
||||
@ -358,15 +394,13 @@ class TimeVector(object):
|
||||
deck = Parser().parse(filename)
|
||||
self._add_deck(deck, date)
|
||||
|
||||
|
||||
def load_string(self, deck_string, date = None):
|
||||
def load_string(self, deck_string, date=None):
|
||||
"""
|
||||
Like load() - but load from a string literal instead of file.
|
||||
"""
|
||||
deck = Parser().parse_string(deck_string)
|
||||
self._add_deck(deck, date)
|
||||
|
||||
|
||||
def __str__(self):
|
||||
"""Will return a string representation of the vector.
|
||||
|
||||
@ -381,12 +415,9 @@ class TimeVector(object):
|
||||
|
||||
return string.getvalue()
|
||||
|
||||
|
||||
|
||||
|
||||
@property
|
||||
def dates(self):
|
||||
"""
|
||||
Will return a list of all the dates in the vector.
|
||||
"""
|
||||
return [ x.dt for x in self.time_steps_list ]
|
||||
return [x.dt for x in self.time_steps_list]
|
||||
|
Loading…
Reference in New Issue
Block a user