**SimpleOpenServer** (or *SOS* for short) is a group of Class Methods and Modules implemented in VBA, intended for the use of Petroleum Engineers that want to automatize tasks involving models.

Using **SimpleOpenServer** is easy, even for the users who did not have contact with OpenServer before. A basic understanding of Excel Macros is needed, in order to understand the inputs and outputs of the methods.

This documentation is divided into sections corresponding to each of the modules of the original SimpleOpenServer package:

**ProsperCalculation**, which is the object used to interact with PROSPER and perform calculations with Well models.**Vector Object**, which is the object that encapsulates methods relative to curves and vectors.

The **ProsperCalculation** object is used to make calculations in PROSPER Well models. The **ProsperCalculation** object supports the following calculations:

- System calculations
- Gradient calculations
- VLP calculations
- IPR calculations

Please see the following sections regarding the Prosper Calculation Object:

In order to make calculations with this object, the user needs to do four things:

- Initialize the Object
- Set the value of any variables for the calculation
- Make the calculation
- Obtain the results in form of values, using
**solution()**; or curves, using**vector()**

In this example we try to find the **System** solution for a **Skin factor** of 2.5, and obtain the **Liquid Rate**.

```
Dim myCalc as New ProsperCalculation
myCalc.calculationType = System
myCalc.setVar Skin, 2.5
myCalc.calculate
Range("B2") = myCalc.solution([Q Liquid])
```

Before making the calcualtions, the `myCalc`

object is created, as a new **ProsperCalculation**. This object will hold the *properties*, *methods* and *functions* that will be used.

To start, we set the `calculationType`

*property* to **System**, and set the **Skin** factor to the indicated value using the `setVar`

*method*. To make the calculation, we run the `calculate`

*method*. Finally, obtain the solution value using the `solution`

*function*, and store the value in cell B2.

The basic syntax is very similar to common OpenServer syntax, replacing the `DoGet`

, `DoCmd`

and `DoSet`

functions for other more representative of what is being done.

This is the OpenServer syntax for the above task:

```
DoSet "PROSPER.ANL.SYS.SENS.SENSDB.CLEAR", 1
DoSet "PROSPER.ANL.SYS.SENS.SENSDB.VARS[0]", 2
DoSet "PROSPER.ANL.SYS.SENS.SENSDB.SENS[27].VALS[0]", 2.5
DoCmd "PROSPER.ANL.SYS.CALC"
Range("B2") = DoGet("PROSPER.OUT.SYS.RESULTS[0].SOL.LIQRATE")
```

But, as you will notice, the OpenServer code is not as literal, easy to read, easy to understand or easy to debug as the **SimpleOpenServer** one.

What does the `2` stand for? And the `27`? It is impossible to know beforehand that those values represent Skin factor.

The **ProsperCalculation** object has the following properties:

`calculationType`

, to set which calculation will be done`regressionStatus`

, to read the status of the regression after doing one`rememberSetVariables`

, to choose whether to remember previous variable settings`resetSensitivities`

, to choose whether to reset the sensDB`useRawValues`

, to choose whether to use RawValues when interacting with PROSPER

List Variable. Defaults to System.

You can set or get the calculation type for a **ProsperCalculation** object using this property. The possible values, and the respective equivalent number are:

- Inflow (0)
- System (1)
- VLP (2)
- Gradient (3)

To **Set** this property, just assign its value. VBA will show the list of options.

`myCalc.calculationType = System`

You can also use the equal sign to **Get** the value of this property, but bear in mind that the output will be an `integer`

.

```
myCalc.calculationType = Gradient
MsgBox myCalc.calculationType ' Will output 3
```

If you are testing for the value of this property, and do not want to use the indexes, you can use the `osCalculationType`

enumerable, which will list all the possible calculation types:

```
myCalc.calculationType = Gradient
If myCalc.calculationType = osCalculationType.Gradient Then
MsgBox "This calculation is a gradient calculation!"
Else
MsgBox "This calculation is NOT a gradient calculation!"
End If
```

List Variable. Initially, its value is No Regression Done.

This property is read-only. Used to know the status of convergence of a regression calculation, after performing a regression (such as `find`

, `maximize`

, etc...). The possible values are:

- No Regression Done (0)
- Converged (1)
- Solution Not Found In Range (2)
- Loops Exceeded (2)
- No Solution Found (3)

