Source code for kappa_sdk.field

from typing import List, Optional, Dict, Any, Literal
from ._private._field_dto import FieldDtoWithHierarchy
from .well_production_enum import WellProductionTypeEnum
from .well import Well
from .well_group import WellGroup
from .file import File
from .document import Document
from ._private._cluster_apis import ClusterAPIS
from datetime import datetime
from .field_data_types_catalog import FieldDataTypesCatalog
from .field_well_properties_catalog import FieldWellPropertiesCatalog
from ._private.dto_converters._field_dto_converter import FieldDtoConverter
from .file_folder import FileFolder
from .file_folders_extensions import find_file_folder_recursively_by_id
from .pvt.pvt import PVT
from .gas_oil_type_enum import GasOilTypeEnum
from .unit_system_pvt_enum import UnitSystemPvtEnum
from .artm_model_enum import ARTMModelEnum
from .artm_improve_target_enum import ARTMImproveTargetEnum
from .artm_regression_algorithm_enum import ARTMRegressionAlgorithmEnum
from .artm_forecast_type_enum import ARTMForecastTypeEnum


[docs] class Field: """ Field object. Presents a KAPPA Automate field object that can be queried for contained wells. Returned as a result of the :meth:`Connection.get_fields` query. .. note:: Should not be instantiated directly. .. note:: :py:obj:`Field.wells` property is populated on-demand and is cached for the duration of the :class:`Connection`. """ def __init__(self, field_id: str, name: str, asset: str, field_dto: Optional[FieldDtoWithHierarchy], cluster_apis: ClusterAPIS, dto_converter: FieldDtoConverter) -> None: self.__id: str = field_id self.__name: str = name self.__asset: str = asset self.__reference_date: Optional[datetime] = None self.__field_dto: Optional[FieldDtoWithHierarchy] = field_dto self.__wells: Optional[List[Well]] = None self.__well_groups: Optional[List[WellGroup]] = None self.__files: Optional[List[File]] = None self.__documents: Optional[List[Document]] = None self.__cluster_apis: ClusterAPIS = cluster_apis self.__dto_converter: FieldDtoConverter = dto_converter self.__data_types_catalog: Optional[FieldDataTypesCatalog] = None self.__well_properties_catalog: Optional[FieldWellPropertiesCatalog] = None self.__file_folders: Optional[List[FileFolder]] = None self.__pvts: Optional[List[PVT]] = None def __get_field_dto(self) -> FieldDtoWithHierarchy: if self.__field_dto is None: self.__field_dto = self.__cluster_apis.field_api.get_field_dto(self.__id) return self.__field_dto @property def id(self) -> str: """ Gets the id of the :class:`Field` object. """ return self.__id @property def name(self) -> str: """ Gets the name of the :class:`Field`. """ return self.__name @property def asset(self) -> str: """ Gets the asset of the :class:`Field`. """ return self.__asset @property def data_types_catalog(self) -> FieldDataTypesCatalog: """Get the Field Data Types catalog object of the :class:`Field`""" if self.__data_types_catalog is None: self.__data_types_catalog = self.__dto_converter.get_field_data_types_catalog_from_data_type_catalog_dto(self.__id, self.__get_field_dto().dataTypeCatalog) return self.__data_types_catalog @property def well_properties_catalog(self) -> FieldWellPropertiesCatalog: """Get the Field Well Properties catalog object of the :class:`Field`""" if self.__well_properties_catalog is None: self.__well_properties_catalog = self.__dto_converter.get_field_well_properties_catalog_from_well_property_catalog_dto(self.__id, self.__get_field_dto().wellPropertyCatalog) return self.__well_properties_catalog @property def reference_date(self) -> datetime: """ Gets the reference date of the :class:`Field`. """ if self.__reference_date is None: self.__reference_date = self.__get_field_dto().referenceDate return self.__reference_date @property def wells(self) -> List[Well]: """ Gets the list of all wells contained in the :class:`Field`, including contained in the well groups. .. note:: This property is populated on-demand and is cached for the duration of the :class:`Connection`. """ if self.__wells is None: self.__wells = self.__dto_converter.get_wells_by_field_from_field_dto(self.id, self.__get_field_dto(), self.data_types_catalog, self.well_properties_catalog) return self.__wells @property def well_groups(self) -> List[WellGroup]: """ Gets the list of all well groups contained in the :class:`Field`. .. note:: This property is populated on-demand and is cached for the duration of the :class:`Connection`. """ if self.__well_groups is None: self.__well_groups = self.__dto_converter.get_well_groups_recursively(self.id, self.__get_field_dto(), self.data_types_catalog, self.well_properties_catalog) return self.__well_groups if self.__well_groups is not None else list() @property def files(self) -> List[File]: """ Gets the list of files contained in this :class:`Field` and its well groups. .. note:: This property is populated on-demand and is cached for the duration of the :class:`Connection`. """ if self.__files is None: self.__files = self.__dto_converter.well_dto_converter.get_files_recursively(self.__id, None, self.__get_field_dto()) return self.__files @property def file_folders(self) -> List[FileFolder]: """ Gets the list of file folders contained in this :class:`Well`. .. note:: This property is populated on-demand and is cached for the duration of the :class:`Connection`. """ if self.__file_folders is None: self.__file_folders = self.__dto_converter.well_dto_converter.file_dto_converter.get_file_folders_from_file_folder_dto_recursively(self.__id, None, None, None, None, self.__get_field_dto().fileFolders) return self.__file_folders @property def documents(self) -> List[Document]: """ Gets the list of KW documents contained in this :class:`Field` and its well groups. .. note:: This property is populated on-demand and is cached for the duration of the :class:`Connection`. """ document_list = list() for file in self.files: try: document = file.as_kw_document() except ValueError: document = None if document is not None: document_list.append(document) return document_list def upload_file(self, file_path: str, file_folder_id: Optional[str] = None, overwrite: bool = False) -> File: """ Uploads a file to this :class:`Field`. Parameters ---------- file_path: Full path and name of the file to upload. overwrite: A value indicating whether to overwrite a file with the same name if it already exists in the field. file_folder_id: Id of the file folder to upload the file Returns ------- :class:`File`: An uploaded file object. """ if file_folder_id is not None: file_folder = find_file_folder_recursively_by_id(file_folder_id, self.file_folders) if file_folder is None: raise ValueError(f"Missing File folder {file_folder_id} in field {self.__name}") file_dto = self.__cluster_apis.field_api.upload_file_to_file_folder_in_field(self.__id, file_folder.id, file_path, overwrite) else: file_dto = self.__cluster_apis.field_api.upload_file_to_field(self.__id, file_path, overwrite) file = self.__dto_converter.well_dto_converter.file_dto_converter.build_file_from_file_dto(None, file_dto) self.files.append(file) return file def refresh_data(self) -> None: """ Clean all attributes and dto, to grab updated attributes. """ self.__field_dto = None self.__data_types_catalog = None self.__well_properties_catalog = None self.__reference_date = None self.__wells = None self.__well_groups = None self.__files = None self.__pvts = None def create_well(self, name: str, uwi: Optional[str] = None, comment: Optional[str] = None, production_type: WellProductionTypeEnum = WellProductionTypeEnum.unknown, labels: Optional[List[str]] = None, well_properties_values: Optional[List[Dict[str, Any]]] = None) -> Well: """ Create a new well under the field associated to this :class:`Field` Parameters ---------- name: Name of the new well uwi: Unique well identifier of the new well comment: Any description production_type: Production type of the new well, unknown by default labels: Labels of the new well well_properties_values: You can fill the well properties values, it has to be a dictionary following this format {'alias_of_the_well_property':value} Returns ------- :class:`Well`: The new well """ payload = self.__dto_converter.well_group_dto_converter.get_create_well_payload(name, uwi, comment, production_type, labels, well_properties_values) well = self.__dto_converter.well_group_dto_converter.build_well(self.id, None, self.__cluster_apis.field_api.create_well(self.__id, None, payload), self.data_types_catalog, self.well_properties_catalog) if self.__wells is not None: self.__wells.append(well) return well def rename(self, new_name: str) -> None: """ Rename the field Parameters ---------- new_name: New name of the field """ self.__cluster_apis.field_api.rename_field(self.__id, {"name": new_name}) self.__name = new_name def delete(self) -> None: """ Delete the field""" self.__cluster_apis.field_api.delete_field(self.__id) def __str__(self) -> str: """String representation of the Field.""" return f"Field(name='{self.name}', id='{self.id}')" def __repr__(self) -> str: """Detailed representation of the Field.""" return f"Field(id='{self.id}', name='{self.name}', asset='{self.asset}')" @property def pvts(self) -> List[PVT]: """ Gets the list of PVTs contained in this :class:`Field`. .. note:: This property is populated on-demand and is cached for the duration of the :class:`Connection`. """ if self.__pvts is None: self.__pvts = self.__dto_converter.well_dto_converter.get_pvts_from_pvts_dto(self.__id, None, self.__get_field_dto().pvts) return self.__pvts def create_pvt_from_file(self, pvt_name: str, file_id: str, start_date: Optional[datetime] = None, reservoir_pressure: Optional[float] = None, reservoir_temperature: Optional[float] = None, gas_oil_type: Optional[GasOilTypeEnum] = None, unit_system: Optional[UnitSystemPvtEnum] = None) -> PVT: """ Creates a PVT (Pressure-Volume-Temperature) object from a file. You can define fallback parameters when the gas oil type is undetermined. Parameters ---------- pvt_name : str The name of the PVT object to be created. file_id : str The identifier of the file from which the PVT object will be created. start_date : datetime, optional The start date for the PVT data coverage. Defaults to None. reservoir_pressure : float, optional The pressure of the reservoir associated with the PVT object. Defaults to None. reservoir_temperature : float, optional The temperature of the reservoir associated with the PVT object. Defaults to None. gas_oil_type : GasOilTypeEnum, optional The type of gas or oil associated with the PVT object, as per the enumerated GasOilTypeEnum. Defaults to None. unit_system : UnitSystemPvtEnum, optional The unit system used for the PVT object, as per the enumerated UnitSystemPvtEnum. Defaults to None. Returns ------- PVT An instance of the PVT object created using the provided parameters and data from the specified text file. """ dto = self.__dto_converter.well_dto_converter.get_command_pvt_from_text_file_dto(pvt_name, self.__id, None, file_id, start_date, reservoir_pressure, reservoir_temperature, gas_oil_type, unit_system) pvt = self.__dto_converter.well_dto_converter.build_pvt(self.__id, None, self.__cluster_apis.automation_api.create_pvt_from_text_file_field(self.id, dto)) self.pvts.append(pvt) return pvt def create_pvt_from_kw_document(self, pvt_name: str, document_id: str, analysis_id: str) -> PVT: """ Create a PVT object in the well group from a KW document Parameters ---------- pvt_name: str Name of the PVT object to create document_id: str Id of the document to use analysis_id: str Id of the analysis to use Returns ------- PVT The PVT object created. """ dto = self.__dto_converter.well_dto_converter.get_command_pvt_from_kw_document_dto(pvt_name, self.__id, None, document_id, analysis_id) pvt = self.__dto_converter.well_dto_converter.build_pvt(self.__id, None, self.__cluster_apis.automation_api.create_pvt_from_kw_document_field(self.id, dto)) self.pvts.append(pvt) return pvt def create_automatic_rtm_aggregator(self, pvt_document: Document, name: str = "Automatic RTM Agg #1", labels: Optional[List[str]] = None, use_black_oil_pvt: bool = False, pressure_data_type: str = "BHP", pressure_label: Optional[List[str]] = None, use_oil_rate: Optional[bool] = None, use_gas_rate: Optional[bool] = None, use_water_rate: Optional[bool] = None, wells: Optional[List[Well]] = None, power_law_segments: Literal[1, 2] = 1, artm_model: ARTMModelEnum = ARTMModelEnum.mzfd, improve_target: ARTMImproveTargetEnum = ARTMImproveTargetEnum.cumulative, regression_algorithm: ARTMRegressionAlgorithmEnum = ARTMRegressionAlgorithmEnum.genetic_algorithm, replicate_results_to_master_container: bool = False, use_equivalent_rate: bool = False, forecast_duration_hours: float = 2160, forecast_type: ARTMForecastTypeEnum = ARTMForecastTypeEnum.constant_pressure, forecast_value: Optional[float] = None, mandatory_well_properties: Optional[List[str]] = None) -> None: if wells is None: wells = self.wells if len(wells) == 0: raise ValueError("No well in the field to create the RTM aggregator") artm_parameters = pvt_document.get_artm_parameters() artm_creation_dto = self.__dto_converter.get_artm_aggretor_creation_dto(self.__id, name, [x.id for x in wells], pvt_document.file_id, artm_parameters, use_black_oil_pvt, pressure_data_type, pressure_label, use_oil_rate, use_gas_rate, use_water_rate, power_law_segments, artm_model, improve_target, regression_algorithm, replicate_results_to_master_container, use_equivalent_rate, forecast_duration_hours, forecast_type, forecast_value, mandatory_well_properties, labels) self.__cluster_apis.automation_api.create_automatic_rtm_aggregator_field(self.__id, artm_creation_dto)