Skip to content

API Reference

calculate_doublet_performance(reservoir_properties, utc_properties=None, rng_seed=None, chunk_size=None, print_execution_duration=False, mask_value=np.nan)

Perform a deterministic Doublet performance simulation.

This function computes doublet performance metrics across all dimensions of the input dataset. The input can be scalar, 1D, or 2D gridded data.

If no temperature values are provided, they are estimated from a gradient defined in utc_properties. If a mask is provided, any non-NaN values in the mask datarray will result in zeroing the output values at those locations.

Parameters:

Name Type Description Default
reservoir_properties Dataset

An xarray Dataset containing the required input variables: - thickness, units: m - porosity, units: [0-1] - ntg, units [0-1] - depth, units: m +ive downwards - permeability, units: mD

Optional variables: - transmissivity, units: Dm, if provided then permeability is ignored. - temperature : If not provided, temperature is estimated using the depth and a temperature gradient from utc_properties. - mask : If provided, all non-NaN values will result in setting corresponding output values to mask_value.

required
utc_properties JClass

A Java class specifying the properties of the doublet being simulated

None
rng_seed int

Random seed used for stochastic components of the simulation.

None
chunk_size int

None by default, if set to an integer then chunking of the reservoir properties occurs. The chunk size is used to split up the number of simulations into "chunks" which can be processed in parallel using the dask framework. Chunk size involves trade-offs: smaller chunks = more parallelism, but more overhead, while larger chunks = less overhead, but can lead to memory pressure. The optimal chunk size is dependent on the hardware being used to run the simulation. The user should test to find the optimal chunk size.

None
print_execution_duration bool

False by default, If set to True print the time in seconds it took to simulate across all reservoir properties

False
mask_value float

0.0 by default, Any cell that results in a failed simulation or corresponds to a non-nan value in the mask parameter will be assigned the mask value

nan

Returns:

Name Type Description
output_data Dataset

An xarray Dataset with the same spatial dimensions as reservoir_properties Contains the following output variables: - "power" - "heat_pump_power" - "capex" - "opex" - "utc" - "npv" - "hprod" - "cop" - "cophp" - "pres" - "flow_rate" - "welld" - "inj_temp" - "prd_temp" - "temperature" - "thickness" - "depth" - "permeability" - "transmissivity" - "transmissivity_with_ntg"

Source code in src/pythermogis/doublet_simulation/deterministic_doublet.py
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
def calculate_doublet_performance(reservoir_properties: xr.Dataset, utc_properties = None, rng_seed: int = None, chunk_size: int = None, print_execution_duration: bool = False, mask_value: float = np.nan) -> xr.Dataset:
    """
    Perform a deterministic Doublet performance simulation.

    This function computes doublet performance metrics across all dimensions of the input dataset.
    The input can be scalar, 1D, or 2D gridded data.

    If no temperature values are provided, they are estimated from a gradient defined in `utc_properties`.
    If a mask is provided, any non-NaN values in the mask datarray will result in zeroing the output values at those locations.

    Parameters
    ----------
    reservoir_properties : xr.Dataset
        An xarray Dataset containing the required input variables:
        - thickness, units: m
        - porosity, units: [0-1]
        - ntg, units [0-1]
        - depth, units: m +ive downwards
        - permeability, units: mD

        Optional variables:
        - transmissivity, units: Dm, if provided then permeability is ignored.
        - temperature : If not provided, temperature is estimated using the depth and a temperature gradient from `utc_properties`.
        - mask : If provided, all non-NaN values will result in setting corresponding output values to mask_value.

    utc_properties : JClass
        A Java class specifying the properties of the doublet being simulated

    rng_seed : int
        Random seed used for stochastic components of the simulation.

    chunk_size : int
        None by default, if set to an integer then chunking of the reservoir properties occurs.
        The chunk size is used to split up the number of simulations into "chunks" which can be processed in parallel using the dask framework.
        Chunk size involves trade-offs: smaller chunks = more parallelism, but more overhead, while larger chunks = less overhead, but can lead to memory pressure.
        The optimal chunk size is dependent on the hardware being used to run the simulation. The user should test to find the optimal chunk size.

    print_execution_duration : bool
        False by default, If set to True print the time in seconds it took to simulate across all reservoir properties

    mask_value : float
        0.0 by default, Any cell that results in a failed simulation or corresponds to a
        non-nan value in the mask parameter will be assigned the mask value

    Returns
    -------
    output_data : xr.Dataset
        An xarray Dataset with the same spatial dimensions as `reservoir_properties`
        Contains the following output variables:
        - "power"
        - "heat_pump_power"
        - "capex"
        - "opex"
        - "utc"
        - "npv"
        - "hprod"
        - "cop"
        - "cophp"
        - "pres"
        - "flow_rate"
        - "welld"
        - "inj_temp"
        - "prd_temp"
        - "temperature"
        - "thickness"
        - "depth"
        - "permeability"
        - "transmissivity"
        - "transmissivity_with_ntg"
    """
    if print_execution_duration:
        start = timeit.default_timer()

    validate_input(reservoir_properties)

    if utc_properties is None:
        utc_properties = instantiate_utc_properties_builder().build()

    # If no mask grid is provided, then provide a dummy mask grid with only nan
    if "mask" not in reservoir_properties:
        reservoir_properties["mask"] = np.nan

    # Generate temperature values from gradient if no temperature provided
    if "temperature" not in reservoir_properties:
        reservoir_properties["temperature"] = calculate_temperature_from_gradient(reservoir_properties.depth, reservoir_properties.thickness, utc_properties.tempGradient(), utc_properties.surfaceTemperature())

    # calculate transmissivity from thickness and permeability if no transmissivity provided
    if "transmissivity" not in reservoir_properties:
        reservoir_properties["transmissivity"] = reservoir_properties["permeability"] * reservoir_properties["thickness"]

    # Setup output_data dataset as a copy of reservoir properties
    if chunk_size is not None:
        reservoir_properties = auto_chunk_dataset(reservoir_properties, chunk_size)

    output_data = reservoir_properties.copy()
    output_data = simulate_doublet(output_data, reservoir_properties, rng_seed, utc_properties, mask_value=mask_value)

    if chunk_size is not None:
        output_data.load() # If chunking has occurred then the data must be de-chunked

    if print_execution_duration:
        print(f"Doublet simulation took {timeit.default_timer() - start:.1f} seconds")

    return output_data