To get the value, just use it as any other property of the object:

```
' Perform any regression here with the ProsperCalculation Object
If myCalc.regressionStatus = 1 Then
MsgBox "Converged!"
Else
MsgBox "Did not Converge"
End If
```

Instead of placing the number 1 in the conditional, you can use the auxiliary Enumerable list called `osRegressionStatus`

:

```
If myCalc.regressionStatus = osRegressionStatus.Converged Then
' continue code ...
```

This property is useful to know what happened to the convergence algorithm. See each of the regression algorithms to learn more about what the convergence flags are in each case.

Boolean. Defaults to `True`

.

As the **ProsperCalculation** object can accept multiple calculations, it is possible that the user wants to set a property (e.g. Gas-to-oil ratio), and consistently use it along all calculations.

An example would be performing a System calculation first, to find the liquid rate, and then use the liquid rate to perform a Gradient calculation. In this case, the logical thing to do would be to maintain the same conditions for both calculations:

```
With myCalc
.setVar GOR, 675 ' GOR set to 675
.calculationType = System
.calculate ' First calculation.
[B5] = .solution([Q Liquid])
.setVar [Flow Rate (Gradient)], [B5]
.calculationType = Gradient
.calculate ' Second calculation. (gradient calculation)
End With
```

If the `rememberSetVariable`

flag is set to `True`

, then the Gradient calculation will also be calculated with the GOR value of 675. There is no need to repeat the `.setVar`

method.

This property can be **Set** by just changing its value:

`myCalc.rememberSetVariables = False`

To **Get** the value of this property, just use it as a normal property:

`Range("B5") = myCalc.rememberSetVariables`

Boolean. Defaults to `True`

.

Before running any calculation in PROSPER, it is usually desirable to reset all the sensitivities. If this property is `True`

(the default), then any sensitivities present will be reset, and only the variables set using `setVar`

will be considered.

For now, this setting has not much impact as currently the **ProsperCalculation** object only deals with single solutions. But it will come in useful if the user wants to keep the sensitivities stored in each calculation.

This property can be **Set** by just changing its value:

`myCalc.resetSensitivities = False`

Boolean. Defaults to `False`

.

If set to `True`

, all values that are input to PROSPER will be considered as being in Oilfield units. Also, all values obtained from PROSPER will be always in Oilfield units.

If set to `False`

, all values that are input to PROSPER will be considered to be in the same units than the variables in the current PROSPER model, and all the values obtained from PROSPER will be in the same unit system as the PROSPER model.

This setting is used when the units of the PROSPER models might not be always the same.

Setting the Reservoir Pressure of a PROSPER model that has BARg as the pressure unit will have a different result whether Raw values are used or not:

- If Raw Values are used, and the value "230" is input, then the reservoir pressure will be changed to 230 psig.
- If Raw Values are not used, and the value "230" is input, then the new Reservoir pressure will be "230 BARg"

The same happens if we use Raw values when getting the values out of PROSPER. Let's suppose that the Reservoir pressure in our model is 230BARg:

- If Raw Values are used, and we ask for the reservoir pressure, then the value returned will be "3336", which corresponds to 3336 psig = 230 BARg.
- If Raw Values are not used, then the value "230" will be returned.

The **ProsperCalculation** object has the following methods:

Parameters:

`variable`

: Input variable to be changed / set`value`

: Value to which it will be set

This method will set the chosen input variable to the value entered.

Currently, only scalar values are accepted so, to avoid errors, `Value`

should be an integer or decimal (double).

For simple variables such as [Top node Pressure] or [GOR], the syntax in OpenServer is simple if you know the AccessString:

```
' Setting Top Node Press. in OpenServer
doSet "PROSPER.ANL.SYS.PRES", 250
```

However, for other variables such as [Permeability], or [Skin Factor], or [ESP Frequency], the syntax is more complicated for OpenServer:

```
' Example setting ESP Frequency = 60Hz in OpenServer (IPM9)
doSet "PROSPER.ANL.SYS.Sens.SensDB.Var[0]", 35
doSet "PROSPER.ANL.SYS.Sens.Sens[148].Var[0]", 60
```

This code is difficult to understand, because the variable is hidden inside an index (35), and that index inside another (148)!. Also, when adding more variables the indexes “[0]" must change accordingly, which further complicates the situation.

