From 7998d1ffcfec6ab4e1f7dc8f9684bc29c09dca27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Berland?= Date: Mon, 30 Sep 2019 18:33:35 +0200 Subject: [PATCH 1/5] Add original resscript sunsch --- subscript/sunsch.py | 276 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 276 insertions(+) create mode 100644 subscript/sunsch.py diff --git a/subscript/sunsch.py b/subscript/sunsch.py new file mode 100644 index 000000000..ca85140ed --- /dev/null +++ b/subscript/sunsch.py @@ -0,0 +1,276 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +"""Tool for merging Schedule files. + +Reads a YAML-file specifying how a Eclipse Schedule section is to be +produced given certain input files. + +Output will not be generated unless the produced data is valid in +Eclipse, checking provided by sunbeam/opm-parser. + +YAML-file components: + + init - filename for the initial file. If omitted, defaults to an + empty file. If you need something to happen between the + Eclipse start date and the first DATES keyword, it must + be present in this file. + + output - filename for output. stdout if omitted + + startdate - YYYY-MM-DD for the initial date in the simulation. This + date is not outputted and anything occuring before that + will be clipped (TODO) + + refdate - if supplied, will work as a reference date for relative + inserts. If not supplied startdate will be used. + + enddate - YYYY-MM-DD, anything after that date will be clipped (TODO). + + dategrid - a string being either 'weekly', 'biweekly', 'monthly', + 'bimonthly' stating how often a DATES keyword is wanted + (independent of inserts/merges). '(bi)monthly' and + 'yearly' will be rounded to first in every month. + + merge - list of filenames to be merged in. DATES must be the first + keyword in these files. + + insert - list of components to be inserted into the final Schedule + file. Each list elemen can contain the elemens: + + date - Fixed date for the insertion + + days - relative date for insertion relative to refdate/startdate + + filename - filename to override the yaml-component element name. + + string - instead of filename, you can write the contents inline + + substitute - key-value pairs that will subsitute in + incoming files (or inline string) with + associated values. + +""" + +import datetime +import tempfile +import argparse +import yaml +from sunbeam.tools import TimeVector + +import resscript.header as header + + +def datetime_from_date(date): + return datetime.datetime.combine(date, datetime.datetime.min.time()) + +def process_sch_config(sunschconf, quiet=True): + """Process a Schedule configuration into a sunbeam TimeVector + + :param sunschconf : configuration for the schedule merges and inserts + :type sunschconf: dict + :param quiet: Whether status messages should be printed during processing + :type quiet: bool + """ + if 'startdate' in sunschconf: + schedule = TimeVector(sunschconf['startdate']) + elif 'refdate' in sunschconf: + schedule = TimeVector(sunschconf['refdate']) + else: + raise Exception("No startdate or refdate given") + + if 'refdate' not in sunschconf and 'startdate' in sunschconf: + sunschconf['refdate'] = sunschconf['startdate'] + + if 'init' in sunschconf: + if not quiet: + print("Loading " + sunschconf['init'] + " at startdate") + schedule.load(sunschconf['init'], + date=datetime.datetime.combine(sunschconf['startdate'], + datetime.datetime.min.time())) + + if 'merge' in sunschconf: + for file in sunschconf['merge']: + try: + if not quiet: + print("Loading " + file) + schedule.load(file) + except ValueError as exception: + raise Exception("Error in " + file + ": " + str(exception)) + + if 'insert' in sunschconf: # inserts should be list of dicts of dicts + for file in sunschconf['insert']: + # file is now a dict with only one key + fileid = file.keys()[0] + filedata = file[fileid].keys() + + + # Figure out the correct filename, only needed when we + # have a string. + if 'string' not in filedata: + if 'filename' not in filedata: + filename = fileid + else: + filename = file[fileid]['filename'] + + resultfile = tempfile.NamedTemporaryFile(delete=False) + resultfilename = resultfile.name + if 'substitute' in filedata: + templatelines = open(filename, 'r').readlines() + + # Parse substitution list: + substdict = file[fileid]['substitute'] + # Perform substitution and put into a tmp file + for line in templatelines: + for key in substdict: + if "<" + key + ">" in line: + line = line.replace("<" + key + ">", str(substdict[key])) + resultfile.write(line) + resultfile.close() + # Now we overwrite the filename coming from the yaml file! + filename = resultfilename + + # Figure out the correct date: + if 'date' in file[fileid]: + date = datetime.datetime.combine(file[fileid]['date'], + datetime.datetime.min.time()) + if 'days' in file[fileid]: + if not 'refdate' in sunschconf: + raise Exception("ERROR: When using days in insert " + \ + "statements, you must provide refdate") + date = datetime.datetime.combine(sunschconf['refdate'], + datetime.datetime.min.time()) + \ + datetime.timedelta(days=file[fileid]['days']) + if 'string' not in filedata: + schedule.load(filename, date=date) + else: + schedule.add_keywords(datetime_from_date(date), + [file[fileid]['string']]) + + if 'enddate' not in sunschconf: + if not quiet: + print("Warning: Implicit end date. " +\ + "Any content at last date is ignored") + # Whether we include it in the output does not matter, + # Eclipse will ignore it + enddate = schedule.dates[-1].date() + else: + enddate = sunschconf['enddate'] # datetime.date + if type(enddate) != datetime.date: + raise Exception("ERROR: end-date not in ISO-8601 format, must be YYYY-MM-DD") + + # Clip anything that is beyond the enddate + for date in schedule.dates: + if date.date() > enddate: + schedule.delete(date) + + # Ensure that the end-date is actually mentioned in the Schedule + # so that we know Eclipse will actually simulate until this date + if enddate not in [x.date() for x in schedule.dates]: + schedule.add_keywords(datetime_from_date(enddate), ['']) + + # Dategrid is added at the end, in order to support + # an implicit end-date + if 'dategrid' in sunschconf: + dates = dategrid(sunschconf['startdate'], enddate, + sunschconf['dategrid']) + for date in dates: + schedule.add_keywords(datetime_from_date(date), [""]) + + return schedule + +def dategrid(startdate, enddate, interval): + """Return a list of datetimes at given interval + + + Parameters + ---------- + startdate: datetime.date + First date in range + enddate: datetime.date + Last date in range + interval: str + Must be among: 'monthly', 'yearly', 'weekly', + 'biweekly', 'bimonthly' + Return + ------ + list of datetime.date. Includes start-date, might not include end-date + """ + + supportedintervals = ['monthly', 'yearly', 'weekly', 'biweekly', + 'bimonthly'] + if interval not in supportedintervals: + raise Exception("Unsupported dategrid interval \"" + interval + \ + "\". Pick among " + \ + ", ".join(supportedintervals)) + dates = [startdate] + date = startdate + datetime.timedelta(days=1) + startdateweekday = startdate.weekday() + + # Brute force implementation by looping over all possible + # days. This is robust with respect to all possible date oddities, + # but makes it difficult to support more interval types. + while date <= enddate: + if interval == 'monthly': + if date.day == 1: + dates.append(date) + elif interval == 'bimonthly': + if date.day == 1 and date.month % 2 == 1: + dates.append(date) + elif interval == 'weekly': + if date.weekday() == startdateweekday: + dates.append(date) + elif interval == 'biweekly': + weeknumber = date.isocalendar()[1] + if date.weekday() == startdateweekday and weeknumber % 2 == 1: + dates.append(date) + elif interval == 'yearly': + if date.day == 1 and date.month == 1: + dates.append(date) + elif interval == 'daily': + dates.append(date) + date += datetime.timedelta(days=1) + return dates + + + +# If we are called from command line: +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("config", + help="Config file in YAML format for Schedule merging") + parser.add_argument("-o", "--output", type=str, default="", + help="Override output in yaml config. Use - for stdout") + parser.add_argument("-q", "--quiet", action='store_true', + help="Mute output from script") + args = parser.parse_args() + + if not args.quiet and args.output != "-": + header.compose("sunsch", + "May 2018", + ["Håvard Berland"], ["havb@equinor.com"], + ["Access help with -h"], + "Generate Schedule files for Eclipse " + \ + "from snippets (merging and insertions)") + + + # Load YAML file: + config = yaml.load(open(args.config)) + + # Overrides: + if args.output != "": + config['output'] = args.output + + if 'output' not in config: + config['output'] = "-" # Write to stdout + + if args.output == "-": + args.quiet = True + + schedule = process_sch_config(config, args.quiet) + + if config['output'] == "-" or 'output' not in config: + print str(schedule) + else: + if not args.quiet: + print("Writing Eclipse deck to " + config['output']) + open(config['output'], 'w').write(str(schedule)) From 933caa5b70378e0470e7caac156881feb8e39d97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Berland?= Date: Mon, 30 Sep 2019 18:41:06 +0200 Subject: [PATCH 2/5] Port sunsch to subscript --- subscript/sunsch.py | 218 ++++++++++++++++++++++++-------------------- 1 file changed, 119 insertions(+), 99 deletions(-) diff --git a/subscript/sunsch.py b/subscript/sunsch.py index ca85140ed..722e5f9b2 100644 --- a/subscript/sunsch.py +++ b/subscript/sunsch.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """Tool for merging Schedule files. @@ -17,9 +16,9 @@ output - filename for output. stdout if omitted - startdate - YYYY-MM-DD for the initial date in the simulation. This - date is not outputted and anything occuring before that - will be clipped (TODO) + startdate - YYYY-MM-DD for the initial date in the simulation. There + should not be any events before this date in merged-in files. + TODO: Clip any events before startdate refdate - if supplied, will work as a reference date for relative inserts. If not supplied startdate will be used. @@ -57,12 +56,12 @@ import yaml from sunbeam.tools import TimeVector -import resscript.header as header - def datetime_from_date(date): + """Set time to 00:00:00 in a date""" return datetime.datetime.combine(date, datetime.datetime.min.time()) + def process_sch_config(sunschconf, quiet=True): """Process a Schedule configuration into a sunbeam TimeVector @@ -71,54 +70,56 @@ def process_sch_config(sunschconf, quiet=True): :param quiet: Whether status messages should be printed during processing :type quiet: bool """ - if 'startdate' in sunschconf: - schedule = TimeVector(sunschconf['startdate']) - elif 'refdate' in sunschconf: - schedule = TimeVector(sunschconf['refdate']) + if "startdate" in sunschconf: + schedule = TimeVector(sunschconf["startdate"]) + elif "refdate" in sunschconf: + schedule = TimeVector(sunschconf["refdate"]) else: raise Exception("No startdate or refdate given") - if 'refdate' not in sunschconf and 'startdate' in sunschconf: - sunschconf['refdate'] = sunschconf['startdate'] + if "refdate" not in sunschconf and "startdate" in sunschconf: + sunschconf["refdate"] = sunschconf["startdate"] - if 'init' in sunschconf: + if "init" in sunschconf: if not quiet: - print("Loading " + sunschconf['init'] + " at startdate") - schedule.load(sunschconf['init'], - date=datetime.datetime.combine(sunschconf['startdate'], - datetime.datetime.min.time())) - - if 'merge' in sunschconf: - for file in sunschconf['merge']: + print("Loading " + sunschconf["init"] + " at startdate") + schedule.load( + sunschconf["init"], + date=datetime.datetime.combine( + sunschconf["startdate"], datetime.datetime.min.time() + ), + ) + + if "merge" in sunschconf: + for filename in sunschconf["merge"]: try: if not quiet: - print("Loading " + file) - schedule.load(file) + print("Loading " + filename) + schedule.load(filename) except ValueError as exception: - raise Exception("Error in " + file + ": " + str(exception)) - - if 'insert' in sunschconf: # inserts should be list of dicts of dicts - for file in sunschconf['insert']: - # file is now a dict with only one key - fileid = file.keys()[0] - filedata = file[fileid].keys() + raise Exception("Error in " + filename + ": " + str(exception)) + if "insert" in sunschconf: # inserts should be list of dicts of dicts + for filedict in sunschconf["insert"]: + # filedict is now a dict with only one key + fileid = list(filedict.keys())[0] + filedata = list(filedict[fileid].keys()) # Figure out the correct filename, only needed when we # have a string. - if 'string' not in filedata: - if 'filename' not in filedata: + if "string" not in filedata: + if "filename" not in filedata: filename = fileid else: - filename = file[fileid]['filename'] + filename = filedict[fileid]["filename"] - resultfile = tempfile.NamedTemporaryFile(delete=False) + resultfile = tempfile.NamedTemporaryFile(mode='w', delete=False) resultfilename = resultfile.name - if 'substitute' in filedata: - templatelines = open(filename, 'r').readlines() + if "substitute" in filedata: + templatelines = open(filename, "r").readlines() # Parse substitution list: - substdict = file[fileid]['substitute'] + substdict = filedict[fileid]["substitute"] # Perform substitution and put into a tmp file for line in templatelines: for key in substdict: @@ -130,54 +131,61 @@ def process_sch_config(sunschconf, quiet=True): filename = resultfilename # Figure out the correct date: - if 'date' in file[fileid]: - date = datetime.datetime.combine(file[fileid]['date'], - datetime.datetime.min.time()) - if 'days' in file[fileid]: - if not 'refdate' in sunschconf: - raise Exception("ERROR: When using days in insert " + \ - "statements, you must provide refdate") - date = datetime.datetime.combine(sunschconf['refdate'], - datetime.datetime.min.time()) + \ - datetime.timedelta(days=file[fileid]['days']) - if 'string' not in filedata: + if "date" in filedict[fileid]: + date = datetime.datetime.combine( + filedict[fileid]["date"], datetime.datetime.min.time() + ) + if "days" in filedict[fileid]: + if "refdate" not in sunschconf: + raise Exception( + "ERROR: When using days in insert " + + "statements, you must provide refdate" + ) + date = datetime.datetime.combine( + sunschconf["refdate"], datetime.datetime.min.time() + ) + datetime.timedelta(days=filedict[fileid]["days"]) + if "string" not in filedata: schedule.load(filename, date=date) else: - schedule.add_keywords(datetime_from_date(date), - [file[fileid]['string']]) + schedule.add_keywords( + datetime_from_date(date), [filedict[fileid]["string"]] + ) - if 'enddate' not in sunschconf: + if "enddate" not in sunschconf: if not quiet: - print("Warning: Implicit end date. " +\ - "Any content at last date is ignored") + print( + ("Warning: Implicit end date. " + "Any content at last date is ignored") + ) # Whether we include it in the output does not matter, # Eclipse will ignore it enddate = schedule.dates[-1].date() else: - enddate = sunschconf['enddate'] # datetime.date - if type(enddate) != datetime.date: - raise Exception("ERROR: end-date not in ISO-8601 format, must be YYYY-MM-DD") - + enddate = sunschconf["enddate"] # datetime.date + if not isinstance(enddate, datetime.date): + raise Exception( + "ERROR: end-date not in ISO-8601 format, must be YYYY-MM-DD" + ) + # Clip anything that is beyond the enddate for date in schedule.dates: if date.date() > enddate: schedule.delete(date) - + # Ensure that the end-date is actually mentioned in the Schedule # so that we know Eclipse will actually simulate until this date if enddate not in [x.date() for x in schedule.dates]: - schedule.add_keywords(datetime_from_date(enddate), ['']) - + schedule.add_keywords(datetime_from_date(enddate), [""]) + # Dategrid is added at the end, in order to support # an implicit end-date - if 'dategrid' in sunschconf: - dates = dategrid(sunschconf['startdate'], enddate, - sunschconf['dategrid']) + if "dategrid" in sunschconf: + dates = dategrid(sunschconf["startdate"], enddate, sunschconf["dategrid"]) for date in dates: schedule.add_keywords(datetime_from_date(date), [""]) return schedule + def dategrid(startdate, enddate, interval): """Return a list of datetimes at given interval @@ -196,12 +204,14 @@ def dategrid(startdate, enddate, interval): list of datetime.date. Includes start-date, might not include end-date """ - supportedintervals = ['monthly', 'yearly', 'weekly', 'biweekly', - 'bimonthly'] + supportedintervals = ["monthly", "yearly", "weekly", "biweekly", "bimonthly"] if interval not in supportedintervals: - raise Exception("Unsupported dategrid interval \"" + interval + \ - "\". Pick among " + \ - ", ".join(supportedintervals)) + raise Exception( + 'Unsupported dategrid interval "' + + interval + + '". Pick among ' + + ", ".join(supportedintervals) + ) dates = [startdate] date = startdate + datetime.timedelta(days=1) startdateweekday = startdate.weekday() @@ -210,67 +220,77 @@ def dategrid(startdate, enddate, interval): # days. This is robust with respect to all possible date oddities, # but makes it difficult to support more interval types. while date <= enddate: - if interval == 'monthly': + if interval == "monthly": if date.day == 1: dates.append(date) - elif interval == 'bimonthly': + elif interval == "bimonthly": if date.day == 1 and date.month % 2 == 1: dates.append(date) - elif interval == 'weekly': + elif interval == "weekly": if date.weekday() == startdateweekday: dates.append(date) - elif interval == 'biweekly': + elif interval == "biweekly": weeknumber = date.isocalendar()[1] if date.weekday() == startdateweekday and weeknumber % 2 == 1: dates.append(date) - elif interval == 'yearly': + elif interval == "yearly": if date.day == 1 and date.month == 1: dates.append(date) - elif interval == 'daily': + elif interval == "daily": dates.append(date) date += datetime.timedelta(days=1) return dates - # If we are called from command line: -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("config", - help="Config file in YAML format for Schedule merging") - parser.add_argument("-o", "--output", type=str, default="", - help="Override output in yaml config. Use - for stdout") - parser.add_argument("-q", "--quiet", action='store_true', - help="Mute output from script") +def get_parser(): + """Set up parser for command line utility""" + parser = argparse.ArgumentParser( + description="Generate Eclipse Schedule file from merges and insertions" + ) + parser.add_argument( + "config", help="Config file in YAML format for Schedule merging" + ) + parser.add_argument( + "-o", + "--output", + type=str, + default="", + help="Override output in yaml config. Use - for stdout", + ) + parser.add_argument( + "-q", "--quiet", action="store_true", help="Mute output from script" + ) + return parser + + +def main(): + """Entry point from command line""" + parser = get_parser() args = parser.parse_args() - if not args.quiet and args.output != "-": - header.compose("sunsch", - "May 2018", - ["Håvard Berland"], ["havb@equinor.com"], - ["Access help with -h"], - "Generate Schedule files for Eclipse " + \ - "from snippets (merging and insertions)") - - # Load YAML file: - config = yaml.load(open(args.config)) + config = yaml.safe_load(open(args.config)) # Overrides: if args.output != "": - config['output'] = args.output + config["output"] = args.output - if 'output' not in config: - config['output'] = "-" # Write to stdout + if "output" not in config: + config["output"] = "-" # Write to stdout if args.output == "-": args.quiet = True schedule = process_sch_config(config, args.quiet) - if config['output'] == "-" or 'output' not in config: - print str(schedule) + if config["output"] == "-" or "output" not in config: + print(str(schedule)) else: if not args.quiet: - print("Writing Eclipse deck to " + config['output']) - open(config['output'], 'w').write(str(schedule)) + print("Writing Eclipse deck to " + config["output"]) + open(config["output"], "w").write(str(schedule)) + + +if __name__ == "__main__": + main() From ffc1a5b9f30b4d53848b65ce7bb32fb5e2739726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Berland?= Date: Mon, 30 Sep 2019 18:42:21 +0200 Subject: [PATCH 3/5] Test and test data for sunsch --- subscript/tests/test_sunsch.py | 38 +++++++++++++++++++ subscript/tests/testdata_sunsch/config.yml | 28 ++++++++++++++ subscript/tests/testdata_sunsch/emptyinit.sch | 4 ++ subscript/tests/testdata_sunsch/foo1.sch | 5 +++ .../tests/testdata_sunsch/footemplate.sch | 3 ++ subscript/tests/testdata_sunsch/mergeme.sch | 14 +++++++ 6 files changed, 92 insertions(+) create mode 100644 subscript/tests/test_sunsch.py create mode 100644 subscript/tests/testdata_sunsch/config.yml create mode 100644 subscript/tests/testdata_sunsch/emptyinit.sch create mode 100644 subscript/tests/testdata_sunsch/foo1.sch create mode 100644 subscript/tests/testdata_sunsch/footemplate.sch create mode 100644 subscript/tests/testdata_sunsch/mergeme.sch diff --git a/subscript/tests/test_sunsch.py b/subscript/tests/test_sunsch.py new file mode 100644 index 000000000..e61779763 --- /dev/null +++ b/subscript/tests/test_sunsch.py @@ -0,0 +1,38 @@ +from __future__ import absolute_import + +import pytest # noqa: F401 +import os +import sys + +from .. import sunsch + + +def test_main(): + """Test command line sunsch, loading a yaml file""" + os.chdir(os.path.join(os.path.dirname(__file__), "testdata_sunsch")) + + outfile = "schedule.sch" # also in config.yml + + if os.path.exists(outfile): + os.unlink(outfile) + sys.argv = ["sunsch", "config.yml"] + sunsch.main() + assert os.path.exists(outfile) + + schlines = open(outfile).readlines() + assert len(schlines) > 70 + + # Check footemplate.sch was included: + assert any(['A-90' in x for x in schlines]) + + # Sample check for mergeme.sch: + assert any(['WRFTPLT' in x for x in schlines]) + + # Check for foo1.sch, A-1 should occur twice + assert sum(['A-1' in x for x in schlines]) == 2 + + # Check for substitutetest: + assert any(['400000' in x for x in schlines]) + + # Check for randomid: + assert any(['A-4' in x for x in schlines]) diff --git a/subscript/tests/testdata_sunsch/config.yml b/subscript/tests/testdata_sunsch/config.yml new file mode 100644 index 000000000..5dd694eb0 --- /dev/null +++ b/subscript/tests/testdata_sunsch/config.yml @@ -0,0 +1,28 @@ +init: emptyinit.sch +output: schedule.sch +startdate: 2017-01-01 +#refdate: 2017-01-01 +enddate: 2020-12-01 +merge: + - mergeme.sch +dategrid: yearly +insert: + - foo1.sch: # filename is read from this line unless filename is supplied + date: 2020-01-01 + - randomidentifier: + filename: foo1.sch + date: 2021-01-01 + - foo1.sch: + days: 100 + - randomid: + days: 40 + string: "WCONHIST\n A-4 OPEN ORAT 5000 /\n/" + - substitutetest: + days: 2 + filename: footemplate.sch + substitute: { ORAT: 3000, GRAT: 400000} + - footemplate.sch: + days: 10 + substitute: + ORAT: 30000 + GRAT: 100 diff --git a/subscript/tests/testdata_sunsch/emptyinit.sch b/subscript/tests/testdata_sunsch/emptyinit.sch new file mode 100644 index 000000000..006b792f2 --- /dev/null +++ b/subscript/tests/testdata_sunsch/emptyinit.sch @@ -0,0 +1,4 @@ +-- This file is intentionally empty. Where does this comment go? +WCONHIST + 'BAR-FOO' 'SHUT'/ +/ diff --git a/subscript/tests/testdata_sunsch/foo1.sch b/subscript/tests/testdata_sunsch/foo1.sch new file mode 100644 index 000000000..2b32b0941 --- /dev/null +++ b/subscript/tests/testdata_sunsch/foo1.sch @@ -0,0 +1,5 @@ +-- From foo1.sch +WCONHIST +-- from foo1.sch + 'A-1' 'OPEN' 'ORAT' 5000 / +/ diff --git a/subscript/tests/testdata_sunsch/footemplate.sch b/subscript/tests/testdata_sunsch/footemplate.sch new file mode 100644 index 000000000..9e22da797 --- /dev/null +++ b/subscript/tests/testdata_sunsch/footemplate.sch @@ -0,0 +1,3 @@ +WCONHIST + 'A-90' 'OPEN' 'ORAT' / +/ diff --git a/subscript/tests/testdata_sunsch/mergeme.sch b/subscript/tests/testdata_sunsch/mergeme.sch new file mode 100644 index 000000000..a964129d3 --- /dev/null +++ b/subscript/tests/testdata_sunsch/mergeme.sch @@ -0,0 +1,14 @@ +DATES + 9 'FEB' 2019 / +/ + +WCONHIST + 'A-2' 'OPEN' 'ORAT' 4000 / +/ +DATES + 1 'OCT' 2020 / +/ + +WRFTPLT + 'NO' / +/ \ No newline at end of file From b6c45095d7ed615d290f8ca946716d01a7eadd60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Berland?= Date: Mon, 30 Sep 2019 18:46:27 +0200 Subject: [PATCH 4/5] Update CI for sunsch * Downgrade ubuntu version for boost/opm-common/sunbeam --- .travis.yml | 46 +++++++++++++++++++++++++++++++++++++++++--- requirements-dev.txt | 1 + requirements.txt | 1 + setup.cfg | 2 ++ setup.py | 2 +- 5 files changed, 48 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index e4b5e13a3..f984da8b8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,20 @@ language: python -dist: xenial + +dist: trusty + +compiler: + - gcc + matrix: include: - python: '2.7' - python: '3.5' - python: '3.6' - - python: '3.7' + # - python: '3.7' # Missing working compile setup for sunbeam before_install: - python --version + - export CXX="g++-4.9" CC="gcc-4.9" - export INSTALL_DIR=`pwd`/../install - export PYTHONPATH=$INSTALL_DIR/lib/python$TRAVIS_PYTHON_VERSION/dist-packages:$PYTHONPATH - export LD_LIBRARY_PATH=$INSTALL_DIR/lib:$INSTALL_DIR/lib64:$LD_LIBRARY_PATH @@ -16,14 +22,27 @@ before_install: - echo $PYTHONPATH - echo $LD_LIBRARY_PATH +addons: + apt: + sources: + - boost-latest + - ubuntu-toolchain-r-test + packages: + - gcc-4.9 + - g++-4.9 + - libboost1.55-all-dev + - liblapack-dev + +sudo: required + install: - python -m pip install --upgrade pip + - python -m pip install --upgrade -r requirements.txt - python -m pip install --upgrade -r requirements-dev.txt before_script: # For now we have to make install libecl. # Remove when it's possible to pip install - - source .libecl_version - git clone https://github.com/equinor/libecl - pushd libecl @@ -45,6 +64,27 @@ before_script: - rm -rf libecl - python -c "import ecl; print(ecl.__file__)" + # We also need sunbeam, which requires opm-common: + - git clone --recursive https://github.com/OPM/opm-common.git + - git clone --recursive https://github.com/equinor/sunbeam.git + - mkdir opm-common/build + - pushd opm-common/build + - git checkout release/sunbeam/2019.02 + - cmake .. -DCMAKE_PREFIX_PATH=$INSTALL_DIR + -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR + -DBUILD_TESTING=OFF + -DBUILD_SHARED_LIBS=ON + - make -j 4 install + - popd + - mkdir sunbeam/build + - pushd sunbeam/build + - cmake .. -DCMAKE_PREFIX_PATH=$INSTALL_DIR + -DUSE_RPATH=ON + -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR + -DPYTHON_EXECUTABLE=`which python` + - make -j 4 install + - popd + script: - python setup.py test - python -m flake8 subscript diff --git a/requirements-dev.txt b/requirements-dev.txt index 571f3b042..66b1238c3 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,6 @@ pandas scipy +pyyaml>=5.1 setuptools >=28 setuptools_scm pytest diff --git a/requirements.txt b/requirements.txt index 6e8dab0da..b97ccf726 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ pandas scipy +pyyaml>=5.1 diff --git a/setup.cfg b/setup.cfg index acd7cd689..89ce7b1e7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -15,6 +15,8 @@ norecursedirs = .env dist build + opm-common + sunbeam addopts = -ra diff --git a/setup.py b/setup.py index 7c439e4cf..62ba0e7d8 100644 --- a/setup.py +++ b/setup.py @@ -21,8 +21,8 @@ tests_require=["pytest"], entry_points={ "console_scripts": [ - "subscript = subscript.cli:main", "presentvalue = subscript.presentvalue:main", + "sunsch = subscript.sunsch:main", ] }, use_scm_version={"write_to": "subscript/version.py"}, From ba86257a4871d22a10a04db694576cfe8a7e7bdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Berland?= Date: Thu, 3 Oct 2019 10:27:10 +0200 Subject: [PATCH 5/5] Move module doc into argparse description and epilog --- subscript/sunsch.py | 103 ++++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 51 deletions(-) diff --git a/subscript/sunsch.py b/subscript/sunsch.py index 722e5f9b2..32c9b7bca 100644 --- a/subscript/sunsch.py +++ b/subscript/sunsch.py @@ -1,53 +1,6 @@ # -*- coding: utf-8 -*- -"""Tool for merging Schedule files. - -Reads a YAML-file specifying how a Eclipse Schedule section is to be -produced given certain input files. - -Output will not be generated unless the produced data is valid in -Eclipse, checking provided by sunbeam/opm-parser. - -YAML-file components: - - init - filename for the initial file. If omitted, defaults to an - empty file. If you need something to happen between the - Eclipse start date and the first DATES keyword, it must - be present in this file. - - output - filename for output. stdout if omitted - - startdate - YYYY-MM-DD for the initial date in the simulation. There - should not be any events before this date in merged-in files. - TODO: Clip any events before startdate - - refdate - if supplied, will work as a reference date for relative - inserts. If not supplied startdate will be used. - - enddate - YYYY-MM-DD, anything after that date will be clipped (TODO). - - dategrid - a string being either 'weekly', 'biweekly', 'monthly', - 'bimonthly' stating how often a DATES keyword is wanted - (independent of inserts/merges). '(bi)monthly' and - 'yearly' will be rounded to first in every month. - - merge - list of filenames to be merged in. DATES must be the first - keyword in these files. - - insert - list of components to be inserted into the final Schedule - file. Each list elemen can contain the elemens: - - date - Fixed date for the insertion - - days - relative date for insertion relative to refdate/startdate - - filename - filename to override the yaml-component element name. - - string - instead of filename, you can write the contents inline - - substitute - key-value pairs that will subsitute in - incoming files (or inline string) with - associated values. - +""" +Tool for generating Eclipse Schedule files """ import datetime @@ -113,7 +66,7 @@ def process_sch_config(sunschconf, quiet=True): else: filename = filedict[fileid]["filename"] - resultfile = tempfile.NamedTemporaryFile(mode='w', delete=False) + resultfile = tempfile.NamedTemporaryFile(mode="w", delete=False) resultfilename = resultfile.name if "substitute" in filedata: templatelines = open(filename, "r").readlines() @@ -246,7 +199,55 @@ def dategrid(startdate, enddate, interval): def get_parser(): """Set up parser for command line utility""" parser = argparse.ArgumentParser( - description="Generate Eclipse Schedule file from merges and insertions" + formatter_class=argparse.RawDescriptionHelpFormatter, + description="""Generate Eclipse Schedule file from merges and insertions. + +Reads a YAML-file specifying how a Eclipse Schedule section is to be +produced given certain input files. + +Output will not be generated unless the produced data is valid in + Eclipse, checking provided by sunbeam/opm-parser.""", + epilog="""YAML-file components: + + init - filename for the initial file. If omitted, defaults to an + empty file. If you need something to happen between the + Eclipse start date and the first DATES keyword, it must + be present in this file. + + output - filename for output. stdout if omitted + + startdate - YYYY-MM-DD for the initial date in the simulation. There + should not be any events before this date in merged-in files. + TODO: Clip any events before startdate + + refdate - if supplied, will work as a reference date for relative + inserts. If not supplied startdate will be used. + + enddate - YYYY-MM-DD, anything after that date will be clipped (TODO). + + dategrid - a string being either 'weekly', 'biweekly', 'monthly', + 'bimonthly' stating how often a DATES keyword is wanted + (independent of inserts/merges). '(bi)monthly' and + 'yearly' will be rounded to first in every month. + + merge - list of filenames to be merged in. DATES must be the first + keyword in these files. + + insert - list of components to be inserted into the final Schedule + file. Each list elemen can contain the elemens: + + date - Fixed date for the insertion + + days - relative date for insertion relative to refdate/startdate + + filename - filename to override the yaml-component element name. + + string - instead of filename, you can write the contents inline + + substitute - key-value pairs that will subsitute in + incoming files (or inline string) with + associated values. + """, ) parser.add_argument( "config", help="Config file in YAML format for Schedule merging"