From 0b5b557f7717b47af263810461b1652bfd8d3852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Ant=C3=A3o?= Date: Wed, 19 Jun 2024 16:20:33 -0300 Subject: [PATCH] Read casing and open hole data; Fill casing and open hole data to create description; Read casing materials; Add new constants for missing valus; Add new unit for roughness; PWPA-1933 --- src/alfasim_score/constants.py | 8 ++ .../converter/alfacase/convert_alfacase.py | 69 +++++++++---- .../converter/alfacase/score_input_reader.py | 96 +++++++++++++------ src/alfasim_score/units.py | 4 +- 4 files changed, 127 insertions(+), 50 deletions(-) diff --git a/src/alfasim_score/constants.py b/src/alfasim_score/constants.py index 606955b..13ecf8a 100644 --- a/src/alfasim_score/constants.py +++ b/src/alfasim_score/constants.py @@ -1,5 +1,13 @@ +from barril.units import Scalar + +from alfasim_score.units import ROUGHNESS_UNIT + WELLBORE_TOP_NODE = "WELBORE_TOP_NODE" WELLBORE_BOTTOM_NODE = "WELBORE_BOTTOM_NODE" ANNULUS_TOP_NODE_NAME = "WELLBORE_ANNULUS_TOP_NODE" CEMENT_NAME = "cement" + +ROCK_DEFAULT_ROUGHNESS = Scalar(0.1, ROUGHNESS_UNIT) +CASING_DEFAULT_ROUGHNESS = Scalar(0.05, ROUGHNESS_UNIT) +TUBING_DEFAULT_ROUGHNESS = Scalar(0.05, ROUGHNESS_UNIT) diff --git a/src/alfasim_score/converter/alfacase/convert_alfacase.py b/src/alfasim_score/converter/alfacase/convert_alfacase.py index 3daae99..7841ed2 100644 --- a/src/alfasim_score/converter/alfacase/convert_alfacase.py +++ b/src/alfasim_score/converter/alfacase/convert_alfacase.py @@ -15,13 +15,24 @@ from barril.units import Scalar from alfasim_score.constants import ANNULUS_TOP_NODE_NAME +from alfasim_score.constants import CASING_DEFAULT_ROUGHNESS from alfasim_score.constants import CEMENT_NAME +from alfasim_score.constants import ROCK_DEFAULT_ROUGHNESS +from alfasim_score.constants import TUBING_DEFAULT_ROUGHNESS from alfasim_score.constants import WELLBORE_BOTTOM_NODE from alfasim_score.constants import WELLBORE_TOP_NODE from alfasim_score.converter.alfacase.score_input_reader import ScoreInputReader from alfasim_score.units import LENGTH_UNIT +def filter_duplicated_materials( + material_list: List[MaterialDescription], +) -> List[MaterialDescription]: + """Remove the duplicated materials parsed by the reader""" + # TODO: implement it to filter the duplicated materials + return material_list + + class ScoreAlfacaseConverter: def __init__(self, score_reader: ScoreInputReader): self.score_input = score_reader @@ -39,11 +50,13 @@ def _convert_well_trajectory(self) -> ProfileDescription: def convert_materials(self) -> List[MaterialDescription]: """Convert list of materials from SCORE file""" material_descriptions = [] - material_list = ( + material_list = filter_duplicated_materials( self.score_input.read_cement_material() + + self.score_input.read_casing_materials() + self.score_input.read_tubing_materials() + self.score_input.read_lithology_materials() ) + for data in material_list: material_descriptions.append( MaterialDescription( @@ -66,27 +79,29 @@ def _convert_formation(self) -> AnnulusDescription: return FormationDescription(reference_y_coordinate=Scalar(0.0, "m", "length")) def _convert_casing_list(self) -> List[CasingSectionDescription]: + """Create the description for the casings.""" casing_sections = [] for i, data in enumerate(self.score_input.read_casings(), start=1): - casing_sections.append( - CasingSectionDescription( - name=f"CASING_{i}", - hanger_depth=data["hanger_md"], - settings_depth=data["final_md"], - hole_diameter=data["hole_diameter"], - outer_diameter=data["outer_diameter"], - # TODO: missing values - # inner_diameter: Scalar - # inner_roughness: Scalar - # material: Optional[str] = None - top_of_filler=data["top_of_cement"], - filler_material=CEMENT_NAME, - # material_above_filler: Optional[str] = None + for section in data["sections"]: + casing_sections.append( + CasingSectionDescription( + name=f"{data['function']}_{data['type']}_{i}", + hanger_depth=section["top_md"], + settings_depth=section["base_md"], + hole_diameter=data["hole_diameter"], + outer_diameter=section["outer_diameter"], + inner_diameter=section["inner_diameter"], + inner_roughness=CASING_DEFAULT_ROUGHNESS, + material=section["material"], + top_of_filler=data["top_of_cement"], + filler_material=CEMENT_NAME, + material_above_filler=data["material_above"], + ) ) - ) return casing_sections def _convert_tubing_list(self) -> List[TubingDescription]: + """Create the description for the tubing list.""" tubing_sections = [] for i, data in enumerate(self.score_input.read_tubing(), start=1): tubing_sections.append( @@ -95,8 +110,7 @@ def _convert_tubing_list(self) -> List[TubingDescription]: length=data["base_md"] - data["top_md"], outer_diameter=data["outer_diameter"], inner_diameter=data["inner_diameter"], - # TODO: set right the value for roughness... - inner_roughness=Scalar(0.0, "mm"), + inner_roughness=TUBING_DEFAULT_ROUGHNESS, material=data["material"], ) ) @@ -116,7 +130,24 @@ def _convert_packer_list(self) -> List[PackerDescription]: return packers def _convert_open_hole_list(self) -> List[OpenHoleDescription]: - return [] + """Create the description for the open hole.""" + open_hole = [] + start_position = Scalar( + max([data["shoe_md"] for data in self.score_input.read_casings()]), + LENGTH_UNIT, + "length", + ) + for i, data in enumerate(self.score_input.read_tubing(), start=1): + open_hole.append( + OpenHoleDescription( + name=f"OPEN_HOLE_{i}", + length=data["final_md"] - start_position, + diameter=data["hole_diameter"], + inner_roughness=ROCK_DEFAULT_ROUGHNESS, + ) + ) + start_position = data["final_md"] + return open_hole def _convert_casings(self) -> WellDescription: """Create the description for the casings.""" diff --git a/src/alfasim_score/converter/alfacase/score_input_reader.py b/src/alfasim_score/converter/alfacase/score_input_reader.py index 3e2cdd9..728572a 100644 --- a/src/alfasim_score/converter/alfacase/score_input_reader.py +++ b/src/alfasim_score/converter/alfacase/score_input_reader.py @@ -24,9 +24,10 @@ class WellItemType(StrEnum): DRILLING = "DRILLING" CASING = "CASING" + NONE = "NONE" -class WellItemInterval(StrEnum): +class WellItemFunction(StrEnum): CONDUCTOR = "CONDUCTOR" SURFACE = "SURFACE" PRODUCTION = "PRODUCTION" @@ -67,15 +68,37 @@ def read_tubing_materials(self) -> List[Dict[str, Union[Scalar, str]]]: ) return tubing_data + def read_casing_materials(self) -> List[Dict[str, Union[Scalar, str]]]: + """Read the data for the casing from SCORE input file.""" + casing_data = [] + for item in self.input_content["operation"]["thermal_simulation"]["well_strings"]: + for section in item["string_sections"]: + properties = section["pipe"]["grade"]["thermomechanical_property"] + casing_data.append( + { + "name": section["pipe"]["grade"]["name"], + "density": Scalar(properties["density"], DENSITY_UNIT), + "thermal_conductivity": Scalar( + properties["thermal_conductivity"], THERMAL_CONDUCTIVITY_UNIT + ), + "specific_heat": Scalar(properties["specific_heat"], SPECIFIC_HEAT_UNIT), + "thermal_expansion": Scalar( + properties["thermal_expansion_coefficient"], THERMAL_EXPANSION_UNIT + ), + "young_modulus": Scalar(properties["e"], YOUNG_MODULUS_UNIT), + "poisson_ratio": Scalar(properties["nu"], FRACTION_UNIT), + } + ) + return casing_data + def read_cement_material(self) -> List[Dict[str, Union[Scalar, str]]]: """ Read the data for the cement from SCORE input file. This method assumes all configured cement properties are the same and that the first_slurry and second_slurry have the same properties. """ - properties = self.input_content["well_strings"][0]["cementing"]["first_slurry"][ - "thermomechanical_property" - ] + well_strings = self.input_content["operation"]["thermal_simulation"]["well_strings"] + properties = well_strings[0]["cementing"]["first_slurry"]["thermomechanical_property"] return [ { "name": CEMENT_NAME, @@ -113,27 +136,47 @@ def read_lithology_materials(self) -> List[Dict[str, Union[Scalar, str]]]: ) return lithology_data - def read_casings(self) -> List[Dict[str, Scalar | str]]: - """ "Read the data for the casing from SCORE input file""" + def read_casings(self) -> List[Dict[str, Any]]: + """Read the data for the casing from SCORE input file""" casing_data = [] - for section in self.input_content["well_strings"]: - if section["interval"] != WellItemInterval.OPEN.value: + for item in self.input_content["operation"]["thermal_simulation"]["well_strings"]: + if item["interval"] != WellItemFunction.OPEN.value: casing_data.append( { - "hanger_md": Scalar(section["hanger_md"], LENGTH_UNIT, "length"), - "shoe_md": Scalar(section["shoe_md"], LENGTH_UNIT, "length"), - "final_md": Scalar(section["final_md"], LENGTH_UNIT, "length"), - "top_of_cement": Scalar(section["toc_md"], LENGTH_UNIT, "length"), - "hole_diameter": Scalar(section["hole_size"], DIAMETER_UNIT, "diameter"), - # TODO: check missing inner diameter here - # "inner_diameter": Scalar(inner_diameter, DIAMETER_UNIT, "diameter"), - "outer_diameter": Scalar(section["od"], DIAMETER_UNIT, "diameter"), + "type": WellItemType(item["type"]) if "type" in item else WellItemType.NONE, + "function": WellItemFunction(item["interval"]), + "hanger_md": Scalar(item["hanger_md"], LENGTH_UNIT, "length"), + "shoe_md": Scalar(item["shoe_md"], LENGTH_UNIT, "length"), + "final_md": Scalar(item["final_md"], LENGTH_UNIT, "length"), + "top_of_cement": Scalar(item["toc_md"], LENGTH_UNIT, "length"), + # TODO: check how to get this material obove filler + "material_above_filler": None, + "hole_diameter": Scalar(item["hole_size"], DIAMETER_UNIT, "diameter"), + # TODO: check if these diameters should be used here + # "inner_diameter":, + # "outer_diameter": Scalar(item["od"], DIAMETER_UNIT, "diameter"), + "sections": [ + { + "material": section["pipe"]["grade"]["name"], + "top_md": Scalar(section["top_md"], LENGTH_UNIT, "length"), + "base_md": Scalar(section["base_md"], LENGTH_UNIT, "length"), + "inner_diameter": Scalar( + section["pipe"]["od"] - 2.0 * section["pipe"]["wt"], + DIAMETER_UNIT, + "diameter", + ), + "outer_diameter": Scalar( + section["pipe"]["od"], DIAMETER_UNIT, "diameter" + ), + } + for section in item["string_sections"] + ], } ) return casing_data def read_tubing(self) -> List[Dict[str, Any]]: - """ "Read the data for the tubing from SCORE input file""" + """Read the data for the tubing from SCORE input file""" tubing_data = [] for section in self.input_content["operation"]["tubing_string"]["string_sections"]: outer_radius = section["pipe"]["od"] / 2.0 @@ -150,8 +193,8 @@ def read_tubing(self) -> List[Dict[str, Any]]: ) return tubing_data - def read_packers(self) -> List[Dict[str, Scalar | str]]: - """ "Read the data for the packers from SCORE input file""" + def read_packers(self) -> List[Dict[str, Any]]: + """Read the data for the packers from SCORE input file""" packer_data = [] for component in self.input_content["operation"]["tubing_string"]["components"]: if component["component"]["type"] == "PACKER": @@ -160,26 +203,21 @@ def read_packers(self) -> List[Dict[str, Scalar | str]]: "name": component["name"], "position": Scalar(component["depth"], LENGTH_UNIT, "length"), # TODO: get material above from somewhere else - "material_above": "", + # well_string items have the annular_fluids, but the fluids don't have properties in file + "material_above": None, } ) return packer_data def read_open_hole(self) -> List[Dict[str, Scalar | str]]: - """ "Read the data for the open hole from SCORE input file""" + """Read the data for the open hole from SCORE input file""" casing_data = [] - for section in self.input_content["well_strings"]: - if section["interval"] == WellItemInterval.OPEN.value: + for section in self.input_content["operation"]["thermal_simulation"]["well_strings"]: + if section["interval"] == WellItemFunction.OPEN.value: casing_data.append( { - "hanger_md": Scalar(section["hanger_md"], LENGTH_UNIT, "length"), - "shoe_md": Scalar(section["shoe_md"], LENGTH_UNIT, "length"), "final_md": Scalar(section["final_md"], LENGTH_UNIT, "length"), - "top_of_cement": Scalar(section["toc_md"], LENGTH_UNIT, "length"), "hole_diameter": Scalar(section["hole_size"], DIAMETER_UNIT, "diameter"), - # TODO: check missing inner diameter here - # "inner_diameter": Scalar(inner_diameter, DIAMETER_UNIT, "diameter"), - "outer_diameter": Scalar(section["od"], DIAMETER_UNIT, "diameter"), } ) return casing_data diff --git a/src/alfasim_score/units.py b/src/alfasim_score/units.py index 574a0e6..6f9a639 100644 --- a/src/alfasim_score/units.py +++ b/src/alfasim_score/units.py @@ -7,7 +7,7 @@ THERMAL_EXPANSION_UNIT = "1/K" YOUNG_MODULUS_UNIT = "psi" FRACTION_UNIT = "-" - +ROUGHNESS_UNIT = "mm" DENSITY_UNIT = "" THERMAL_CONDUCTIVITY_UNIT = "W/m.K" -SPECIFIT_HEAT_UNIT = "J/kg.K" \ No newline at end of file +SPECIFIT_HEAT_UNIT = "J/kg.K"