Using **setVar**, you will always use the same syntax:

```
myCalc.setVar [Top Node Pressure], 250
myCalc.setVar [Pump Operating Frequency (ESP)], 60
```

Which is clearer, easier to read, debug, and understand.

If you are going to make multiple calculations which are related, such as a System calculation followed by a Gradient calculation, you should make sure that the `rememberSetVariables`

flag is set to true, to keep the values set using `setVar`

in all the calculations.

As setVar will sometimes modify the sensitivity section of the calculations, the `resetSensitivities`

flag is used to either reset the current sensitivities or to keep them when making the calculations.

This method performs the calculation set in `calculationType`

property of the **ProsperCalculation** object.

`myCalc.calculate`

Notice that the same method is called for any of the three different `calculationTypes`

in PROSPER.

The **ProsperCalculation** object has several functions.

Some are to get results of calculations:

`solution`

, to obtain any variable of the system calculation solution`vector`

, to obtain any curve resulting from a calculation (e.g. IPR Pressure, or Pressure Gradient vs Depth)

Other functions are used to make a regression using the PROSPER model, and obtaining the result of such regression:

`find`

, to find the input variable that matches a condition`maximize`

, to find the input variable that maximizes a certain output variable`minimize`

, to find the input variable that minimizes a certain output variable`match`

, to match a certain curve to a set of points by changing an input variable.`rateOfChange`

, to find the rate of change of an output variable when an input variable is modified

Parameters:

`variable`

: Output variable to be obtained

The Solution function will output any of the variables corresponding to the result of a System calculation in PROSPER.

```
Dim myCalc as New ProsperCalculation
myCalc.calculationType = System
myCalc.calculate
Range("F3") = myCalc.solution([Q Gas])
```

This example code makes the system calculation and outputs the gas rate found to the F3 cell.

Parameters:

`variable`

: Output variable to be obtained

This function returns a vector object. Using this command you can obtain any curve generated by PROSPER in a calculation. For example, you can obtain the IPR curve by using:

```
Dim myCalc as New ProsperCalculation, theVector as New Vector
myCalc.calculationType = IPR
myCalc.calculate
Set theVector = myCalc.vector([IPR Pressure])
' Now theVector holds the ipr curve
```

or, for a gradient calculation, you can get the pressure gradient in the well:

```
Dim myCalc as New ProsperCalculation, theVector as New Vector
myCalc.calculationType = IPR
myCalc.calculate
Set theVector = myCalc.vector(Pressure)
' Now theVector holds the pressure gradient vs depth
```

To show the whole list of options, use the prefix **rv** (for rate vectors) and **dv** for depth vectors, as in this example:

If you want to obtain the IPR curve, for eaxmple, you would request for a rate vector with the IPR pressures, as in the example above. Then, Simple OpenServer would obtain both the rates and the pressures for you to build the whole curve. You can see more about that in the Vector Object documentation.

Parameters:

`valueOf`

: The**Input Variable**will be modified until the conditions are met`thatMakes`

:**Target variable**that will be set equal to the value of`equalTo`

`equalTo`

: The**Target value**`betweenValue`

and`andValue`

:**Range of Search**, defined by minimum and maximum values for the Input variable.`precision`

(optional): The precision, in terms of the target value, to which to adjust the match.

Usage:

```
[F2] = myCalc.find(valueOf:=[Top Node Pressure], _
thatMakes:=[Liquid Rate], _
equalTo:=2500, _
betweenValue:=300, _
andValue:=600)
```

Which is equivalent to:

"We will look for the value of

`Top Node Pressure`

that will make the`Liquid Rate`

of the well equal to 2500[STB/d], searching between`Top Node Pressures`

of 300 and 600[psig]."

*Remember: The units are implied in the PROSPER model, or depending on the setting of* `useRawValues`

.

This regression algorithm will only find a solution if the seeked value of the **Input Variable** is between the range given (defined by the `betweenValue`

& `andValue`

parameters).

The returned value is the value of the **Input Variable** that fulfills the condition: Target Variable = Target Value.

If the seeked value is outside the range given, the `regressionStatus`

will be equal to [Solution Not Found in Range], and the value returned will be one of the two extremes of the **Range** provided, whichever is closest.

