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.