# Binder API

The **Binder API** enables you to assign a variable produced by one AWI method (referred to as the **producer**) as an input parameter for another AWI method (referred to as the **consumer**).
Specifically, the consumer AWI method utilizes the assigned variable depth by depth during computation, replacing the need for a constant parameter.
This connection between the producer’s variable and the consumer’s parameter is established through the **mapping** configuration.
With this API, you can define and manage all three components: producer, consumer, and mapping configurations.

![image](/assets/binder.3657efb525f344f0e75f6fd4a6597107cecbf629cad2536ca3c56d9de8aed989.3180d2f6.png)

The configuration files can be deployed at User and Company levels under `ApplicationWorkflowInterface` folder.
Producer, Consumer and Mapping configuration files could reside at different levels. ​

Mapping configuration file should have the same name as consumer file.​

Example of a default configuration files are deployed at Techlog level under `ApplicationWorkflowInterface` folder:

- `QuantiElan.consumer`
- `KerogenProperties.producer`
- `PreSaltTemp.producer`
- `QuantiElan.mapping​`


## Getting started with AWI files handling

This documentation page explains how to read and modify AWI files with Python.

It introduces the following classes:

1. [AWI consumer](#awi-consumer),
2. [AWI producer](#awi-producer),
3. [AWI mapping](#awi-mapping),
4. [Version](#version).


### AWI consumer

An **AWI consumer** defines a list of **parameters** that can be used as input to an AWI method.
These parameters are grouped together in tables.

For more details see [AWI consumer class](#awi-consumer-class).

```python Load an AWI consumer from a file
import Binder

# Define a function that will print the content of an AWI consumer.
def printConsumer(consumer:Binder.AwiConsumer):
  tables = consumer.get_parameter_tables()
  print(f"The AWI consumer \"{consumer}\" has {len(tables)} tables:")
  for table in tables:
    parameters = table.get_parameters()
    print(f"\t- Table \"{table}\" has {len(parameters)} parameters:")
    for parameter in parameters:
      print(f"\t\t- Parameter \"{parameter}\"")

# Load the AWI consumer and display all of the parameters.
quantiElan = Binder.AwiConsumer.load(r"..\ApplicationWorkflowInterface\QuantiElan.consumer")
printConsumer(quantiElan)

# Get a specific parameter table.
deepResistivityTable = quantiElan.get_parameter_table_from_name("DeepResistivity")
print(f"\nThe table \"{deepResistivityTable}\" contains {len(deepResistivityTable.get_parameters())} parameters.")
# Get a specific parameter.
shallowFormationParameter = quantiElan.get_parameter_table_from_name("ShallowResistivity").get_parameter_from_name("Shallow_Formation_Water_Resistivity")
print(f"\nThe parameter \"{shallowFormationParameter}\" is in the table \"{shallowFormationParameter.get_parent()}\".")
```

### AWI producer

An **AWI producer** defines a list of **outputs** that represent the results of computations from an external method.

For more details see [AWI producer class](#awi-producer-class).

```python Load an AWI producer from a file
import Binder

# Define a function that will print the content of an AWI producer.
def printProducer(producer:Binder.AwiProducer):
    outputs = producer.get_outputs()
    print(f"The AWI producer \"{producer}\" has {len(outputs)} outputs:")
    for output in outputs:
        print(f"\t- Output \"{output.get_default_variable_name()}\" @ {output.get_index()}")

# Load the AWI producers and display all of the outputs.
kerogenProperties = Binder.AwiProducer.load(r"..\ApplicationWorkflowInterface\KerogenProperties.producer")
printProducer(kerogenProperties)
preSaltTemp = Binder.AwiProducer.load(r"..\ApplicationWorkflowInterface\PreSaltTemp.producer")
printProducer(preSaltTemp)

# Get a specific output.
rhok = kerogenProperties.get_output_from_index(1)
print(f"\nThe output \"{rhok}\" belongs to the AWI producer \"{rhok.get_parent()}\".")
```

```python Create an AWI producer from scratch
import Binder

# Create an AWI producer with 3 outputs.
myProducer = Binder.AwiProducer("MyProducerId", "My beautiful producer")
myProducer.add_output(0, "GR")
myProducer.add_output(1, "POTA")
myProducer.add_output(2, "TH")
myProducer.save_as(r"D:\My User Folder\My AWI Files\MyBeautifulProducer.producer", Binder.Version(1))

# Remove the 2nd output and save.
myProducer.remove_output_from_index(1)
myProducer.save(Binder.Version(2))
```

### AWI mapping

An **AWI mapping** binds some outputs, defined by one or several [AWI producer](#awi-producer) (s), to the parameters defined by an [AWI consumer](#awi-consumer).
It allows to specify, for each input parameter of an AWI method, the possible source of the values.

For more details see [AWI mapping class](#awi-mapping-class).

Note
As an **AWI mapping** is tightly coupled with an **AWI consumer**, the related files are stored in the same folder, sharing the same filename but having a different file extension ( *“.consumer”* and  *“.mapping”*).
For this reason, the path is not needed when **saving** the mapping file.

```python Load an AWI mapping from a file
import Binder

# Define a function that will print the content of an AWI producer.
def printMapping(mapping:Binder.AwiMapping):
  bindings = mapping.get_bindings()
  print(f"The AWI mapping \"{mapping}\" has {len(bindings)} bindings:")
  for binding in bindings:
    output_full_name = f"{binding.get_output().get_parent()}.{binding.get_output()}"
    parameter = binding.get_parameter()
    parameter_full_name = f"{parameter.get_parent().get_parent()}.{parameter.get_parent()}.{parameter}"
    print(f"\t- {output_full_name} --> {parameter_full_name}")

# Load the AWI consumer and the AWI producers used for the mapping.
quantiElan = Binder.AwiConsumer.load(r"..\ApplicationWorkflowInterface\QuantiElan.consumer")
kerogenProperties = Binder.AwiProducer.load(r"..\ApplicationWorkflowInterface\KerogenProperties.producer")
preSaltTemp = Binder.AwiProducer.load(r"..\ApplicationWorkflowInterface\PreSaltTemp.producer")
quantiElanMapping = Binder.AwiMapping.load(quantiElan, { kerogenProperties, preSaltTemp })
printMapping(quantiElanMapping)
```

```python Create an AWI mapping from scratch
import Binder

# Get the consumer and the producers that will be bound together.
quantiElan = Binder.AwiConsumer.load(r"..\ApplicationWorkflowInterface\QuantiElan.consumer")
kerogenProperties = Binder.AwiProducer.load(r"..\ApplicationWorkflowInterface\KerogenProperties.producer")
myProducer = Binder.AwiProducer.load(r"D:\My User Folder\My AWI Files\MyBeautifulProducer.producer")

# Create the mapping.
myMapping = Binder.AwiMapping()

# Bind 3 outputs to 2 parameters.
xiWaterSalinity = quantiElan.get_parameter_table_from_name("Minerals").get_parameter_from_name("XIWater_Salinity")
myMapping.bind(myProducer.get_output_from_index(0), xiWaterSalinity)
myMapping.bind(kerogenProperties.get_output_from_index(2), xiWaterSalinity)
uiWaterSalinity = quantiElan.get_parameter_table_from_name("Minerals").get_parameter_from_name("UIWater_Salinity")
myMapping.bind(kerogenProperties.get_output_from_index(1), uiWaterSalinity)

# Save the mapping, version 1.2.3.
myMapping.save_as(r"D:\My User Folder\My AWI Files", Binder.Version(1, 2, 3))
```

### Version

A simple class that handles a **version** number.

```python Versions handling
from Binder import Version, VersionPart

# Fixed versions.
print(f"version 1: {Version(1)}")
print(f"version 1.2: {Version(1, 2)}")
print(f"version 1.2.3: {Version(1, 2, 3)}")
v234 = Version.parse("2.3.4")
print(f"Parsed version \"2.3.4\": {v234}")

# Increase version numbers.
print(f"Next major version after 1.2.3: {Version(1, 2, 3).increase(VersionPart.Major)}")
print(f"Next minor version after 1.2.3: {Version(1, 2, 3).increase(VersionPart.Minor)}")
print(f"Next patch version after 1.2.3: {Version(1, 2, 3).increase(VersionPart.Patch)}")
```

For more details see [Version class](#version-class) and [VersionPart enumeration](#versionpart-enumeration).

## Using a Python AWI as an AWI producer

The **Binder API** can be used, for instance, to bind the outputs of a **Python AWI** with the parameters of an AWI method.
Note: Currently, the only AWI method supporting the Binder API is **Quanti.Elan**.

1. Create a **Python AWI** by clicking on the command “**Python editor for AWI**”, in the menu “**Utility**”.
2. Define your **output** variables.
3. Set up the function which will perform the desired **computation**.


#### HINT

Here is an example of a Python AWI defining one output variable *PI* and setting its content to the constant value *3.14159*:

![image](/assets/binder_pythonawiexample.1fc2af859033e853e02465865dd0ddc2e85b0d893b23f9bbda15dc72ca78c381.3180d2f6.png)

1. **Save** the Python AWI at the desired level (*Project*, *Company* or *User*)


#### HINT

The previous Python AWI is saved as *PiComputation.py* at *Project* level:

![image](/assets/binder_pythonawisave.b0b4bc8836df724a56361eb99d4dfff1fd3de2118b9cd7deb9a50a5e1b30c61e.3180d2f6.png)

1. Using the **Binder API**, create a new **AWI producer**.
Define the outputs.
Use the function *set_python_script(…)* to define the file name of the associated Python script.


```python Create an AWI producer associated to a Python script.
from Binder import *

USER_FOLDER = "C:\\Users\\MyUserName\\AppData\\Roaming\\Schlumberger\\Techlog\\"
TARGET_FOLDER = USER_FOLDER + "ApplicationWorkflowInterface\\"

# Create a new Awi Producer associated to the Python script 'PiComputation.py'.
piConstantProducer = AwiProducer("PiConstant", "Pi constant value")
piConstantProducer.set_python_script("PiComputation.py")
piConstantProducer.add_output(0, "PI")

# Save it into the subdirectory `ApplicationWorkflowInterface` of the User folder.
piConstantProducer.save_as(TARGET_FOLDER + "PiConstant.producer", Version(1))
```

1. Load the current **AWI mapping**.
Bind the new outputs to the desired parameters.
Save the AWI mapping.


```python Bind the output “PI” to the parameter “Kerogen / Bulk Density” of Quanti.Elan.
# Load the AWI consumer, the AWI producers and the AWI mapping.
quantiElan = AwiConsumer.load(r"..\ApplicationWorkflowInterface\QuantiElan.consumer")
kerogenProperties = AwiProducer.load(r"..\ApplicationWorkflowInterface\KerogenProperties.producer")
preSaltTemp = AwiProducer.load(r"..\ApplicationWorkflowInterface\PreSaltTemp.producer")
quantiElanMapping = AwiMapping.load(quantiElan, { kerogenProperties, preSaltTemp })

# Replace the outputs bound with the parameter "Kerogen / Bulk Density", and bind this parameter to the output "PI".
kerogenBulkDensity = quantiElan.get_parameter_table_from_name("Minerals").get_parameter_from_name("Kerogen_Bulk_Density")
quantiElanMapping.unbind(None, kerogenBulkDensity)    # Remove any output bound to the parameter
piOutput = piConstantProducer.get_output_from_index(0)
quantiElanMapping.bind(piOutput, kerogenBulkDensity)

# Save the modified AWI mapping in the user folder.
quantiElanMapping.save_as(TARGET_FOLDER, quantiElanMapping.get_version().increase(None))
```

1. Start **Techlog**.
2. From the *Project Browser*, in the *Python scripts* folder, right click on your Python AWI and select **Open with workflow**.
⟹ A new workflow should be created with a method “*Python: …*”.
3. Set the **Apply Mode** either to *save* or *save and display*.
4. Run the method.
5. Open the **Quanti.Elan** method, located in the submenu “*Quanti.Elan*” of the menu “**Petrophysics**”.
⟹ The method “*Quanti.Elan*” should be added to your workflow.
6. Open the parameters tab and check if the outputs of your Python AWI have been bound properly.


#### HINT

Example of an AWI workflow with the Python AWI “*PiComputation*” defined above, and a *Quanti.Elan* method:

![image](/assets/binder_quantielanworkflow.fa01c7abaad87ead71f23ed58585e4551c24fe2286587c5ae12d97edb63ab8e8.3180d2f6.png)

## Class details

### API

### AWI consumer class

### AWI producer class

### AWI mapping class

### Version class

### VersionPart enumeration