*This might not be true for non-monotonous curves, such as pump efficiency, so caution in the selection of the ranges is advised.*

If the seeked value is found, but after the iterations it did not converge to the required precision, then the `regressionStatus`

will be equal to [Loops Exceeded]. The value returned will be the best guess so far.

If the seeked value is found, and the required precision is met, then the found value is returned, and the `regressionStatus`

will be equal to [Converged].

Parameters:

`variable`

:**Target variable**that will try to be maximized`byChanging`

: The**Input Variable**will be modified until the maximum is found`betweenValue`

and`andValue`

:**Range of Search**, defined by minimum and maximum values for the Input variable.`precision`

(optional): The precision, in terms of the target value, that dictates when to stop the algorithm.

Usage:

```
[F2] = myCalc.maximize(variable:=[Q Oil], _
byChanging:=[Gaslift Casing Pressure], _
betweenValue:=1000, _
andValue:=2500)
```

Which is equivalent to:

"What is the

`Casing Pressure`

that maximizes`Oil Rate`

, searching between`Casing Pressures`

of 1000 and 2500[psig]."

The returned value will be the value of the **Input Variable** that maximizes the **Target Variable**.

If the trend of the **Target Variable** shows no maximum point between the searched **range**, then one of the extremes will be returned, whatever one that makes the **Target Variable** higher, and the `regressionStatus`

will be set to [No Solution Found].

If the trend of the **Target Variable** shows a maximum point, but that point seems to be outside the searched **range**, then one of the extremes will be returned, and the `regressionStatus`

will be set to [Solution Not Found in Range].

If the trend of the **Target Variable** shows a maximum point, and seems to be inside the searched **range**, then the maximum point will be searched for.

After a certain number of iterations, if the solution has not been found within a certain tolerance (the `precision`

variable), then the returned value will be the best guess so far, and the `regressionStatus`

will be set to [Loops Exceeded].

If, however, the solution is found within the tolerance set by `precision`

, then the returned value will be the value of the **Input Variable** that maximizes the **Target Variable**, and the `regressionStatus`

will be set to [Converged].

Parameters:

`variable`

:**Target variable**that will try to be minimized`byChanging`

: The**Input Variable**will be modified until the minimum is found`betweenValue`

and`andValue`

:**Range of Search**, defined by minimum and maximum values for the Input variable.`precision`

(optional): The precision, in terms of the target value, that dictates when to stop the algorithm.

Usage:

```
[F2] = myCalc.minimize(variable:=[Bottomhole Pressure], _
byChanging:=[Gaslift Injection Rate], _
betweenValue:=2, _
andValue:=10)
```

Which is equivalent to:

"What is the

`Gaslift Injection Rate`

that minimizes`Bottomhole Pressure`

, searching between`Injection Rates`

of 2 and 10[MMScfd]."

The returned value will be the value of the **Input Variable** that maximizes the **Target Variable**.

The same flag concepts described in the `maximize`

function apply.

Parameters:

`curve`

: The**Curve**that is going to be matched`toPoints`

: The**Test Points**that will be used to find the match`byChanging`

: The**Input Variable**that will be modified to meet the conditions`betweenValue`

and`andValue`

:**Range of Search**, defined by minimum and maximum values for the Input variable`precision`

(optional): The precision, in terms of the target value, to which to adjust the match

Usage:

```
[F2] = myCalc.match(curve:=[IPR Pressure], _
toPoints:=testPoints, _
byChanging:=Skin, _
betweenValue:=0, _
andValue:=12)
```

Which is equivalent to:

"We will look for the

`Skin`

factor value that will make the`IPR`

curve match our`Test Points`

, searching between`Skin`

factors 0 and 12."

The **Curve** parameter can be filled using intellisense, just by adding either the rate vector (**rv**) or depth vector (**dv**) prefixes. To show the whole list of options, use the prefix **rv** (for rate vectors) and **dv** for depth vectors, as in this example:

*Even though this example is showing the vector function, the prefixes work the same way here.*

If you want to match the IPR curve, for exmple, you would choose to match the IPR Pressures, which is a **rate vector**. An example was created to exemplify in depth the use of this functionality: how to obtain curves from PROSPER.

The **Test Points** passed in to this function should be a `vector`

object. Usually, the `newFromRange`

method is used to grab the Test points from the worksheet directly, or the `addPoint`

