Real-Time Drilling Parameter Updates
Build a real-time system that continuously updates drilling parameters in Oliasoft based on live sensor data. By implementing this integration, you can ensure your digital well model always reflects actual drilling conditions, enabling accurate real-time calculations and decision support.
Use Cases
- Hydraulics Monitoring: Update flow rates and pressure readings for real-time ECD calculations
- Torque & Drag Analysis: Feed actual hook load and torque measurements for T&D validation
- Trajectory Tracking: Update actual wellpath as drilling progresses
- Formation Evaluation: Adjust formation tops and pressures based on real-time data
Architecture Overview
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Rig Sensors │────▶│ WITSML Server │────▶│ Integration │
│ (WITS/WITSML) │ │ │ │ Service │
└─────────────────┘ └──────────────────┘ └────────┬────────┘
│
┌────────────────────────────┼────────┐
│ ▼ │
┌─────▼────────┐ ┌─────────────────┐
│ Message Queue│ │ Oliasoft │
│ (Kafka/MQTT) │ │ WellDesign API │
└──────────────┘ └─────────────────┘
Implementation Guide
Step 1: Set Up WebSocket Connection
First, establish a WebSocket connection to receive real-time drilling data.
using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using Newtonsoft.Json;
public class RealTimeDrillingClient
{
private ClientWebSocket _webSocket;
private CancellationTokenSource _cancellationTokenSource;
private readonly string _oliasoftApiKey;
private readonly string _witsmlEndpoint;
public RealTimeDrillingClient(string oliasoftApiKey, string witsmlEndpoint)
{
_oliasoftApiKey = oliasoftApiKey;
_witsmlEndpoint = witsmlEndpoint;
_webSocket = new ClientWebSocket();
_cancellationTokenSource = new CancellationTokenSource();
}
public async Task ConnectAsync()
{
try
{
await _webSocket.ConnectAsync(new Uri(_witsmlEndpoint), _cancellationTokenSource.Token);
Console.WriteLine("Connected to WITSML real-time feed");
// Start listening for messages
_ = Task.Run(async () => await ListenForUpdates());
}
catch (Exception ex)
{
Console.WriteLine($"Connection failed: {ex.Message}");
throw;
}
}
private async Task ListenForUpdates()
{
var buffer = new ArraySegment<byte>(new byte[4096]);
while (_webSocket.State == WebSocketState.Open)
{
try
{
var result = await _webSocket.ReceiveAsync(buffer, _cancellationTokenSource.Token);
if (result.MessageType == WebSocketMessageType.Text)
{
var message = Encoding.UTF8.GetString(buffer.Array, 0, result.Count);
await ProcessDrillingData(message);
}
}
catch (Exception ex)
{
Console.WriteLine($"Error receiving data: {ex.Message}");
}
}
}
}
Step 2: Process and Validate Incoming Data
Implement data validation and transformation logic to ensure data quality.
public class DrillingDataProcessor
{
private readonly Dictionary<string, double> _lastKnownValues;
private readonly double _maxChangeThreshold = 0.2; // 20% max change
public DrillingDataProcessor()
{
_lastKnownValues = new Dictionary<string, double>();
}
public async Task<DrillingParameters> ProcessDrillingData(string rawData)
{
var data = JsonConvert.DeserializeObject<WitsmlData>(rawData);
var parameters = new DrillingParameters
{
Timestamp = data.Timestamp,
DepthMD = ValidateAndSmooth("depth_md", data.DepthMD),
HookLoad = ValidateAndSmooth("hook_load", data.HookLoad),
StandpipePressure = ValidateAndSmooth("spp", data.StandpipePressure),
FlowRateIn = ValidateAndSmooth("flow_in", data.FlowRateIn),
FlowRateOut = ValidateAndSmooth("flow_out", data.FlowRateOut),
RotaryRPM = ValidateAndSmooth("rpm", data.RotaryRPM),
RotaryTorque = ValidateAndSmooth("torque", data.RotaryTorque),
WOB = ValidateAndSmooth("wob", data.WOB),
ROP = CalculateROP(data.DepthMD, data.Timestamp)
};
// Detect anomalies
if (IsAnomalous(parameters))
{
await HandleAnomaly(parameters);
}
return parameters;
}
private double ValidateAndSmooth(string parameter, double value)
{
// Check for invalid values
if (double.IsNaN(value) || double.IsInfinity(value))
{
return _lastKnownValues.ContainsKey(parameter)
? _lastKnownValues[parameter]
: 0.0;
}
// Apply smoothing for noise reduction
if (_lastKnownValues.ContainsKey(parameter))
{
var lastValue = _lastKnownValues[parameter];
var change = Math.Abs((value - lastValue) / lastValue);
// If change is too large, apply smoothing
if (change > _maxChangeThreshold)
{
value = lastValue + (value - lastValue) * 0.3; // 30% of change
}
}
_lastKnownValues[parameter] = value;
return value;
}
private bool IsAnomalous(DrillingParameters parameters)
{
// Check for common anomalies
if (parameters.FlowRateOut - parameters.FlowRateIn > 50) // Gain
{
return true;
}
if (parameters.StandpipePressure < 100 && parameters.FlowRateIn > 0) // Washout
{
return true;
}
return false;
}
}
Step 3: Update Oliasoft WellDesign
Send validated data to Oliasoft WellDesign API for real-time updates.
public class OliasoftRealtimeUpdater
{
private readonly HttpClient _httpClient;
private readonly string _baseUrl = "https://welldesign.oliasoft.com/api/v1";
private readonly string _designId;
public OliasoftRealtimeUpdater(string apiKey, string designId)
{
_httpClient = new HttpClient();
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}");
_httpClient.DefaultRequestHeaders.Add("Content-Type", "application/json");
_designId = designId;
}
public async Task UpdateDrillingParameters(DrillingParameters parameters)
{
try
{
// Update current drilling depth
await UpdateCurrentDepth(parameters.DepthMD);
// Update hydraulics parameters
await UpdateHydraulics(parameters);
// Update mechanical parameters
await UpdateMechanics(parameters);
// Trigger recalculation
await TriggerCalculations();
}
catch (Exception ex)
{
Console.WriteLine($"Failed to update Oliasoft: {ex.Message}");
// Implement retry logic
await RetryUpdate(parameters);
}
}
private async Task UpdateHydraulics(DrillingParameters parameters)
{
var hydraulicsUpdate = new
{
current_depth = parameters.DepthMD,
flow_rate = parameters.FlowRateIn,
standpipe_pressure = parameters.StandpipePressure,
mud_properties = new
{
density = parameters.MudWeight,
plastic_viscosity = parameters.PlasticViscosity,
yield_point = parameters.YieldPoint
}
};
var response = await _httpClient.PutAsJsonAsync(
$"{_baseUrl}/designs/{_designId}/realtime/hydraulics",
hydraulicsUpdate
);
if (!response.IsSuccessStatusCode)
{
throw new Exception($"Hydraulics update failed: {response.StatusCode}");
}
}
private async Task UpdateMechanics(DrillingParameters parameters)
{
var mechanicsUpdate = new
{
current_depth = parameters.DepthMD,
hook_load = parameters.HookLoad,
surface_torque = parameters.RotaryTorque,
rotary_speed = parameters.RotaryRPM,
weight_on_bit = parameters.WOB,
rate_of_penetration = parameters.ROP
};
var response = await _httpClient.PutAsJsonAsync(
$"{_baseUrl}/designs/{_designId}/realtime/mechanics",
mechanicsUpdate
);
if (!response.IsSuccessStatusCode)
{
throw new Exception($"Mechanics update failed: {response.StatusCode}");
}
}
private async Task TriggerCalculations()
{
// Trigger specific calculations based on updated parameters
var calculations = new[]
{
"ecd", // Equivalent Circulating Density
"surge_swab", // Surge and Swab pressures
"torque_drag", // Torque and Drag
"hole_cleaning" // Hole cleaning efficiency
};
foreach (var calc in calculations)
{
await _httpClient.PostAsync(
$"{_baseUrl}/designs/{_designId}/calculate/{calc}",
null
);
}
}
}
Step 4: Implement Alert System
Create an alert system for critical parameter changes.
public class DrillingAlertSystem
{
private readonly Dictionary<string, AlertThreshold> _thresholds;
private readonly INotificationService _notificationService;
public DrillingAlertSystem(INotificationService notificationService)
{
_notificationService = notificationService;
_thresholds = LoadAlertThresholds();
}
public async Task CheckAlerts(DrillingParameters current, CalculationResults results)
{
// Check ECD limits
if (results.ECD > _thresholds["ecd_max"].Value)
{
await SendAlert(new Alert
{
Type = AlertType.Critical,
Parameter = "ECD",
Message = $"ECD ({results.ECD:F2} sg) exceeds maximum limit ({_thresholds["ecd_max"].Value:F2} sg)",
Recommendations = new[]
{
"Reduce pump rate",
"Check for pack-off conditions",
"Verify mud properties"
}
});
}
// Check torque limits
var torqueLimit = GetDepthBasedLimit("max_torque", current.DepthMD);
if (current.RotaryTorque > torqueLimit)
{
await SendAlert(new Alert
{
Type = AlertType.Warning,
Parameter = "Torque",
Message = $"Surface torque ({current.RotaryTorque:F0} ft-lbs) approaching limit",
Recommendations = new[]
{
"Reduce ROP",
"Check for differential sticking",
"Verify hole cleaning"
}
});
}
// Check flow differential (kick/loss indicator)
var flowDiff = Math.Abs(current.FlowRateOut - current.FlowRateIn);
if (flowDiff > _thresholds["flow_differential"].Value)
{
var alertType = current.FlowRateOut > current.FlowRateIn
? "Possible influx"
: "Possible losses";
await SendAlert(new Alert
{
Type = AlertType.Critical,
Parameter = "Flow",
Message = $"{alertType} detected. Flow differential: {flowDiff:F0} gpm",
Recommendations = new[]
{
"Stop drilling",
"Check for gains/losses",
"Monitor pit levels"
}
});
}
}
}
Step 5: Complete Integration Service
Tie everything together in a Windows Service or containerized application.
public class RealtimeDrillingService : BackgroundService
{
private readonly RealTimeDrillingClient _drillingClient;
private readonly DrillingDataProcessor _processor;
private readonly OliasoftRealtimeUpdater _updater;
private readonly DrillingAlertSystem _alertSystem;
private readonly ILogger<RealtimeDrillingService> _logger;
public RealtimeDrillingService(
IConfiguration configuration,
ILogger<RealtimeDrillingService> logger)
{
_logger = logger;
var apiKey = configuration["Oliasoft:ApiKey"];
var designId = configuration["Oliasoft:DesignId"];
var witsmlEndpoint = configuration["WITSML:Endpoint"];
_drillingClient = new RealTimeDrillingClient(apiKey, witsmlEndpoint);
_processor = new DrillingDataProcessor();
_updater = new OliasoftRealtimeUpdater(apiKey, designId);
_alertSystem = new DrillingAlertSystem(new EmailNotificationService());
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Starting real-time drilling service");
// Connect to data sources
await _drillingClient.ConnectAsync();
// Main processing loop
while (!stoppingToken.IsCancellationRequested)
{
try
{
// Process incoming data
var rawData = await _drillingClient.ReceiveDataAsync();
var parameters = await _processor.ProcessDrillingData(rawData);
// Update Oliasoft
await _updater.UpdateDrillingParameters(parameters);
// Get calculation results
var results = await _updater.GetCalculationResults();
// Check alerts
await _alertSystem.CheckAlerts(parameters, results);
// Log metrics
LogMetrics(parameters, results);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in processing loop");
await Task.Delay(5000, stoppingToken); // Wait before retry
}
}
}
private void LogMetrics(DrillingParameters parameters, CalculationResults results)
{
_logger.LogInformation(
"Depth: {Depth:F1} ft | ROP: {ROP:F1} ft/hr | ECD: {ECD:F2} sg | SPP: {SPP:F0} psi",
parameters.DepthMD,
parameters.ROP,
results.ECD,
parameters.StandpipePressure
);
}
}
Configuration
appsettings.json
{
"Oliasoft": {
"ApiKey": "your-api-key",
"DesignId": "current-well-design-id",
"BaseUrl": "https://welldesign.oliasoft.com/api/v1"
},
"WITSML": {
"Endpoint": "wss://your-witsml-server/realtime",
"Username": "witsml-user",
"Password": "witsml-password"
},
"Alerts": {
"EmailRecipients": ["drilling-engineer@company.com"],
"SlackWebhook": "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
},
"Processing": {
"UpdateIntervalMs": 5000,
"MaxRetries": 3,
"SmoothingFactor": 0.3
}
}
Deployment Options
Docker Container
FROM mcr.microsoft.com/dotnet/runtime:7.0 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY ["RealtimeDrilling.csproj", "."]
RUN dotnet restore "RealtimeDrilling.csproj"
COPY . .
RUN dotnet build "RealtimeDrilling.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "RealtimeDrilling.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "RealtimeDrilling.dll"]
Kubernetes Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: realtime-drilling-service
spec:
replicas: 1
selector:
matchLabels:
app: realtime-drilling
template:
metadata:
labels:
app: realtime-drilling
spec:
containers:
- name: realtime-drilling
image: your-registry/realtime-drilling:latest
env:
- name: Oliasoft__ApiKey
valueFrom:
secretKeyRef:
name: oliasoft-credentials
key: api-key
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
Monitoring and Diagnostics
Health Checks
public class HealthCheck : IHealthCheck
{
private readonly RealTimeDrillingClient _client;
public async Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default)
{
try
{
var isConnected = await _client.IsConnectedAsync();
var lastUpdate = await _client.GetLastUpdateTime();
if (!isConnected)
return HealthCheckResult.Unhealthy("WITSML connection lost");
if (DateTime.UtcNow - lastUpdate > TimeSpan.FromMinutes(5))
return HealthCheckResult.Degraded("No data received for 5 minutes");
return HealthCheckResult.Healthy("All systems operational");
}
catch (Exception ex)
{
return HealthCheckResult.Unhealthy($"Health check failed: {ex.Message}");
}
}
}
Performance Metrics
Track key performance indicators:
- Latency: Time from sensor reading to Oliasoft update
- Throughput: Updates processed per second
- Error Rate: Failed updates per hour
- Data Quality: Percentage of valid vs. filtered data points
Troubleshooting
| Issue | Possible Cause | Solution |
|---|---|---|
| Connection drops | Network instability | Implement exponential backoff retry |
| High latency | Processing bottleneck | Use message queue for buffering |
| Data gaps | Sensor failures | Implement interpolation for short gaps |
| API rate limits | Too frequent updates | Batch updates, implement throttling |
Security Considerations
- Encrypt all data in transit using TLS 1.2+
- Rotate API keys regularly
- Implement access controls for the integration service
- Audit log all parameter changes
- Validate all inputs to prevent injection attacks
Next Steps
- Set up monitoring dashboards
- Implement data archival for historical analysis
- Add machine learning for predictive alerts
- Create mobile app for field notifications
For additional support, contact Oliasoft technical support or consult the API documentation.