Skip to content

Commit

Permalink
Merge pull request #26 from ESSS/PWPA-2291-improve-converter-api
Browse files Browse the repository at this point in the history
[PWPA-2291] Improve the converter API
  • Loading branch information
gabrielantao authored Dec 16, 2024
2 parents f71372b + 7eb56b7 commit 590eed6
Show file tree
Hide file tree
Showing 15 changed files with 145 additions and 137 deletions.
27 changes: 26 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,32 @@ Features
-----------

* Converter from Score input JSON to Alfacase
* Parser for the ALFAsim results
* Parser for the ALFAsim results and generate a JSON compatible with SCORE

How to use it
-------------
#. First, the user needs to create an instance of the converter::

from pathlib import Path
from alfasim_score.converter.alfacase.alfasim_score_converter import AlfasimScoreConverter
# path indicating where the SCORE input file is
score_input_filepath = Path("path/to/score_input.json")
# path indicating where the output file (converted from ALFAsim results) should be created
score_output_filepath = Path("path/to/score_output_result.json")
# then create a converter instance
converter = AlfasimScoreConverter(score_input_filepath, score_output_filepath)

#. To convert the SCORE input into an alfacase file, the user can do the following::

alfacase_filepath = Path("path/where/save/converted_score.alfacase")
converter.generate_alfasim_input_file(alfacase_filepath)

#. Run the ALFAsim with the generated file (and the pvt tables in the same folder)

#. Once the result file of ALFAsim is generated, one can call the converter for the output file::

alfasim_results_directory = Path("path/to/alfasim_results_folder")
converter.generate_score_output_file(alfasim_results_directory)


Development
Expand Down
1 change: 0 additions & 1 deletion src/alfasim_score/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from dataclasses import dataclass
from dataclasses import field
from enum import Enum
from pathlib import Path

from alfasim_score.constants import AIR_DENSITY_STANDARD
from alfasim_score.constants import WATER_DENSITY_STANDARD
Expand Down
26 changes: 10 additions & 16 deletions src/alfasim_score/conftest.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
import pytest
from alfasim_sdk import PluginDescription
from barril.units import Array
from barril.units import Scalar
from enum import Enum
from pathlib import Path

from alfasim_score.common import Annuli
from alfasim_score.common import Annulus
from alfasim_score.common import AnnulusDepthTable
from alfasim_score.common import AnnulusModeType
from alfasim_score.common import FluidModelPvt
from alfasim_score.common import SolidMechanicalProperties
from alfasim_score.converter.alfacase.base_operation import BaseOperationBuilder
from alfasim_score.converter.alfacase.convert_alfacase import ScoreAlfacaseConverter
from alfasim_score.converter.alfacase.injection_operation import InjectionOperationBuilder
Expand All @@ -33,20 +23,24 @@ def alfacase_gas_lift(score_input_gas_lift: ScoreInputReader) -> ScoreAlfacaseCo


@pytest.fixture
def base_operation_gas_lift(shared_datadir: Path) -> BaseOperationBuilder:
return BaseOperationBuilder(shared_datadir / SCORE_GAS_LIFT_EXAMPLE_FILENAME)
def base_operation_gas_lift(score_input_gas_lift: ScoreInputReader) -> BaseOperationBuilder:
return BaseOperationBuilder(score_input_gas_lift)


@pytest.fixture
def production_operation_gas_lift(shared_datadir: Path) -> ProductionOperationBuilder:
return ProductionOperationBuilder(shared_datadir / SCORE_GAS_LIFT_EXAMPLE_FILENAME)
def production_operation_gas_lift(
score_input_gas_lift: ScoreInputReader,
) -> ProductionOperationBuilder:
return ProductionOperationBuilder(score_input_gas_lift)


@pytest.fixture
def production_operation_natural_flow(shared_datadir: Path) -> ProductionOperationBuilder:
return ProductionOperationBuilder(shared_datadir / SCORE_NATURAL_FLOW_EXAMPLE_FILENAME)
score_input_reader = ScoreInputReader(shared_datadir / SCORE_NATURAL_FLOW_EXAMPLE_FILENAME)
return ProductionOperationBuilder(score_input_reader)