method to add points one at a time.

*Remember: The units are implied in the PROSPER model, or depending on the setting of* `useRawValues`

.

This regression algorithm will only find a solution if the seeked value of the **Input Variable** is between the range given (defined by the `betweenValue`

& `andValue`

parameters).

The returned value is the value of the **Input Variable** that matches the provided curve within the tolerance.

The same flag concepts described in the `maximize`

function apply.

- Use of the Match function
- How to obtain curves from PROSPER.

Parameters:

`of`

: The**Output Variable**whose rate of change is evaluated`whenChanging`

: The**Input Variable**that will be modified`atValue`

: The rate of change returned corresponds to a certain value of the**Input Variable**. As rates of change are not constant, such value need to be informed.

Usage:

```
[F2] = myCalc.rateOfChange(of:=[Q Liquid], _
whenChanging:=[Top Node Pressure], _
atValue:=450)
```

Which is equivalent to:

"How sensible is the

`Liquid Rate`

of my well to`WHP`

? Current`WHP`

is 450[psig]."

This function will return the derivative of the **Output Variable** with respect to the **Input Variable** at the given point.

In the above example, the returned value will be in [STB/d per psi]. This function can be used for marginal decision making analysis.

The **vector** object was designed to hold the curves obtained from PROSPER calculations, mainly from the .vector() function.

It provides enhanced functions and methods that facilitate the manipulation of curves obtained from PROSPER in Excel.

Please see the following sections regarding the Vector Object:

The Vector Object has the following Properties:

`first`

and`last`

, to get the first or last values of the curve`min`

and`max`

, to get the extreme values of the curve`points`

, to get the number of points in the curve

Parameter:

`coordinate`

(optional): If the string "X" is passed, then the returned value will be the X coordinate. Otherwise, the Y coordinate is returned.

Returns the Y-coordinate of the first point in the vector. If the optional parameter passed is a string "X", then the X-coordinate of the first point in the vector is returned.

Works really the same as `first`

, only that it returns the last point.

Parameter:

`coordinate`

(optional): If the string "X" is passed, then the returned value will be the X coordinate. Otherwise, the Y coordinate is returned.

Returns the Y-coordinate of the minimum point in the vector/curve. The comparison is done using the Y-values. If the optional parameter passed is a string "X", then the X-coordinate of the minimum point in the vector is returned.

Works really the same as `min`

, only that it returns the maximum point.

Returns the number of points in the vector. Will return zero if the vector has no points assigned yet.

The Vector Object has the following Methods:

`add`

,`subtract`

and`multiply`

, the vector operations, can perform operations with vectors or values`printInCell`

, to print a vector in a range of cells`transpose`

, exchanges the x and y coordinates of the vector`newFromRange`

, sets the values of the vector using the given range of cells

Parameter:

`vectorOrValue`

: A vector object or a number to be added to the vector.

Usage:

There is a usage example in the `subtract`

section, which is totally compatible with the add method.

If the parameter passed is another vector, add will add the y-coordinates of two vectors. Both vectors must be equal in size, and the X-coordinates must be compatible.

In the case of scalar addition, all y-coordinates are subtracted the given scalar.

Subtract works in the same way as `add`

, only that it subtracts.

Usage:

```
Dim myCalc as New ProsperCalculation, PumpDp as New Vector, PIP as New Vector
myCalc.calculationType = System
myCalc.calculate
' PumpDp vector initially holds PDP
Set PumpDp = myCalc.vector([Pump Discharge Press.])
Set PIP = myCalc.vector([Pump Intake Press.])
' We subtract PIP to obtain Pump the DP
PumpDp.subtract PIP
' We can now print the results.. or do other calculations
```

The script above is equivalent to:

Make a System calculation, find the PDP and PIP curves, and subtract them to get the DP across the Pump.

Parameter:

`vectorOrValue`

: A vector object or a number to be multiplied with the vector.

Multiply will make the multiplication of two vectors' values or the multiplication of all values in the vector by the provided scalar.

In the case of vector multiplication, both vectors must be equal in size, and the Y-values are multiplied like this:

`y_i = y_i * z_i`

, where y and z are the two vectors, and i the index.

In the case of scalar multiplication, all y-coordinates are multiplied by the given scalar.

Parameters:

`inRange`

