Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 32 additions & 31 deletions src/subscript/fmuobs/fmuobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,16 +209,20 @@ def autoparse_file(filename: str) -> tuple[str | None, pd.DataFrame]:
"""
try:
dframe = pd.read_csv(filename, sep=";")
except ValueError:
pass
else:
if {"DATE", "VECTOR", "VALUE", "ERROR"}.issubset(
set(dframe.columns)
) and not dframe.empty:
logger.info("Parsed %s as a ResInsight observation file", filename)
return ("resinsight", resinsight_df2df(dframe))
except ValueError:
pass

try:
dframe = pd.read_csv(filename, sep=",")
except ValueError:
pass
else:
if {"CLASS", "LABEL"}.issubset(dframe.columns) and not dframe.empty:
logger.info(
"Parsed %s as a CSV (internal dataframe format for ertobs) file",
Expand All @@ -227,47 +231,44 @@ def autoparse_file(filename: str) -> tuple[str | None, pd.DataFrame]:
if "DATE" in dframe:
dframe["DATE"] = pd.to_datetime(dframe["DATE"])
return ("csv", dframe)
except ValueError:
pass

try:
obsdict = yaml.safe_load(Path(filename).read_text(encoding="utf8"))
if isinstance(obsdict, dict) and (
obsdict.get("smry", None) or obsdict.get("rft", None)
):
logger.info("Parsed %s as a YAML file with observations", filename)
return ("yaml", obsdict2df(obsdict))
except yaml.scanner.ScannerError as exception:
# This occurs if there are tabs in the file, which is not
# allowed in a YAML file (but it can be present in ERT observation files)
# Tabs in the file (not allowed in YAML, but valid in ERT obs files)
logger.debug("ScannerError while attempting yaml-parsing")
logger.debug(str(exception))
except ValueError:
pass
else:
if isinstance(obsdict, dict) and (
obsdict.get("smry", None) or obsdict.get("rft", None)
):
logger.info("Parsed %s as a YAML file with observations", filename)
return ("yaml", obsdict2df(obsdict))

try:
with open(filename, encoding="utf8") as f_handle:
# This function does not have information on include file paths.
# Accept a FileNotFoundError while parsing, if we encounter that
# it is most likely an ert file, but which needs additional hints
# on where include files are located.
try:
dframe = ertobs2df(f_handle.read())
except FileNotFoundError:
logger.info(
"Parsed %s as an ERT observation file, with include statements",
filename,
)
return ("ert", pd.DataFrame())
if (
{"CLASS", "LABEL"}.issubset(dframe.columns)
and not dframe.empty
and set(dframe["CLASS"]).intersection(set(CLASS_SHORTNAME.keys()))
content = Path(filename).read_text(encoding="utf8")
except (OSError, UnicodeDecodeError):
content = None
if content is not None:
try:
ert_dframe = ertobs2df(content)
except FileNotFoundError:
logger.info(
"Parsed %s as an ERT observation file, with include statements",
filename,
)
return ("ert", pd.DataFrame())
except ValueError:
ert_dframe = None
if ert_dframe is not None and (
{"CLASS", "LABEL"}.issubset(ert_dframe.columns)
and not ert_dframe.empty
and set(ert_dframe["CLASS"]).intersection(set(CLASS_SHORTNAME.keys()))
):
logger.info("Parsed %s as an ERT observation file", filename)
return ("ert", dframe)
except ValueError:
pass
return ("ert", ert_dframe)

logger.error(
"Unable to parse %s as any supported observation file format", filename
Expand Down
45 changes: 28 additions & 17 deletions src/subscript/restartthinner/restartthinner.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,30 +88,41 @@ def rd_repacker(rstfilename: str, slicerstindices: list[int], quiet: bool) -> No
shutil.move(rstname, tempdir / rstname)

with chdir(tempdir):
subprocess.run(
[rd_unpack, rstname],
stdout=subprocess.DEVNULL if quiet else None,
check=True,
_run_unpack_and_slice(
rd_unpack, rstname, quiet, slicerstindices, rd_pack
)

for file in Path(".").glob("*.X*"):
index = int(file.suffix.lstrip(".X"))
if index not in slicerstindices:
file.unlink()

remaining_files = sorted(Path(".").glob("*.X*"))
subprocess.run(
[rd_pack, *[str(f) for f in remaining_files]],
stdout=subprocess.DEVNULL if quiet else None,
check=True,
)

# Move result back up
shutil.move(rstname, Path("..") / rstname)
finally:
shutil.rmtree(tempdir)


def _run_unpack_and_slice(
rd_unpack: str,
rstname: str,
quiet: bool,
slicerstindices: list[int],
rd_pack: str,
) -> None:

# Unpack the UNRST file first
subprocess.run(
[rd_unpack, rstname], stdout=subprocess.DEVNULL if quiet else None, check=True
)

for file in Path(".").glob("*.X*"):
index = int(file.suffix.lstrip(".X"))
if index not in slicerstindices:
file.unlink()

remaining_files = sorted(Path(".").glob("*.X*"))
subprocess.run(
[rd_pack, *[str(f) for f in remaining_files]],
stdout=subprocess.DEVNULL if quiet else None,
check=True,
)


def get_restart_indices(rstfilename: str) -> list[int]:
"""Extract a list of restart indices for a filename.

