friction_loss.inputs.read_test_data
1import io 2import json 3import datetime as dt 4from typing import Dict, Union, List, Tuple 5 6import numpy as np 7from custom_plugin_base.server.calculation import SolveRequest 8from custom_plugin_base.services.clients.project_client import ProjectClient 9from custom_plugin_base.services.clients.unisum_client import UnisumClient 10from custom_plugin_base.utils.service_enum import (CategoryGaugeEnum, PocketTypeEnum, LangEnum, 11 TypeFluidEnum, PvtPropertyEnum) 12 13from friction_loss.utils.const import DAYS_TO_SECONDS, P_MIN_SIMPLE_PVT, P_MAX_SIMPLE_PVT, N_POINTS_SIMPLE_PVT 14from friction_loss.utils.custom_plugin_utils import get_entities, datetime_from_message 15from friction_loss.utils.errors import (get_message_incorrect_well_type, get_message_incorrect_pocket_rate, 16 get_message_incorrect_pocket_rate_value, get_message_incorrect_pocket_pressure, 17 get_message_incorrect_pocket_pressure_value) 18from friction_loss.utils.plugin_dataclasses import ProjectInfo, TransientData 19 20 21def read_test_data(request: SolveRequest) -> (np.ndarray, np.ndarray, np.ndarray, bytes): 22 """ 23 Reading data from well gauges and converting to system SI 24 25 Parameters 26 ---------- 27 request: SolveRequest 28 Input data 29 30 Returns 31 ------- 32 data_transient : np.ndarray(dict) 33 keys : Index flow rate. 34 value : Dataclass with phase flow rate and pressure point values. 35 is_producer : np.ndarray(bool) 36 Flag of producer wells (True - producer wells; False - injection wells). 37 gauge_calc_pocket : np.ndarray(dict) 38 Data for sending pressure to the pocket Calc 39 pvt: bytes 40 Bytes of json file with PVT table data 41 """ 42 43 entities = get_entities(request) 44 wells = entities[request.test_id].WellIds 45 pvt = _read_pvt(entities, entities[request.test_id]) 46 transients_data = np.empty(len(wells), object) 47 is_producer = np.empty(len(wells), bool) 48 gauge_calc_pocket = np.empty(len(wells), object) 49 for idx, well_id in enumerate(wells): 50 data_transient, is_producer[idx], gauge_calc_pocket[idx] = _read_test_data(entities, well_id) 51 transients_data[idx] = _convert_to_unit_si(data_transient) 52 53 return transients_data, is_producer, gauge_calc_pocket, pvt 54 55 56def _read_pvt(entities: Dict[str, object], test) -> bytes: 57 """ 58 Reading PVT Table Data. 59 60 Parameters 61 ---------- 62 entities: dict[str, object] 63 A dictionary that allows you to get an entity by Id. 64 test: TestMessage 65 Test for which calculation is performed. 66 67 Returns 68 ------- 69 byte_data: bytes 70 PVT table bytes in JSON format 71 """ 72 byte_stream = io.BytesIO() 73 74 pvt = entities[test.PvtIds[-1]] 75 curves = [entities[i] for i in pvt.CurveIds] 76 parameters = [entities[i] for i in pvt.PvtParameterValueIds] 77 78 temperature = [i for i in parameters if i.ParameterName == 'Temperature'][0].Value 79 80 if len(curves) == 0: 81 p_axis = list(np.linspace(P_MIN_SIMPLE_PVT, P_MAX_SIMPLE_PVT, N_POINTS_SIMPLE_PVT)) 82 ones = np.ones(N_POINTS_SIMPLE_PVT) 83 zeros = list(np.zeros(N_POINTS_SIMPLE_PVT)) 84 85 mu_o = list([i for i in parameters if i.ParameterName == 'FluidViscosity'][0].Value * ones) 86 rho_o = list([i for i in parameters if i.ParameterName == 'FluidDensity'][0].Value * ones) 87 C_o = list([i for i in parameters if i.ParameterName == 'FluidCompressibility'][0].Value * ones) 88 B_g = B_w = B_o = list([i for i in parameters if i.ParameterName == 'FormationVolumeFactor'][0].Value * ones) 89 mu_w = rho_w = C_w = zeros 90 mu_g = rho_g = C_g = Z_f = R_s = zeros 91 92 else: 93 p_axis = list((i for i in curves[-1].Curve.X)) 94 zeros = list(np.zeros(len(p_axis))) 95 ones = list(np.ones(len(p_axis))) 96 types_fluids = [i.CurveSetting.FluidType for i in curves] 97 98 # Нефть 99 if TypeFluidEnum.OIL.get_int_value() in types_fluids or TypeFluidEnum.DEAD_OIL.get_int_value() in types_fluids: 100 mu_o = list([i.Curve.Values for i in curves if 101 PvtPropertyEnum(i.CurveSetting.FluidParameter) == PvtPropertyEnum.Mu and 102 TypeFluidEnum(i.CurveSetting.FluidType) in [TypeFluidEnum.OIL, TypeFluidEnum.DEAD_OIL]][0]) 103 rho_o = list([i.Curve.Values for i in curves if 104 PvtPropertyEnum(i.CurveSetting.FluidParameter) == PvtPropertyEnum.Rho and 105 TypeFluidEnum(i.CurveSetting.FluidType) in [TypeFluidEnum.OIL, TypeFluidEnum.DEAD_OIL]][0]) 106 B_o = list([i.Curve.Values for i in curves if 107 PvtPropertyEnum(i.CurveSetting.FluidParameter) == PvtPropertyEnum.B and 108 TypeFluidEnum(i.CurveSetting.FluidType) in [TypeFluidEnum.OIL, TypeFluidEnum.DEAD_OIL]][0]) 109 C_o = list([i.Curve.Values for i in curves if 110 PvtPropertyEnum(i.CurveSetting.FluidParameter) == PvtPropertyEnum.C and 111 TypeFluidEnum(i.CurveSetting.FluidType) in [TypeFluidEnum.OIL, TypeFluidEnum.DEAD_OIL]][0]) 112 else: 113 mu_o = rho_o = C_o = zeros 114 B_o = ones 115 116 # Вода 117 if TypeFluidEnum.WATER.get_int_value() in types_fluids: 118 mu_w = list([i.Curve.Values for i in curves if 119 PvtPropertyEnum(i.CurveSetting.FluidParameter) == PvtPropertyEnum.Mu and 120 TypeFluidEnum(i.CurveSetting.FluidType) == TypeFluidEnum.WATER][0]) 121 rho_w = list([i.Curve.Values for i in curves if 122 PvtPropertyEnum(i.CurveSetting.FluidParameter) == PvtPropertyEnum.Rho and 123 TypeFluidEnum(i.CurveSetting.FluidType) == TypeFluidEnum.WATER][0]) 124 B_w = list([i.Curve.Values for i in curves if 125 PvtPropertyEnum(i.CurveSetting.FluidParameter) == PvtPropertyEnum.B and 126 TypeFluidEnum(i.CurveSetting.FluidType) == TypeFluidEnum.WATER][0]) 127 C_w = list([i.Curve.Values for i in curves if 128 PvtPropertyEnum(i.CurveSetting.FluidParameter) == PvtPropertyEnum.C and 129 TypeFluidEnum(i.CurveSetting.FluidType) == TypeFluidEnum.WATER][0]) 130 else: 131 mu_w = rho_w = C_w = zeros 132 B_w = ones 133 134 # Газ 135 if TypeFluidEnum.GAS.get_int_value() in types_fluids: 136 mu_g = list([i.Curve.Values for i in curves if 137 PvtPropertyEnum(i.CurveSetting.FluidParameter) == PvtPropertyEnum.Mu and 138 TypeFluidEnum(i.CurveSetting.FluidType) == TypeFluidEnum.GAS][0]) 139 rho_g = list([i.Curve.Values for i in curves if 140 PvtPropertyEnum(i.CurveSetting.FluidParameter) == PvtPropertyEnum.Rho and 141 TypeFluidEnum(i.CurveSetting.FluidType) == TypeFluidEnum.GAS][0]) 142 B_g = list([i.Curve.Values for i in curves if 143 PvtPropertyEnum(i.CurveSetting.FluidParameter) == PvtPropertyEnum.B and 144 TypeFluidEnum(i.CurveSetting.FluidType) == TypeFluidEnum.GAS][0]) 145 C_g = list([i.Curve.Values for i in curves if 146 PvtPropertyEnum(i.CurveSetting.FluidParameter) == PvtPropertyEnum.C and 147 TypeFluidEnum(i.CurveSetting.FluidType) == TypeFluidEnum.GAS][0]) 148 Z_f = list([i.Curve.Values for i in curves if 149 PvtPropertyEnum(i.CurveSetting.FluidParameter) == PvtPropertyEnum.Z][0]) 150 R_s = list([i.Curve.Values for i in curves if 151 PvtPropertyEnum(i.CurveSetting.FluidParameter) == PvtPropertyEnum.Rs][0]) 152 else: 153 mu_g = rho_g = C_g = Z_f = R_s = zeros 154 B_g = ones 155 156 pvt_dict = {"temperatures [C]": [temperature * 0.95, temperature], 157 # TODO временное решение (полипрес требует 2 и более температуры, хотя использует только одну:)) 158 "pressures [bar]": p_axis, 159 "Rs [m3/m3]": [R_s, R_s], 160 "dynamic viscosity water [cps]": [mu_w, mu_w], 161 "dynamic viscosity oil [cps]": [mu_o, mu_o], 162 "dynamic viscosity gas [cps]": [mu_g, mu_g], 163 "density water [kg/m3]": [rho_w, rho_w], 164 "density oil [kg/m3]": [rho_o, rho_o], 165 "density gas [kg/m3]": [rho_g, rho_g], 166 "B_w [m3/m3]": [B_w, B_w], 167 "B_o [m3/m3]": [B_o, B_o], 168 "B_g [m3/m3] (st)": [B_g, B_g], 169 "C_w [1/bar]": [C_w, C_w], 170 "C_o [1/bar]": [C_o, C_o], 171 "C_g [1/bar]": [C_g, C_g], 172 "Z_factor gas": [Z_f, Z_f]} 173 174 # Записываем данные в байтовый поток в формате JSON 175 with io.TextIOWrapper(byte_stream, encoding='utf-8', write_through=True) as text_stream: 176 json.dump(pvt_dict, text_stream) 177 byte_data = byte_stream.getvalue() 178 179 return byte_data 180 181 182def _read_test_data(entities: Dict[str, object], well_id: str) -> Tuple[Dict[int, TransientData], bool, 183 Dict[str, Union[str, PocketTypeEnum, np.ndarray]]]: 184 """ 185 Returns a dictionary containing arrays of pressure and flow rates by phases from the RawData pocket. 186 It is assumed that the well has one pressure gauge marked with a tick. 187 188 Parameters 189 ---------- 190 entities: dict[str, object] 191 A dictionary that allows you to get an entity by Id. 192 well_id: str 193 Well ID. 194 195 Returns 196 ------- 197 data_transient : dict 198 keys : Index flow rate. 199 value : Dataclass with phase flow rate and pressure point values. 200 is_producer : bool 201 Flag of producer wells (True - producer wells; False - injection wells). 202 gauge_calc_pocket : dict 203 Data for sending pressure to the pocket Calc 204 """ 205 well = entities[well_id] 206 calendar_time, pressure, pressure_unit, time, id_reference_p_gauge = _read_pressure_pocket(well, entities) 207 208 q_o, q_o_unit, q_g, q_g_unit, q_w, q_w_unit, time_transients, is_producer = _read_rate_pocket(well, entities) 209 210 time_transients = list(time_transients) 211 data_transient = {} 212 for idx in range(len(time_transients) - 1): 213 214 # Splitting data into transients. 215 if idx == 0: 216 mask_transients = np.logical_and(time['value'] >= time_transients[idx], 217 time['value'] <= time_transients[idx + 1]) 218 else: 219 mask_transients = np.logical_and(time['value'] > time_transients[idx], 220 time['value'] <= time_transients[idx + 1]) 221 222 data_transient[idx] = TransientData(pressure=pressure[mask_transients], 223 qo=q_o[idx], 224 qg=q_g[idx], 225 qw=q_w[idx], 226 pressure_unit=pressure_unit, 227 qo_unit=q_o_unit, 228 qg_unit=q_g_unit, 229 qw_unit=q_w_unit) 230 231 # Data for sending pressure to the pocket Calc 232 gauge_calc_pocket = {'gauge message': id_reference_p_gauge, 233 'graphic_times': time, 234 'calendar_time': calendar_time} 235 236 return data_transient, is_producer, gauge_calc_pocket 237 238 239def _read_pressure_pocket(well, entities) -> Tuple[dt.datetime, np.ndarray, str, 240 Dict[str, Union[str, np.ndarray]], str]: 241 """ 242 Pressure gauge data reading function. 243 244 Parameters 245 ---------- 246 well: WellMessage 247 Data of well. 248 entities: dict[str, object] 249 A dictionary that allows you to get an entity by Id. 250 251 Returns 252 ------- 253 calendar_time: dt.datetime 254 Gauge calendar time. 255 pressure: np.ndarray 256 Pressure points array. 257 pressure_unit: str 258 Pressure units. 259 time: dict[str, str | np.ndarray] 260 'unit': Time units. 261 'value': Time points array. 262 reference_pressure_gauge[0]: str 263 Id gauge of pressure. 264 """ 265 pressure_gauges_list = [entities[i] for i in well.GaugeIds if 266 entities[i].GaugeCategory == CategoryGaugeEnum.PRESSURE.get_int_value()] 267 reference_pressure_gauge = [i for i in pressure_gauges_list if i.ReferencePocketId != ''] 268 assert len(reference_pressure_gauge) == 1, get_message_incorrect_pocket_pressure(well.Name) 269 270 pocket_pressure_row_id = [entities[i] for i in reference_pressure_gauge[0].GaugePocketIds if 271 entities[i].PocketType == PocketTypeEnum.ORIGINAL.get_int_value()] 272 273 pocket_pressure_row = entities[pocket_pressure_row_id[0].GaugeGraphIds[0]] 274 275 calendar_time = datetime_from_message(pocket_pressure_row.CalendarTime) 276 277 pressure_data = pocket_pressure_row.Points 278 279 pressure_unit = pressure_data.ValueUnitCode 280 time_unit = pressure_data.XUnitCode 281 282 pressure = np.array(pressure_data.Values) 283 time_value = np.array(pressure_data.X) 284 assert len(pressure) > 0, get_message_incorrect_pocket_pressure_value(well.Name) 285 286 time = {'unit': time_unit, 287 'value': time_value} 288 289 return calendar_time, pressure, pressure_unit, time, reference_pressure_gauge[0] 290 291 292def _read_rate_pocket(well, entities) -> (np.ndarray, str, np.ndarray, str, np.ndarray, str, List[float], bool): 293 """ 294 Rate gauge data reading function. 295 296 Parameters 297 ---------- 298 well: WellMessage 299 Data of well. 300 entities: dict[str, object] 301 A dictionary that allows you to get an entity by Id. 302 Returns 303 ------- 304 q_o: np.ndarray 305 Array of oil flow rates. 306 q_o_unit: str 307 Units of oil flow rates. 308 q_g: np.ndarray 309 Array of gas flow rates. 310 q_g_unit: str 311 Units of gas flow rates. 312 q_w: np.ndarray 313 Array of water flow rates. 314 q_w_unit: str 315 Units of water flow rates. 316 time_transients: list 317 Array of fluid flow change points. 318 is_producer: bool 319 Flag of producer wells (True - producer wells; False - injection wells). 320 """ 321 rate_gauges_list = [entities[i] for i in well.RateIds] 322 323 reference_rate_gauge = [i for i in rate_gauges_list if i.ReferencePocketId != ''] 324 assert len(reference_rate_gauge) == 1, get_message_incorrect_pocket_rate(well.Name) 325 326 pocket_rate_row_id = [entities[i] for i in reference_rate_gauge[0].RatePocketIds if 327 entities[i].PocketType == PocketTypeEnum.ORIGINAL.get_int_value()] 328 329 pocket_rate_row = entities[pocket_rate_row_id[0].TransientsId] 330 331 q_o_unit = pocket_rate_row.QoUnitCode 332 q_w_unit = pocket_rate_row.QwUnitCode 333 q_g_unit = pocket_rate_row.QgUnitCode 334 335 transients = [i for i in pocket_rate_row.Points] 336 assert len(transients) > 0, get_message_incorrect_pocket_rate_value(well.Name) 337 338 time_transients = [i.Time for i in transients] 339 time_transients.append(time_transients[-1] + pocket_rate_row.LastDuration) 340 341 q_o = np.array([i.Qo.value for i in transients]) 342 q_w = np.array([i.Qw.value for i in transients]) 343 q_g = np.array([i.Qg.value for i in transients]) 344 345 check_producer = np.all([q_g >= 0, q_o >= 0, q_w >= 0]) 346 check_injector = np.all([q_g <= 0, q_o <= 0, q_w <= 0]) 347 assert np.any([check_producer, check_injector]), get_message_incorrect_well_type() 348 349 if check_producer: 350 is_producer = True 351 else: 352 is_producer = False 353 354 return q_o, q_o_unit, q_g, q_g_unit, q_w, q_w_unit, time_transients, is_producer 355 356 357def _convert_to_unit_si(data: Dict[int, TransientData]) -> Dict[int, TransientData]: 358 """ 359 Function to bring units of measurement to the system SI 360 361 Parameters 362 ---------- 363 data : dict 364 keys: number of transient. 365 values: dataclass with pressure and flow rates. 366 Returns 367 ------- 368 dict 369 keys: number of transient. 370 values: dataclass with array of pressure and flow rates. 371 """ 372 373 unisum_convert = UnisumClient() 374 375 transients_data_units_si = {} 376 # TODO переписать словарь на ndarray 377 for idx, value in data.items(): 378 # Convertation pressure 379 pressure_unit_si = unisum_convert.convert_many_to_numpy_array(value.pressure_unit, 'Pa', value.pressure) 380 381 # Divide by DAYS_TO_SECONDS in order to convert days into seconds. 382 # Convertation oil flow rate 383 qo_unit_si = unisum_convert.convert_one(value.qo_unit, 'm^3/day', value.qo) / DAYS_TO_SECONDS 384 385 # Convertation gas flow rate 386 qg_unit_si = unisum_convert.convert_one(value.qg_unit, 'm^3/day', value.qg) / DAYS_TO_SECONDS 387 388 # Convertation water flow rate 389 qw_unit_si = unisum_convert.convert_one(value.qw_unit, 'm^3/day', value.qw) / DAYS_TO_SECONDS 390 391 transients_data_units_si[idx] = TransientData(pressure=pressure_unit_si, 392 qo=qo_unit_si, 393 qg=qg_unit_si, 394 qw=qw_unit_si, 395 pressure_unit=value.pressure_unit, 396 qo_unit=value.qo_unit, 397 qg_unit=value.qg_unit, 398 qw_unit=value.qw_unit) 399 return transients_data_units_si 400 401 402def read_project_info(request) -> (LangEnum, str, ProjectInfo): 403 """ 404 Reading project information 405 406 Parameters 407 ---------- 408 request: SolveRequest 409 Input data 410 411 Returns 412 ------- 413 language: LangEnum 414 Language selected in the interface. 415 unit_system: str 416 Units selected in the interface. 417 project_info: ProjectInfo 418 Project information. 419 """ 420 entities = get_entities(request) 421 test = entities[request.test_id] 422 423 project_client = ProjectClient() 424 project_reply = project_client.get_project() 425 426 language = LangEnum(project_reply.Language) 427 unit_system = project_reply.UnitSystem 428 project = project_reply.Project 429 wells_names = [entities[i].Name for i in test.WellIds] 430 431 project_info = ProjectInfo( 432 analyst=project.Analyst, 433 analyst_email=project.AnalystEmail, 434 company=project_reply.Company, 435 contractor=project.Contractor, 436 country=project_reply.Country, 437 curator=project.Curator, 438 curator_email=project.Curator, 439 field=project_reply.Field, 440 id=project.InfoId, 441 info_date=project.InfoDate, 442 project_name=project.Name, 443 well_name=', '.join(wells_names), 444 test_name=test.Name 445 ) 446 return language, unit_system, project_info
def
read_test_data( request: custom_plugin_base.server.calculation.SolveRequest) -> (<class 'numpy.ndarray'>, <class 'numpy.ndarray'>, <class 'numpy.ndarray'>, <class 'bytes'>):
22def read_test_data(request: SolveRequest) -> (np.ndarray, np.ndarray, np.ndarray, bytes): 23 """ 24 Reading data from well gauges and converting to system SI 25 26 Parameters 27 ---------- 28 request: SolveRequest 29 Input data 30 31 Returns 32 ------- 33 data_transient : np.ndarray(dict) 34 keys : Index flow rate. 35 value : Dataclass with phase flow rate and pressure point values. 36 is_producer : np.ndarray(bool) 37 Flag of producer wells (True - producer wells; False - injection wells). 38 gauge_calc_pocket : np.ndarray(dict) 39 Data for sending pressure to the pocket Calc 40 pvt: bytes 41 Bytes of json file with PVT table data 42 """ 43 44 entities = get_entities(request) 45 wells = entities[request.test_id].WellIds 46 pvt = _read_pvt(entities, entities[request.test_id]) 47 transients_data = np.empty(len(wells), object) 48 is_producer = np.empty(len(wells), bool) 49 gauge_calc_pocket = np.empty(len(wells), object) 50 for idx, well_id in enumerate(wells): 51 data_transient, is_producer[idx], gauge_calc_pocket[idx] = _read_test_data(entities, well_id) 52 transients_data[idx] = _convert_to_unit_si(data_transient) 53 54 return transients_data, is_producer, gauge_calc_pocket, pvt
Reading data from well gauges and converting to system SI
Parameters
request: SolveRequest Input data
Returns
data_transient : np.ndarray(dict) keys : Index flow rate. value : Dataclass with phase flow rate and pressure point values. is_producer : np.ndarray(bool) Flag of producer wells (True - producer wells; False - injection wells). gauge_calc_pocket : np.ndarray(dict) Data for sending pressure to the pocket Calc pvt: bytes Bytes of json file with PVT table data
def
read_project_info( request) -> (<enum 'LangEnum'>, <class 'str'>, <class 'friction_loss.utils.plugin_dataclasses.ProjectInfo'>):
403def read_project_info(request) -> (LangEnum, str, ProjectInfo): 404 """ 405 Reading project information 406 407 Parameters 408 ---------- 409 request: SolveRequest 410 Input data 411 412 Returns 413 ------- 414 language: LangEnum 415 Language selected in the interface. 416 unit_system: str 417 Units selected in the interface. 418 project_info: ProjectInfo 419 Project information. 420 """ 421 entities = get_entities(request) 422 test = entities[request.test_id] 423 424 project_client = ProjectClient() 425 project_reply = project_client.get_project() 426 427 language = LangEnum(project_reply.Language) 428 unit_system = project_reply.UnitSystem 429 project = project_reply.Project 430 wells_names = [entities[i].Name for i in test.WellIds] 431 432 project_info = ProjectInfo( 433 analyst=project.Analyst, 434 analyst_email=project.AnalystEmail, 435 company=project_reply.Company, 436 contractor=project.Contractor, 437 country=project_reply.Country, 438 curator=project.Curator, 439 curator_email=project.Curator, 440 field=project_reply.Field, 441 id=project.InfoId, 442 info_date=project.InfoDate, 443 project_name=project.Name, 444 well_name=', '.join(wells_names), 445 test_name=test.Name 446 ) 447 return language, unit_system, project_info
Reading project information
Parameters
request: SolveRequest Input data
Returns
language: LangEnum Language selected in the interface. unit_system: str Units selected in the interface. project_info: ProjectInfo Project information.