validate_input(reservoir_properties)

Ensure that the reservoir_properties Dataset contains the minimum required variables, and that these variables meet certain conditions. Other-wise raise an informative error.

Parameters:

Name Type Description Default
reservoir_properties Dataset

Input reservoir_properties that must contain the required ThermoGIS variables.

required

Returns:

Type Description
None
Source code in src/pythermogis/doublet_simulation/deterministic_doublet.py
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
def validate_input(reservoir_properties: xr.Dataset):
    """
    Ensure that the reservoir_properties Dataset contains the minimum required variables, and that these variables meet certain conditions.
    Other-wise raise an informative error.

    Parameters
    ----------
    reservoir_properties : xr.Dataset
        Input reservoir_properties that must contain the required ThermoGIS variables.

    Returns
    -------
    None
    """
    # check all necessary variables are present
    missing_variables = []
    for variable in ["thickness", "porosity", "ntg", "depth"]:
        if variable not in reservoir_properties:
            missing_variables.append(variable)

    if len(missing_variables) > 0:
        raise ValueError(f"provided reservoir properties Dataset does not contain the following required variables: {missing_variables}")

    if "permeability" not in reservoir_properties and "transmissivity" not in reservoir_properties:
        raise ValueError(f"provided reservoir properties Dataset must provide either permeability or transmissivity variables, currently neither are provided")

    if "permeability" in reservoir_properties and "transmissivity" in reservoir_properties:
        warnings.warn("Both reservoir permeability and transmissivity provided; however the doublet simulation will use only the provided transmissivity and ignore permeability (transmissivity = permeability * thickness)")

    # check that certain variables are always >0
    always_positive = ["thickness", "permeability", "transmissivity"]
    for param in always_positive:
        if param not in reservoir_properties:
            continue
        if (reservoir_properties[param] < 0.0).any():
            raise ValueError(f"provided reservoir properties Dataset contains a negative {param} value. {param} must always be >= 0.0 in [m]")

    # check that certain variables are always >0 and <1
    always_between_0_and_1 = ["porosity", "ntg"]
    for param in always_between_0_and_1:
        if (reservoir_properties[param] > 1.0).any() or (reservoir_properties[param] < 0.0).any():
            raise ValueError(f"provided reservoir properties Dataset contains a {param} value > 1.0. {param} must always be between 0.0 and 1.0 (100% {param} = 1.0)")