Expand Down
148 changes: 103 additions & 45 deletions src/subscript/summaryplot/summaryplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@
import sys
import termios
import tty
from collections.abc import Callable
from multiprocessing import Process
from pathlib import Path
from typing import Any

import matplotlib.pyplot as plt
import numpy as np
Expand Down Expand Up @@ -578,22 +580,24 @@ def split_vectorsdatafiles(
for vecdata in vectorsdatafiles:
try:
sumfn = Summary(vecdata)
datafiles.append(vecdata)

summaryfiles.append(sumfn)

# Try to load a corresponding parameter-file for colouring data
paths_to_check = [
Path(vecdata).absolute().parent / relpath / "parameters.txt"
for relpath in ["../..", "../", "."]
]
for path in paths_to_check:
if path.exists():
parameterfiles.append(str(path.resolve()))
break
except OSError:
# If we get here, we assume it was an Eclipse vector name.
vectors.append(vecdata)
continue

datafiles.append(vecdata)
summaryfiles.append(sumfn)
Comment on lines +588 to +589

# Try to load a corresponding parameter-file for colouring data
paths_to_check = [
Path(vecdata).absolute().parent / relpath / "parameters.txt"
for relpath in ["../..", "../", "."]
]
for path in paths_to_check:
if path.exists():
parameterfiles.append(str(path.resolve()))
break

return (summaryfiles, datafiles, vectors, parameterfiles)


Expand Down Expand Up @@ -659,40 +663,94 @@ def main() -> None:
old_settings = termios.tcgetattr(filedesc)
print("Menu: 'q' = quit, 'r' = reload plots")
try:
# change terminal settings to allow keyboard
# input without user pressing 'enter'
tty.setcbreak(sys.stdin.fileno())
char = ""
while char != "q" and plotprocess.is_alive():
char = sys.stdin.read(1)
if char == "r":
plotprocess.terminate()
plotprocess = Process(
target=summaryplotter,
kwargs={
"summaryfiles": None, # forces reload
"datafiles": datafiles,
"vectors": vectors,
"colourby": args.colourby,
"maxlabels": args.maxlabels,
"logcolourby": args.logcolourby,
"parameterfiles": parameterfiles,
"histvectors": args.hist,
"normalize": args.normalize,
"singleplot": args.singleplot,
"nolegend": args.nolegend,
"dumpimages": args.dumpimages,
"ensemblemode": args.ensemblemode,
},
)
plotprocess.start()
# change terminal settings to allow keyboard input without user pressing
# 'enter'
tty.setcbreak(filedesc)
plotprocess = interactive_menu(
plotprocess,
summaryplotter,
datafiles,
vectors,
args,
parameterfiles,
)
finally:
# Always restore terminal settings, even if an error occurs
termios.tcsetattr(filedesc, termios.TCSADRAIN, old_settings)
# Close plot windows (running in a subprocess)
stop_process(plotprocess)


def stop_process(process: Process) -> None:
if process.is_alive():
process.terminate()
process.join(timeout=5)
if process.is_alive():
process.kill()
process.join()
else:
process.join()


def restart_plotprocess(
plotprocess: Process,
summaryplotter: Callable[..., Any],
datafiles: list[str],
vectors: list[str],
args: argparse.Namespace,
parameterfiles: list[str],
) -> Process:
stop_process(plotprocess)
new_process = Process(
target=summaryplotter,
kwargs={
"summaryfiles": None, # forces reload
"datafiles": datafiles,
"vectors": vectors,
"colourby": args.colourby,
"maxlabels": args.maxlabels,
"logcolourby": args.logcolourby,
"parameterfiles": parameterfiles,
"histvectors": args.hist,
"normalize": args.normalize,
"singleplot": args.singleplot,
"nolegend": args.nolegend,
"dumpimages": args.dumpimages,
"ensemblemode": args.ensemblemode,
},
)
new_process.start()
return new_process


def interactive_menu(
plotprocess: Process,
summaryplotter: Callable[..., Any],
datafiles: list[str],
vectors: list[str],
Comment thread
rnyb marked this conversation as resolved.
args: argparse.Namespace,
parameterfiles: list[str],
) -> Process:
char = ""
while char != "q" and plotprocess.is_alive():
try:
char = sys.stdin.read(1)
except KeyboardInterrupt:
pass
# We have messed up the terminal, remember to fix:
termios.tcsetattr(filedesc, termios.TCSADRAIN, old_settings)
break

if char == "":
break
if char == "r":
plotprocess = restart_plotprocess(
plotprocess,
summaryplotter,
datafiles,
vectors,
args,
parameterfiles,
)

# Close plot windows (running in a subprocess)
plotprocess.terminate()
return plotprocess


if __name__ == "__main__":
Expand Down
Loading