@pytest.fixture
def injection_operation(shared_datadir: Path) -> InjectionOperationBuilder:
return InjectionOperationBuilder(shared_datadir / SCORE_INJECTION_EXAMPLE_FILENAME)
score_input_reader = ScoreInputReader(shared_datadir / SCORE_INJECTION_EXAMPLE_FILENAME)
return InjectionOperationBuilder(score_input_reader)
4 changes: 3 additions & 1 deletion src/alfasim_score/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from alfasim_score.units import PRESSURE_UNIT
from alfasim_score.units import ROUGHNESS_UNIT
from alfasim_score.units import STD_VOLUMETRIC_FLOW_RATE_UNIT
from alfasim_score.units import TIME_UNIT

WELLBORE_NAME = "WELLBORE"
WELLBORE_TOP_NODE_NAME = "WELLBORE_TOP_NODE"
Expand Down Expand Up @@ -64,3 +63,6 @@
# set default value for annulus for the plugin APB
# there is no such option in the SCORE input so use this default value
HAS_FLUID_RETURN = True

# total number of walls in the output
TOTAL_WALLS = 6
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from pathlib import Path
from pytest_regressions.file_regression import FileRegressionFixture

from alfasim_score.converter.alfacase.converter_main import convert_score_to_alfacase
from alfasim_score.converter.alfacase.alfasim_score_converter import AlfasimScoreConverter


