Digital Twin Integration Guide
This guide demonstrates how to integrate NeqSim’s process simulation capabilities with digital twin architectures for real-time operations.
Overview
A digital twin combines:
- Physics-Based Model: NeqSim’s rigorous thermodynamic simulation
- Real-Time Data: Live sensor readings from the physical asset
- Predictive Analytics: Look-ahead simulations for advisory systems
- ML Acceleration: Surrogate models for faster execution
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Physical Asset │
│ (Platform, Plant, etc.) │
└────────────────────────┬────────────────────────────────────────┘
│ Sensors (OPC UA)
▼
┌─────────────────────────────────────────────────────────────────┐
│ Data Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Historian │ │ Real-Time │ │ Event │ │
│ │ (PI, OSI) │ │ Database │ │ Streaming │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└────────────────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ NeqSim Digital Twin │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ ProcessSystem│ │ Surrogate │ │ Physics │ │
│ │ (Physics) │ │ Registry │ │ Validator │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Prediction │ │ Emissions │ │ Model │ │
│ │ Engine │ │ Tracker │ │ Metadata │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└────────────────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Applications │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Advisory │ │ MPC/APC │ │ Operations │ │
│ │ System │ │ Controller │ │ Dashboard │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Implementation Patterns
1. State Synchronization
Keep the digital twin synchronized with the physical asset:
public class DigitalTwinService {
private final ProcessSystem model;
private final ProcessSystemState currentState;
private final ModelMetadata metadata;
public DigitalTwinService(ProcessSystem model) {
this.model = model;
this.currentState = ProcessSystemState.fromProcessSystem(model);
this.metadata = new ModelMetadata();
this.metadata.setLifecyclePhase(LifecyclePhase.OPERATION);
}
/**
* Update model from live sensor data.
*/
public void synchronize(Map<String, Double> sensorData) {
// Update inlet conditions from sensors
Stream inlet = (Stream) model.getUnit("inlet");
if (sensorData.containsKey("inlet.temperature")) {
inlet.setTemperature(sensorData.get("inlet.temperature"), "C");
}
if (sensorData.containsKey("inlet.pressure")) {
inlet.setPressure(sensorData.get("inlet.pressure"), "bara");
}
if (sensorData.containsKey("inlet.flowrate")) {
inlet.setFlowRate(sensorData.get("inlet.flowrate"), "kg/hr");
}
// Re-run model with updated inputs
model.run();
// Capture new state
currentState.updateFrom(model);
}
/**
* Save checkpoint for audit trail.
*/
public void checkpoint(String version, String description) {
ProcessSystemState state = ProcessSystemState.fromProcessSystem(model);
state.setVersion(version);
state.setDescription(description);
state.saveToFile("checkpoints/model_" + version + ".json");
metadata.recordModification(description);
}
}
2. Look-Ahead Prediction
Generate predictions for operator advisory:
public class AdvisoryService {
private final ProcessSystem model;
private final PhysicsConstraintValidator validator;
public AdvisoryService(ProcessSystem model) {
this.model = model;
this.validator = new PhysicsConstraintValidator(model);
}
/**
* Run look-ahead simulation and generate advisory.
*/
public PredictionResult predict(Duration horizon) {
// Clone model for prediction (don't affect main state)
ProcessSystem predictModel = model.copy();
PredictionResult result = new PredictionResult(horizon, "Look-ahead");
try {
// Run prediction (could include trend extrapolation)
predictModel.run();
// Collect predicted values
for (ProcessEquipmentInterface unit : predictModel.getUnitOperations()) {
if (unit instanceof Separator) {
Separator sep = (Separator) unit;
result.addPredictedValue(
sep.getName() + ".pressure",
new PredictedValue(sep.getPressure(), 0.5, "bara")
);
result.addPredictedValue(
sep.getName() + ".temperature",
new PredictedValue(sep.getTemperature(), 1.0, "C")
);
}
}
// Check for constraint violations
ValidationResult validation = validator.validateCurrentState();
for (ConstraintViolation violation : validation.getViolations()) {
result.addViolation(violation);
}
} catch (Exception e) {
result.setStatus(PredictionStatus.FAILED);
result.setStatusMessage("Prediction failed: " + e.getMessage());
}
return result;
}
}
3. Hybrid Physics-ML Execution
Use surrogates for speed, physics for accuracy:
Declare model input/output dimensions and training bounds before registering a surrogate. The registry rejects malformed requests before either callback and validates numeric results from both paths; see the ML validation and fallback contract. Apply model-specific conservation and physical checks before using the result to update the process. Fallback failures are explicit errors, so the caller must retain the previous valid state or otherwise handle the unavailable result.
public class HybridExecutionService {
private final ProcessSystem physicsModel;
private final SurrogateModelRegistry surrogateRegistry;
public HybridExecutionService(ProcessSystem physicsModel) {
this.physicsModel = physicsModel;
this.surrogateRegistry = SurrogateModelRegistry.getInstance();
}
/**
* Execute with automatic physics/ML selection.
*/
public void executeWithFallback(String unitName, double[] inputs) {
String surrogateKey = unitName + "-surrogate";
// Try surrogate first, fall back to physics
double[] result = surrogateRegistry.predictWithFallback(
surrogateKey,
inputs,
this::runPhysicsCalculation
);
// Apply result to model
applyResult(unitName, result);
}
private double[] runPhysicsCalculation(double[] inputs) {
// Run full physics simulation
physicsModel.run();
return extractOutputs(physicsModel);
}
}
4. Emissions Monitoring
Track emissions in real-time:
public class EmissionsMonitoringService {
private final ProcessSystem model;
private final EmissionsTracker tracker;
private final List<EmissionsSnapshot> history;
public EmissionsMonitoringService(ProcessSystem model, double gridFactor) {
this.model = model;
this.tracker = new EmissionsTracker(model);
this.tracker.setGridEmissionFactor(gridFactor);
this.history = new ArrayList<>();
}
/**
* Record current emissions snapshot.
*/
public void recordSnapshot() {
EmissionsReport report = tracker.calculateEmissions();
EmissionsSnapshot snapshot = new EmissionsSnapshot(
Instant.now(),
report.getTotalCO2e("kg/hr"),
report.getTotalPower("kW")
);
history.add(snapshot);
}
/**
* Get cumulative emissions for period.
*/
public double getCumulativeCO2e(Instant start, Instant end) {
return history.stream()
.filter(s -> !s.timestamp.isBefore(start) && !s.timestamp.isAfter(end))
.mapToDouble(s -> s.co2eKgPerHr / 3600.0) // Convert to kg/s for integration
.sum();
}
}
Deployment Patterns
Containerized Deployment
FROM eclipse-temurin:21-jre
# Copy NeqSim and application
COPY target/neqsim-*.jar /app/neqsim.jar
COPY target/digital-twin.jar /app/app.jar
COPY models/ /app/models/
# Configure
ENV JAVA_OPTS="-Xmx4g"
ENV MODEL_PATH="/app/models/current.json"
WORKDIR /app
ENTRYPOINT ["java", "-jar", "app.jar"]
Kubernetes Scaling
apiVersion: apps/v1
kind: Deployment
metadata:
name: neqsim-digital-twin
spec:
replicas: 3
selector:
matchLabels:
app: digital-twin
template:
metadata:
labels:
app: digital-twin
spec:
containers:
- name: neqsim
image: neqsim-digital-twin:latest
resources:
requests:
memory: "4Gi"
cpu: "2"
limits:
memory: "8Gi"
cpu: "4"
env:
- name: GRID_EMISSION_FACTOR
value: "0.05" # Norway
Data Integration
OPC UA Integration (Conceptual)
// Pseudo-code for OPC UA integration
public class OpcUaConnector {
private final DigitalTwinService twinService;
public void startSubscription() {
opcClient.createSubscription(1000, (nodeId, value) -> {
Map<String, Double> sensorData = new HashMap<>();
sensorData.put(nodeId.getIdentifier(), value);
// Update digital twin
twinService.synchronize(sensorData);
});
}
}
Kafka Event Streaming (Conceptual)
// Pseudo-code for Kafka integration
public class KafkaStreamProcessor {
public void processStream() {
kafkaConsumer.subscribe("sensor-data");
while (running) {
ConsumerRecords<String, SensorReading> records =
kafkaConsumer.poll(Duration.ofMillis(100));
for (ConsumerRecord<String, SensorReading> record : records) {
twinService.synchronize(record.value().toMap());
}
}
}
}
Best Practices
- State Versioning: Checkpoint model state regularly for audit and rollback
- Validation: Always validate AI recommendations against physics constraints
- Fallback Strategy: Ensure physics calculation is always available as fallback
- Monitoring: Track prediction accuracy and surrogate model health
- Emissions: Use location-specific grid emission factors
Related Documentation
- Lifecycle Management - State management and versioning
- ML Integration - Surrogate models and physics validation
- Advisory Systems - Prediction results
- Sustainability - Emissions tracking