: The top-left cell where the data will be printed`direction`

(optional): The direction to print. Can be`printDown`

or`printRight`

. Defaults to`printDown`

`printX`

(optional): Boolean. Choose to print the X-values or not. Defaults to True

Usage:

```
myVector.printInCell Range("B5")
' Will print the vector starting from B5 down, including the x-coordinates of myVector
```

You can pass othe optional values like this:

```
myVector.printInCell Range("B5"), printX:=False
' Will only print the Y-values of the vector
```

**TIP**: you can reference ranges in VBA with the shorter syntax:

```
Range("F15")
' is equivalent to
[F15]
' which makes
myVector.printInCell [F15]
' insanely simple
```

Transpose exchanges the X and Y coordinates of a vector.

Sets the vector equal to the values in the range of cells passed. The range must be a column-oriented range with the values you want to have inside the vector object.

Parameters:

`rng`

: Range of cells to be converted to a vector, or name of range of cells.

Usage:

```
Dim matchingPoints as New Vector
matchingPoints.newFromRange Range("A2:B9")
' matchingPoints holds a vector from that range
```

The `rng`

parameter can be a range or a string:

```
matchingPoints.newFromRange Range("A2:B9")
' is equivalent to
matchingPoints.newFromRange "A2:B9"
```

Vector Object has the following Functions:

`xFromY`

, to obtain the X value that corresponds to a given Y value`Y`

, to obtain the Y value that corresponds to a given X value`dYdX`

, to obtain the Y derivative at a given X value`MSE`

, returns the mean square error between the vector and matching points

Parameters:

`Y`

: The Y value you will use to look for the X coordinate.`searchDirection`

(optional): can be either`rightToLeft`

or`leftToRight`

, and will determine the search direction of the algorithm

Usage:

```
Dim myCalc as New ProsperCalculation, IPR as as New Vector, LIQUID_RATE as as Double, BHP as Double
myCalc.calculationType = Inflow
myCalc.calculate
Set IPRcurve = myCalc.vector([IPR Pressure])
BHP = 600
LIQUID_RATE = IPR.xFromY(BHP)
```

Is equivalent to:

Once you have the IPR of the well, find the rate that will be produced if the BHP is equal to 600.

This function will try to find the X value that corresponds to the given Y value. The searchDirection can be used to modify the algorithm and condition the result found, particularly in non-monotonous curves, as the VLP curve.

The interpolation between the points is linear interpolation.

Parameter:

`X`

: The X value you will use to look for the corresponding Y coordinate.

Usage:

```
Dim myCalc as New ProsperCalculation, Pres_gradient as New Vector, Gauge_D as Double, P_At_Gauge_D as Double
myCalc.calculationType = Gradient
myCalc.calculate
Set Pres_gradient = myCalc.vector(Pressure)
Gauge_D = 3540
P_At_Gauge_D = Pres_gradient(Gauge_D)
```

Is equivalent to:

Calculate the Flowing Pressure Gradient of the well, store it in the PressGradient variable, and find the pressure at the Gauge Depth.

The Y function returns the Y value that corresponds to a given X value. If the X value does not lie in the X-range of the vector, the last points will be used to extrapolate.

Linear interpolation/extrapolation is used in this function.

Parameter:

`X`

: The X value you will use to look for the corresponding Y derivative.

The Usage is very similar to the `Y`

function.

Returns the derivative value with respect to X of the curve, at the given X value.

Parameter:

`Points`

: The Points to compare with the vector

MSE will return the Mean Squared error

Usage:

```
dim myCalc as New ProsperCalculation, IPRcurve as New Vector, matchingPoints as New Vector
myCalc.calculationType = Inflow
myCalc.calculate
Set IPRcurve = myCalc.vector([IPR Pressure])
matchingPoints.newFromRange("B4:C9")
[F3] = IPRcurve.MSE(matchingPoints)
```

Is equivalent to:

Calculate the IPR of the well, and record the Mean Squared Error in cell F3 when comparing the IPR to the matching points in the range B4:C9.

This function returns the Mean Squared Error that exists between the points and a given vector. To find the error, the vector is interpolated at each of the x-values of the points vector.

The Points vector must be a vector. To generate a vector from a range, the function to use is `newFromRange`

.

This function can be used in matching algorithms, to match a curve to a set of points.