@pytest.mark.parametrize(
Expand All @@ -12,8 +12,11 @@ def test_create_alfacase_file(
shared_datadir: Path, datadir: Path, file_regression: FileRegressionFixture, score_filename: str
) -> None:
score_input = shared_datadir / f"{score_filename}.json"
alfacase_output = datadir / f"{score_filename}.alfacase"
convert_score_to_alfacase(score_input, alfacase_output)
converted_alfacase_filepath = datadir / f"{score_filename}.alfacase"
converter = AlfasimScoreConverter(score_input, Path("score_output/dummy.json"))
converter.generate_alfasim_input_file(converted_alfacase_filepath)
file_regression.check(
alfacase_output.read_text(encoding="utf-8"), encoding="utf-8", extension=".alfacase"
converted_alfacase_filepath.read_text(encoding="utf-8"),
encoding="utf-8",
extension=".alfacase",
)
29 changes: 12 additions & 17 deletions src/alfasim_score/converter/alfacase/_tests/test_output_results.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,22 @@
import json
import pandas as pd
import pytest
from barril.units import Scalar
from pathlib import Path
from pytest_regressions.file_regression import FileRegressionFixture

from alfasim_score.common import AnnulusLabel
from alfasim_score.converter.alfacase.score_output_generator import ScoreOutputGenerator
from alfasim_score.converter.alfacase.alfasim_score_converter import AlfasimScoreConverter


def test_generate_output_file_results(
shared_datadir: Path, datadir: Path, file_regression: FileRegressionFixture
) -> None:
alfasim_results_directory = shared_datadir / "case.data"
well_start_position = Scalar(2072, "m")
active_annuli = [AnnulusLabel.A, AnnulusLabel.B, AnnulusLabel.C]
# It was defined to use 6 wall layers as output from ALFAsim
layers = list(range(6))
output_generator = ScoreOutputGenerator(
alfasim_results_directory, well_start_position, active_annuli, layers
)
output_generator.element_name = "7-SRR-2-RJS (2022-07-28_15-01-27)"
output_filepath = datadir / "output_score.json"
output_generator.generate_output_file(datadir / "output_score.json")
output_content = output_filepath.read_text(encoding="utf-8")
alfasim_results_path = shared_datadir / "case.data"
# dummy input file just to have the reader for this test
score_input_file = shared_datadir / "score_input_natural_flow.json"
output_file = datadir / "output_score.json"
converter = AlfasimScoreConverter(score_input_file, output_file)
# change the element name to match this test result well name
converter.output_builder.element_name = "7-SRR-2-RJS (2022-07-28_15-01-27)"
# TODO: remember to get these annuli inside the output class
converter.output_builder.active_annuli = [AnnulusLabel.A, AnnulusLabel.B, AnnulusLabel.C]
converter.generate_score_output_file(alfasim_results_path)
output_content = converter.output_builder.score_output_filepath.read_text(encoding="utf-8")
file_regression.check(output_content, extension=".json", encoding="utf-8")
43 changes: 43 additions & 0 deletions src/alfasim_score/converter/alfacase/alfasim_score_converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import json
from alfasim_sdk import convert_description_to_alfacase
from pathlib import Path

from alfasim_score.common import OperationType
from alfasim_score.converter.alfacase.base_operation import BaseOperationBuilder
from alfasim_score.converter.alfacase.injection_operation import InjectionOperationBuilder
from alfasim_score.converter.alfacase.production_operation import ProductionOperationBuilder
from alfasim_score.converter.alfacase.score_input_reader import ScoreInputReader
from alfasim_score.converter.alfacase.score_output_generator import ScoreOutputBuilder


class AlfasimScoreConverter:
"""
This class handles the process of convertions between ALFAsim and SCORE file formats:
- it can convert the SCORE input file format into an alfacase to be used by ALFAsim simulator
- it can use a ALFAsim result into a SCORE output file.
"""

def __init__(self, score_input_file: Path, score_output_file: Path):
self.score_input = ScoreInputReader(score_input_file)
self.alfacase_builder = self._get_score_to_alfacase_builder()
self.output_builder = ScoreOutputBuilder(self.score_input, score_output_file)

def _get_score_to_alfacase_builder(self) -> BaseOperationBuilder:
"""Convert SCORE input file to an alfacase description."""
operation_type = self.score_input.read_operation_type()
if operation_type == OperationType.PRODUCTION:
return ProductionOperationBuilder(self.score_input)
else:
return InjectionOperationBuilder(self.score_input)

def generate_alfasim_input_file(self, alfacase_filepath: Path) -> None:
"""Create the ALFAsim input file (AKA alfacase) from an SCORE input file."""
alfacase_description = self.alfacase_builder.generate_operation_alfacase_description()
alfacase_content = convert_description_to_alfacase(alfacase_description)
alfacase_filepath.write_text(data=alfacase_content, encoding="utf-8")

def generate_score_output_file(self, alfasim_results_folder: Path) -> None:
"""Create the output file for SCORE based on the results generated by ALFAsim."""
output = self.output_builder.generate_output_results(alfasim_results_folder)
json_data = json.dumps(output, indent=2)
self.output_builder.score_output_filepath.write_text(json_data, encoding="utf-8")
9 changes: 2 additions & 7 deletions src/alfasim_score/converter/alfacase/base_operation.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
from typing import Any
from typing import Union

from alfasim_sdk import CaseDescription
from alfasim_sdk import CaseOutputDescription
from alfasim_sdk import EnergyModel
Expand All @@ -21,7 +18,6 @@
from alfasim_sdk import NumericalOptionsDescription
from alfasim_sdk import OutputAttachmentLocation
from alfasim_sdk import PhysicsDescription
from alfasim_sdk import PluginDescription
from alfasim_sdk import PressureContainerDescription
from alfasim_sdk import PressureNodePropertiesDescription
from alfasim_sdk import ProfileOutputDescription
Expand All @@ -39,7 +35,6 @@
from barril.units import Array
from barril.units import Scalar
from copy import deepcopy
from dataclasses import asdict
from pathlib import Path

from alfasim_score.constants import GAS_LIFT_MASS_NODE_NAME
Expand All @@ -63,8 +58,8 @@


class BaseOperationBuilder:
def __init__(self, score_filepath: Path):
self.score_input = ScoreInputReader(score_filepath)
def __init__(self, score_input_reader: ScoreInputReader):
self.score_input = score_input_reader
self.alfacase_converter = ScoreAlfacaseConverter(self.score_input)
self.apb_plugin_converter = ScoreAPBPluginConverter(self.score_input)
self.base_alfacase = self.alfacase_converter.build_base_alfacase_description()
Expand Down
6 changes: 3 additions & 3 deletions src/alfasim_score/converter/alfacase/convert_alfacase.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ def get_section_top_of_filler(


class ScoreAlfacaseConverter:
def __init__(self, score_reader: ScoreInputReader):
self.score_input = score_reader
self.general_data = score_reader.read_general_data()
def __init__(self, score_input_reader: ScoreInputReader):
self.score_input = score_input_reader
self.general_data = score_input_reader.read_general_data()
self.well_start_position = self.general_data["water_depth"] + self.general_data["air_gap"]

def get_position_in_well(self, position: Scalar) -> Scalar:
Expand Down
12 changes: 3 additions & 9 deletions src/alfasim_score/converter/alfacase/convert_plugin_data.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
from typing import Any
from typing import Dict
from typing import List
from typing import Union

import numpy as np
from alfasim_sdk import PluginDescription
from barril.units import Array
from barril.units import Scalar
from dataclasses import asdict
from dataclasses import dataclass
from enum import Enum
from pathlib import Path

from alfasim_score.common import Annuli
from alfasim_score.common import Annulus
from alfasim_score.common import AnnulusDepthTable
from alfasim_score.common import AnnulusModeType
from alfasim_score.common import AnnulusTemperatureTable
from alfasim_score.common import FluidModelPvt
from alfasim_score.common import SolidMechanicalProperties
Expand All @@ -28,9 +22,9 @@


class ScoreAPBPluginConverter:
def __init__(self, score_reader: ScoreInputReader):
self.score_input = score_reader
self.general_data = score_reader.read_general_data()
def __init__(self, score_input_reader: ScoreInputReader):
self.score_input = score_input_reader
self.general_data = score_input_reader.read_general_data()
self.well_start_position = self.general_data["water_depth"] + self.general_data["air_gap"]

def get_position_in_well(self, position: Scalar) -> Scalar:
Expand Down
28 changes: 0 additions & 28 deletions src/alfasim_score/converter/alfacase/converter_main.py

This file was deleted.

7 changes: 3 additions & 4 deletions src/alfasim_score/converter/alfacase/injection_operation.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import attr
import numpy as np
from alfasim_sdk import CaseDescription
from alfasim_sdk import MassInflowSplitType
from alfasim_sdk import MassSourceNodePropertiesDescription
Expand All @@ -11,7 +10,6 @@
from alfasim_sdk._internal.constants import FLUID_OIL
from alfasim_sdk._internal.constants import FLUID_WATER
from barril.units import Scalar
from pathlib import Path

from alfasim_score.common import FluidType
from alfasim_score.common import OperationType
Expand All @@ -20,13 +18,14 @@
from alfasim_score.constants import WELLBORE_BOTTOM_NODE_NAME
from alfasim_score.constants import WELLBORE_TOP_NODE_NAME
from alfasim_score.converter.alfacase.base_operation import BaseOperationBuilder
from alfasim_score.converter.alfacase.score_input_reader import ScoreInputReader
from alfasim_score.units import FRACTION_UNIT
from alfasim_score.units import TEMPERATURE_UNIT


class InjectionOperationBuilder(BaseOperationBuilder):
def __init__(self, score_filepath: Path):
super().__init__(score_filepath)
def __init__(self, score_input_reader: ScoreInputReader):
super().__init__(score_input_reader)
self.operation_type = OperationType.INJECTION
assert (
self.general_data["type"] == self.operation_type
Expand Down
8 changes: 3 additions & 5 deletions src/alfasim_score/converter/alfacase/production_operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from alfasim_sdk import GasLiftValveEquipmentDescription
from alfasim_sdk import HydrodynamicModelType
from alfasim_sdk import InitialConditionsDescription
from alfasim_sdk import InitialConditionStrategyType
from alfasim_sdk import InitialPressuresDescription
from alfasim_sdk import InitialTemperaturesDescription
from alfasim_sdk import InitialVelocitiesDescription
Expand All @@ -19,7 +18,6 @@
from alfasim_sdk import PressureContainerDescription
from alfasim_sdk import PressureNodePropertiesDescription
from alfasim_sdk import PvtModelCorrelationDescription
from alfasim_sdk import PvtModelsDescription
from alfasim_sdk import SimulationRegimeType
from alfasim_sdk import TableInputType
from alfasim_sdk import TemperaturesContainerDescription
Expand All @@ -31,7 +29,6 @@
from alfasim_sdk._internal.constants import FLUID_WATER
from barril.units import Array
from barril.units import Scalar
from pathlib import Path

from alfasim_score.common import LiftMethod
from alfasim_score.common import OperationType
Expand All @@ -48,6 +45,7 @@
from alfasim_score.constants import WELLBORE_BOTTOM_NODE_NAME
from alfasim_score.constants import WELLBORE_TOP_NODE_NAME
from alfasim_score.converter.alfacase.base_operation import BaseOperationBuilder
from alfasim_score.converter.alfacase.score_input_reader import ScoreInputReader
from alfasim_score.units import FRACTION_UNIT
from alfasim_score.units import LENGTH_UNIT
from alfasim_score.units import PRESSURE_UNIT
Expand All @@ -56,8 +54,8 @@


class ProductionOperationBuilder(BaseOperationBuilder):
def __init__(self, score_filepath: Path):
super().__init__(score_filepath)
def __init__(self, score_input_reader: ScoreInputReader):
super().__init__(score_input_reader)
self.operation_type = OperationType.PRODUCTION
self.lift_method_data = self.score_input.read_operation_method_data()
self.produced_fluid_data = self.score_input.read_operation_fluid_data()
Expand Down
Loading

0 comments on commit 590eed6

Please sign in to comment.