Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Inj_Valve_fix #113

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
8 changes: 6 additions & 2 deletions completor/completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
pass

# Use more precise type information, if possible
DeviceType: TypeAlias = 'Literal["AICD", "ICD", "DAR", "VALVE", "AICV", "ICV"]'
DeviceType: TypeAlias = 'Literal["AICD", "ICD", "DAR", "VALVE", "AICV", "ICV", "INJV"]'


class Information:
Expand Down Expand Up @@ -612,7 +612,7 @@ def get_device(df_well: pd.DataFrame, df_device: pd.DataFrame, device_type: Devi
Args:
df_well: Must contain device type, device number, and the scaling factor.
df_device: Device table.
device_type: Device type. `AICD`, `ICD`, `DAR`, `VALVE`, `AICV`, `ICV`.
device_type: Device type. `AICD`, `ICD`, `DAR`, `VALVE`, `AICV`, `ICV`, `INJV`.

Returns:
Updated well information with device characteristics.
Expand All @@ -635,6 +635,10 @@ def get_device(df_well: pd.DataFrame, df_device: pd.DataFrame, device_type: Devi
# rescale the Cv
# because no scaling factor in WSEGVALV
df_well[Headers.CV_DAR] = -df_well[Headers.CV_DAR] / df_well[Headers.SCALING_FACTOR]
elif device_type == "INJV":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make oneliner, ie:
'# rescale the CV, because there are no scaling factor in WSEGVALV.'
This can be done in all occasions or replace the three to one time at the top.

# rescale the Cv
# because no scaling factor in WSEGVALV
df_well[Headers.CV_INJV] = -df_well[Headers.CV_INJV] / df_well[Headers.SCALING_FACTOR]
return df_well


Expand Down
7 changes: 7 additions & 0 deletions completor/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ class Headers:
GHF_LCF_DAR = "GHF_LCF_DAR"
GHF_HCF_DAR = "GHF_HCF_DAR"

CV_INJV = "CV_INJV"
AC_PRIMARY = "AC_PRIMARY"
AC_SECONDARY = "AC_SECONDARY"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a fan of this names, but it's ok for now

WR_CF_INJV = "WR_CF_INJV"
PRD_CF_INJV = "PRD_CF_INJV"

ALPHA = "ALPHA"
X = "X"
Y = "Y"
Expand Down Expand Up @@ -207,6 +213,7 @@ class _Keywords:
WSEGICV = "WSEGICV"
WSEGSICD = "WSEGSICD"
WSEGDAR = "WSEGDAR"
WSEGINJV = "WSEGINJV"

SCHFILE = "SCHFILE"
OUTFILE = "OUTFILE"
Expand Down
28 changes: 26 additions & 2 deletions completor/create_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class CreateOutput:
schedule: ReadSchedule object.
wells: CreateWells object.
well_name: Well name.
iwell: Well number used in creating WSEGAICV and WSEGDAR output.
iwell: Well number used in creating WSEGAICV, WSEGDAR, and WSEGINJV output.
version: Completor version information.
show_figure: Flag for pdf export of well completion schematic.
figure_no: Figure number.
Expand Down Expand Up @@ -132,6 +132,17 @@ def __init__(
{"-" * 100}{self.newline1}"""

self.print_wsegdarinit = self.print_wsegdar
self.print_wseginjv = f"""\
{'-' * 100}
-- This is how we model Injection Valve technology using sets of ACTIONX keywords.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

repeating information.
Should the line maybe be?
-- The segment dP curves changes according to the segment water rate and segment -
-- pressure drop at downhole condition.

instead?

-- The segment dP curves changes according to the segment water-
-- rate and segment pressure drop at downhole condition.
-- The value of Cv is adjusted according to the segment length and the number of
-- devices per joint. The constriction area varies according to values of
-- volume fractions.
{"-" * 100}{self.newline1}"""

self.print_wseginjvinit = self.print_wseginjv
self.print_wsegaicv = f"""\
{"-" * 100}
-- This is how we model AICV technology using sets of ACTIONX keyword
Expand Down Expand Up @@ -186,6 +197,7 @@ def __init__(
self.df_wsegsicd = po.prepare_wsegsicd(self.well_name, lateral, self.df_well, self.df_device)
self.df_wsegaicd = po.prepare_wsegaicd(self.well_name, lateral, self.df_well, self.df_device)
self.df_wsegdar = po.prepare_wsegdar(self.well_name, lateral, self.df_well, self.df_device)
self.df_wseginjv = po.prepare_wseginjv(self.well_name, lateral, self.df_well, self.df_device)
self.df_wsegaicv = po.prepare_wsegaicv(self.well_name, lateral, self.df_well, self.df_device)
self.df_wsegicv = po.prepare_wsegicv(
self.well_name,
Expand All @@ -205,6 +217,7 @@ def __init__(
self.make_wsegaicd(lateral)
self.make_wsegicv(lateral)
self.make_wsegdar()
self.make_wseginjv()
self.make_wsegaicv()

if show_figure and figure_name is not None:
Expand Down Expand Up @@ -365,6 +378,11 @@ def make_wsegdar(self) -> None:
if self.df_wsegdar.shape[0] > 0:
self.print_wsegdar += po.print_wsegdar(self.df_wsegdar, self.iwell + 1) + "\n"

def make_wseginjv(self) -> None:
"""Print WSEGINJV to file."""
if self.df_wseginjv.shape[0] > 0:
self.print_wseginjv += po.print_wseginjv(self.df_wseginjv, self.iwell + 1) + "\n"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not super fan how this is written, maybe a TODO on refactoring this selv update stuff?
But that is not relevant for this PR.


def make_wsegaicv(self) -> None:
"""Print WSEGAICV to file."""
if self.df_wsegaicv.shape[0] > 0:
Expand Down Expand Up @@ -412,7 +430,12 @@ def fix_printing(self) -> None:
self.print_wsegdar = ""
else:
self.print_wsegdar += self.newline1
# if no DAR then dont print
# if no Injection Valve then dont print
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"If there are no injection valve dont print update the print statement."
Maybe reduntant comment,

if self.print_wseginjv == self.print_wseginjvinit:
self.print_wseginjv = ""
else:
self.print_wseginjv += self.newline1
# if no AICV then dont print
if self.print_wsegaicv == self.print_wsegaicvinit:
self.print_wsegaicv = ""
else:
Expand Down Expand Up @@ -440,6 +463,7 @@ def print_per_well(self) -> None:
+ self.print_wsegsicd
+ self.print_wsegaicd
+ self.print_wsegdar
+ self.print_wseginjv
+ self.print_wsegaicv
+ self.print_wsegicv
)
Expand Down
4 changes: 3 additions & 1 deletion completor/create_wells.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def _active_wells(self) -> npt.NDArray[np.unicode_]:
Headers.DEVICE_TYPE
]
gp_check = not ann_series.isin(["OA"]).any()
perf_check = not type_series.isin(["AICD", "AICV", "DAR", "ICD", "VALVE", "ICV"]).any()
perf_check = not type_series.isin(["AICD", "AICV", "DAR", "ICD", "VALVE", "ICV", "INJV"]).any()
if gp_check and perf_check and not self.case.gp_perf_devicelayer:
# De-activate wells with GP_PERF if instructed to do so:
active_wells.remove(well_name)
Expand Down Expand Up @@ -266,6 +266,8 @@ def get_devices(self) -> None:
self.df_well = completion.get_device(self.df_well, self.case.wsegaicd_table, "AICD")
if "DAR" in active_devices:
self.df_well = completion.get_device(self.df_well, self.case.wsegdar_table, "DAR")
if "INJV" in active_devices:
self.df_well = completion.get_device(self.df_well, self.case.wseginjv_table, "INJV")
if "AICV" in active_devices:
self.df_well = completion.get_device(self.df_well, self.case.wsegaicv_table, "AICV")
if "ICV" in active_devices:
Expand Down
22 changes: 20 additions & 2 deletions completor/input_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,10 @@ def _check_for_errors(df_comp: pd.DataFrame, well_name: str, idx: int) -> None:
f"t{df_comp[Headers.END_MEASURED_DEPTH].iloc[idx - 1]} "
f"to depth {(df_comp[Headers.START_MEASURED_DEPTH].iloc[idx])}"
)
if df_comp[Headers.DEVICE_TYPE].iloc[idx] not in ["PERF", "AICD", "ICD", "VALVE", "DAR", "AICV", "ICV"]:
if df_comp[Headers.DEVICE_TYPE].iloc[idx] not in ["PERF", "AICD", "ICD", "VALVE", "DAR", "INJV", "AICV", "ICV"]:
raise CompletorError(
f"{df_comp[Headers.DEVICE_TYPE].iloc[idx]} not a valid device type. "
"Valid types are PERF, AICD, ICD, VALVE, DAR, AICV, and ICV."
"Valid types are PERF, AICD, ICD, VALVE, DAR, INJV, AICV, and ICV."
)
if df_comp[Headers.ANNULUS].iloc[idx] not in ["GP", "OA", "PA"]:
raise CompletorError(
Expand Down Expand Up @@ -250,6 +250,24 @@ def set_format_wsegdar(df_temp: pd.DataFrame) -> pd.DataFrame:
return df_temp


def set_format_wseginjv(df_temp: pd.DataFrame) -> pd.DataFrame:
"""Format the well segments Injection Valve (WSEGINJV) data.

Args:
df_temp: Well segments Injection Valve device data.

Returns:
Updated data.
"""
df_temp[Headers.DEVICE_NUMBER] = df_temp[Headers.DEVICE_NUMBER].astype(np.int64)
# left out devicenumber because it has been formatted as integer
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# left out devicenumber because it has been formatted as integer
# Left out devicenumber because it has been formatted as integer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove decivenumber?
I want capital letter and dot.

columns = df_temp.columns.to_numpy()[1:]
df_temp[columns] = df_temp[columns].astype(np.float64)
# Create ID device column
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Create ID device column
# Create ID device column.

df_temp.insert(0, Headers.DEVICE_TYPE, np.full(df_temp.shape[0], "INJV"))
return df_temp


def set_format_wsegaicv(df_temp: pd.DataFrame) -> pd.DataFrame:
"""Format the well segments automatic inflow control valve (WSEGAICV) table.

Expand Down
155 changes: 152 additions & 3 deletions completor/prepare_outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,16 +112,21 @@ def dataframe_tostring(
Headers.END_MEASURED_DEPTH: "{:.3f}".format,
Headers.CV_DAR: "{:.10g}".format,
Headers.CV: "{:.10g}".format,
Headers.CV_INJV: "{:.10g}".format,
Headers.AC: "{:.3e}".format,
Headers.AC_OIL: "{:.3e}".format,
Headers.AC_GAS: "{:.3e}".format,
Headers.AC_WATER: "{:.3e}".format,
Headers.AC_PRIMARY: "{:.3e}".format,
Headers.AC_SECONDARY: "{:.3e}".format,
Headers.AC_MAX: "{:.3e}".format,
Headers.DEFAULTS: "{:.10s}".format,
Headers.WHF_LCF_DAR: "{:.10g}".format,
Headers.WHF_HCF_DAR: "{:.10g}".format,
Headers.GHF_LCF_DAR: "{:.10g}".format,
Headers.GHF_HCF_DAR: "{:.10g}".format,
Headers.WR_CF_INJV: "{:.10g}".format,
Headers.PRD_CF_INJV: "{:.10g}".format,
Headers.ALPHA_MAIN: "{:.10g}".format,
Headers.ALPHA_PILOT: "{:.10g}".format,
}
Expand Down Expand Up @@ -438,9 +443,13 @@ def prepare_device_layer(
df_well[Headers.DEVICE_TYPE] == "DAR",
"/ -- DAR types",
np.where(
df_well[Headers.DEVICE_TYPE] == "AICV",
"/ -- AICV types",
np.where(df_well[Headers.DEVICE_TYPE] == "ICV", "/ -- ICV types", ""),
df_well[Headers.DEVICE_TYPE] == "INJV",
"/ -- Injection Valve types",
np.where(
df_well[Headers.DEVICE_TYPE] == "AICV",
"/ -- AICV types",
np.where(df_well[Headers.DEVICE_TYPE] == "ICV", "/ -- ICV types", ""),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method should be re written!
7 wheres in each other is to much.
Should create an issue about this.

),
),
),
),
Expand Down Expand Up @@ -1190,6 +1199,42 @@ def prepare_wsegdar(well_name: str, lateral: int, df_well: pd.DataFrame, df_devi
return wsegdar


def prepare_wseginjv(well_name: str, lateral: int, df_well: pd.DataFrame, df_device: pd.DataFrame) -> pd.DataFrame:
"""Prepare data frame for Injection Valve.

Args:
well_name: Well name.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Name of the well, maybe?

lateral: Lateral number.
df_well: df_well from class CreateWells.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really describing

df_device: From function prepare_device_layer for this well and this lateral.

Returns:
DataFrame for Injection Valve.
"""
df_well = df_well[df_well[Headers.LATERAL] == lateral]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be renamed, maybe to well_lateral or something

df_well = df_well[(df_well[Headers.DEVICE_TYPE] == "PERF") | (df_well[Headers.NUMBER_OF_DEVICES] > 0)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overwritten the last one, should be renamed, or remove the previous df_well definition

if df_well.shape[0] == 0:
return pd.DataFrame()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

returning an empty dataframe?
Should just return, and the None should be handled.

df_merge = pd.merge_asof(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

df_merge is not really a describing name, what is merge?
Do not need to say that it is a df in the name either

left=df_device, right=df_well, left_on=[Headers.MD], right_on=[Headers.TUB_MD], direction="nearest"
)
df_merge = df_merge[df_merge[Headers.DEVICE_TYPE] == "INJV"]
wseginjv = pd.DataFrame()
if df_merge.shape[0] > 0:
wseginjv[Headers.WELL] = [well_name] * df_merge.shape[0]
wseginjv[Headers.SEG] = df_merge[Headers.SEG].to_numpy()
# the Cv is already corrected by the scaling factor
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# the Cv is already corrected by the scaling factor
# The Cv is already corrected by the scaling factor.

wseginjv[Headers.CV_INJV] = df_merge[Headers.CV_INJV].to_numpy()
wseginjv[Headers.AC_PRIMARY] = df_merge[Headers.AC_PRIMARY].to_numpy()
wseginjv[Headers.AC_SECONDARY] = df_merge[Headers.AC_SECONDARY].to_numpy()
wseginjv[Headers.WR_CF_INJV] = -1 * df_merge[Headers.WR_CF_INJV].to_numpy()
wseginjv[Headers.PRD_CF_INJV] = -1 * df_merge[Headers.PRD_CF_INJV].to_numpy()
wseginjv[Headers.DEFAULTS] = "5*"
wseginjv[Headers.AC_MAX] = wseginjv[Headers.AC_PRIMARY].to_numpy()
wseginjv[Headers.EMPTY] = "/"
return wseginjv


def prepare_wsegaicv(well_name: str, lateral: int, df_well: pd.DataFrame, df_device: pd.DataFrame) -> pd.DataFrame:
"""Prepare data frame for AICV.

Expand Down Expand Up @@ -1368,6 +1413,110 @@ def print_wsegdar(df_wsegdar: pd.DataFrame, well_number: int) -> str:
return action


def print_wseginjv(df_wseginjv: pd.DataFrame, well_number: int) -> str:
"""Print Injection Valves devices.

Args:
df_wseginjv: Output from function prepare_wseginjv.
well_number: Well number.

Returns:
Formatted actions to be included in the output file.

Raises:
CompletorError: If there are to many wells and/or segments with Injection Valve.
"""

"""Important Information
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be included in the docstring.
No need for the "--" either


-- the valves will react on both pressure drop and rate limit ----
-- it will move to smaller nozzle, and back again if certain criteria is fulfilled ---
-- the SUVTRIG is a marker flag to check if it is on big nozzle or small nozzle --
-- primary nozzle is 0 and secondary nozzle is 1 --

"""

header = [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

two list? primary headers, secondary?

[Headers.WELL, Headers.SEG, Headers.CV_INJV, Headers.AC_PRIMARY, Headers.DEFAULTS, Headers.AC_MAX],
[Headers.WELL, Headers.SEG, Headers.CV_INJV, Headers.AC_SECONDARY, Headers.DEFAULTS, Headers.AC_MAX],
]
sign_rate = ["<"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not be a list, and the string should be written in directrly in the actions. Line 1479

sign_pressure_drop = [">"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

suvtrig = ["0", "1"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont like this name, and should maybe used explisitt? 0 is easier than suvtrig[0]

action = "UDQ\n"
for idx in range(df_wseginjv.shape[0]):
segment_number = df_wseginjv[Headers.SEG].iloc[idx]
well_name = df_wseginjv[Headers.WELL].iloc[idx]
action += f" ASSIGN SUVTRIG {well_name} {segment_number} 0 /\n"
action += "/\n\n"
iaction = 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the number 1 directly?

action += Keywords.WSEGVALV + "\n"
header_string = "--"
for itm in header[iaction]:
header_string += " " + itm
action += header_string.rstrip() + "\n"
for idx in range(df_wseginjv.shape[0]):
segment_number = df_wseginjv[Headers.SEG].iloc[idx]
print_df = df_wseginjv[df_wseginjv[Headers.SEG] == segment_number]
print_df = print_df[header[iaction]]
print_df = dataframe_tostring(print_df, True, False, False) + "\n"
action += print_df
action += "/\n\n"
for idx in range(df_wseginjv.shape[0]):
segment_number = df_wseginjv[Headers.SEG].iloc[idx]
well_name = df_wseginjv[Headers.WELL].iloc[idx]
water_segment_rate_cutoff = df_wseginjv[Headers.WR_CF_INJV].iloc[idx]
pressure_drop_cutoff = df_wseginjv[Headers.PRD_CF_INJV].iloc[idx]

iaction = 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should not be set in every loop

act_number = iaction + 1
act_name = f"INJVOP{well_number:03d}{segment_number:03d}{act_number:1d}"
if len(act_name) > 13:
raise CompletorError("Too many wells and/or too many segments with Injection Valve")
action += (
f"ACTIONX\n{act_name} 1000000 /\n"
f"SWFR '{well_name}' {segment_number} "
f"{sign_rate[iaction]} {water_segment_rate_cutoff} AND /\n"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
f"{sign_rate[iaction]} {water_segment_rate_cutoff} AND /\n"
f"< {water_segment_rate_cutoff} AND /\n"

f"SUVTRIG '{well_name}' {segment_number} "
f"= {suvtrig[iaction]} /\n/\n\n"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
f"= {suvtrig[iaction]} /\n/\n\n"
"= > /\n/\n\n"

)
print_df = df_wseginjv[df_wseginjv[Headers.SEG] == segment_number]
print_df = print_df[header[iaction]] # type: ignore
header_string = Keywords.WSEGVALV + "\n--"
for item in header[iaction]:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this loop seems unnecessary

header_string += " " + item
header_string = header_string.rstrip() + "\n"
print_df = header_string + dataframe_tostring(print_df, True, False, False) # type: ignore
print_df += "\n/\n"
print_df += f"\nUDQ\n ASSIGN SUVTRIG {well_name} {segment_number} 0 /\n/\n"
action += print_df + "\nENDACTIO\n\n"

iaction = 0
act_number = iaction + 1
act_name = f"INJVCL{well_number:03d}{segment_number:03d}{act_number:1d}"
if len(act_name) > 13:
raise CompletorError("Too many wells and/or too many segments with Injection Valve")
action += (
f"ACTIONX\n{act_name} 1000000 /\n"
f"SPRD '{well_name}' {segment_number} "
f"{sign_pressure_drop[iaction]} {pressure_drop_cutoff} AND /\n"
f"SUVTRIG '{well_name}' {segment_number} "
f"= {suvtrig[iaction]} /\n/\n\n"
)
print_df = df_wseginjv[df_wseginjv[Headers.SEG] == segment_number]
print_df = print_df[header[iaction]] # type: ignore
header_string = Keywords.WSEGVALV + "\n--"
for item in header[iaction]:
header_string += " " + item
header_string = header_string.rstrip() + "\n"
print_df = header_string + dataframe_tostring(print_df, True, False, False) # type: ignore
print_df += "\n/\n"
print_df += f"\nUDQ\n ASSIGN SUVTRIG {well_name} {segment_number} 1 /\n/\n"
action += print_df + "\nENDACTIO\n\n"

return action
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is a bit long



def print_wsegaicv(df_wsegaicv: pd.DataFrame, well_number: int) -> str:
"""Print for AICV devices.

Expand Down
Loading
Loading