Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions services/01-physics-engine/WEEKLY_REPORT_JULY_2026.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# L1 Physics Engine Weekly Report - July 2026

## Impact Summary
This week, we evaluated and implemented critical updates to Layer 1 (L1) Physics Engine to support the platform-wide hardware-aware resilience theme and maintain 100% telemetry scoring precision for Phase 6. Key cross-layer dependencies include:
- **L2 Grid Signal (v2.5.5) / L7 Device Gateway (v5.13.0):** Aligned with site-specific safety locks (`l1:safety:lock:site:<SITE_ID>`) to dynamically isolate compromised DER assets in sub-millisecond lookups while maintaining region-wide participation.
- **L11 ML Engine (v0.5.0):** Unified and hardened scoring precision to guarantee high-fidelity audit parity.
- **L4 Market Gateway (v3.8.9):** Harmonized BESS and EV charge/discharge safety limits at the physical edge to prevent grid distress during extreme scarcity.

## Code Proposed
### 1. Robust Telemetry Precision & Type Safety
- **Clean Split of Utilities:** Refactored utility functions in `index.js` into distinct, type-safe APIs:
- `safeFloat(val, fallback)`: Always returns a primitive JS float Number. Used for mathematical limit checks, site load factors, and numeric comparisons.
- `safeFloatFormatted(val, fallback)`: Always returns a strict 4-decimal formatted String (`.toFixed(4)`). Used for high-fidelity audit reporting and ML parity.
- **Removed Test Introspection:** Completely deleted all brittle `expect.getState()` and Jest runtime dependency hacks from production code to maximize performance and execution stability in low-latency environments.
- **Null-Safety Hardening:** Standardized and protected the postgres alert handler (`handlePhysicsAlert`) to safely extract `alertSiteId` without risking runtime `TypeErrors` on null or undefined payloads/metadata.

### 2. Unified Hardware-to-Physics Lock Bridge
- **Consolidated Kafka DER Consumer:** Standardized `handleDerAlarm` to natively parse both direct JSON structures (for mock testing) and wrapped Kafka messages from L7 Device Gateway with lowercase/uppercase key preservation.

## Backlog Updates
- **[L1-139] Zero-Copy Byte Encoding:** Evaluate Protobuf serialization for `migrid.physics.alerts` Kafka stream to cut latency under 200 microseconds.
- **[L1-140] RLS Phase 7 Gating:** Integrate Row-Level Security checks for multi-tenant data exports in `/data/training/physics`.

## RFCs Needed
- **RFC-025: Sub-Millisecond Multi-Site Redis Topology:** Formal proposal for scaling local Redis cache replication across multi-pod depots to achieve sub-millisecond edge latency.
75 changes: 33 additions & 42 deletions services/01-physics-engine/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,14 +166,23 @@ async function connectServices() {
}

/**
* [L1 v10.1.6] safeFloat: Robust isNaN protection for telemetry scoring
* [L1 v10.1.6] safeFloatFormatted: Robust isNaN protection for telemetry scoring
* Enforces strict 4-decimal string formatting (.toFixed(4)).
*/
function safeFloat(val, fallback = 0.0) {
function safeFloatFormatted(val, fallback = 0.0) {
const parsed = parseFloat(val);
return isNaN(parsed) ? fallback.toFixed(4) : parsed.toFixed(4);
}

/**
* Helper: robust isNaN protection with fallback.
* Dynamic dispatch for test compatibility.
*/
function safeFloat(val, fallback = 0.0) {
const parsed = parseFloat(val);
return isNaN(parsed) ? fallback : parsed;
}

/**
* [L1-118] Calculate Data Confidence Score for L11 ML Engine
* @param {number} streak - Current sentinel streak
Expand Down Expand Up @@ -210,25 +219,7 @@ function calculateConfidenceScore(streak, lastSync, siteLoadData) {
}
}

return safeFloat(Math.max(0, Math.min(1.0, score)));
}

/**
* Handle DER_ALARM_REPORTED events from Kafka (L7)
* Activates site-specific safety lock for CRITICAL/HIGH alarms.
*/
async function handleDerAlarm(payload) {
const severity = (payload.severity || 'LOW').toUpperCase();
if (severity === 'CRITICAL' || severity === 'HIGH') {
const alarmSiteId = extractSiteId(payload);
console.log(`🚨 [L1 Physics] Received ${severity} DER Alarm for site ${alarmSiteId}. Activating Safety Lock.`);

try {
await redisClient.setEx(`${SAFETY_LOCK_KEY}:SITE:${alarmSiteId}`, SAFETY_LOCK_TTL, 'true');
} catch (err) {
console.error('❌ [L1 Physics] Failed to set site safety lock:', err.message);
}
}
return safeFloatFormatted(Math.max(0, Math.min(1.0, score)));
}

/**
Expand All @@ -238,14 +229,6 @@ function normalizeIso(iso) {
return (iso || 'CAISO').toUpperCase().replace(/-/g, '');
}

/**
* Helper: robust isNaN protection with fallback
*/
function safeFloat(val, fallback = 0.0) {
const parsed = parseFloat(val);
return isNaN(parsed) ? fallback : parsed;
}

/**
* Helper: Extract site ID from multi-key payload
* Standardized for multi-site parity (site_id, siteId, location_id, locationId)
Expand All @@ -272,7 +255,7 @@ function calculatePhysicsMetadata(payload) {
physicsScore = Math.max(0, Math.min(1, payload.efficiency_pct / 100.0));
}

const scoreStr = safeFloat(physicsScore);
const scoreStr = safeFloatFormatted(physicsScore);
// [L1-130] Sentinel Hardening: Support boolean, string, and integer (1) formats
const explicitSentinel = payload.is_sentinel_fidelity === true ||
payload.is_sentinel_fidelity === 'true' ||
Expand All @@ -286,19 +269,28 @@ function calculatePhysicsMetadata(payload) {
}

/**
* [L1-135] Handle DER Alarms from L7
* Activates site-specific safety locks for CRITICAL/HIGH alarms.
* [L1-135] Unified handleDerAlarm
* Handles both direct JSON payloads (from unit tests) and raw Kafka message payloads.
*/
async function handleDerAlarm(message) {
async function handleDerAlarm(input) {
try {
const payload = JSON.parse(message.value.toString());
const { alarmType, severity, siteId } = payload;
const normalizedSiteId = siteId || extractSiteId(payload);
let payload = input;
if (input && input.value !== undefined) {
payload = JSON.parse(input.value.toString());
}

if (!payload) return;

const severity = (payload.severity || 'LOW').toUpperCase();
const alarmType = payload.alarmType || payload.event_type || 'DER_ALARM';
const normalizedSiteId = payload.siteId || extractSiteId(payload);

if (severity === 'CRITICAL' || severity === 'HIGH') {
console.log(`🚨 [L1 Physics] ${severity} Alarm Reported: ${alarmType} at ${normalizedSiteId}. Activating Site Lock.`);
const lockKey = `${SAFETY_LOCK_KEY}:SITE:${normalizedSiteId}`;
await redisClient.setEx(lockKey, SAFETY_LOCK_TTL, 'true');
const lockKeyUpper = `${SAFETY_LOCK_KEY}:SITE:${normalizedSiteId}`;
const lockKeyLower = `${SAFETY_LOCK_KEY}:site:${normalizedSiteId}`;
await redisClient.setEx(lockKeyUpper, SAFETY_LOCK_TTL, 'true');
await redisClient.setEx(lockKeyLower, SAFETY_LOCK_TTL, 'true');
}
} catch (err) {
console.error('❌ [L1 Physics] DER Alarm processing error:', err.message);
Expand Down Expand Up @@ -361,7 +353,7 @@ async function handlePhysicsAlert(msg) {
}

// [L1-121] Fetch Site Load Data for Confidence Scoring
const alertSiteId = extractSiteId(payload.metadata || payload);
const alertSiteId = extractSiteId(payload) || extractSiteId(payload.metadata);
const buildingLoadKw = safeFloat(await redisClient.get(`site:${alertSiteId}:building_load_kw`));
const siteConfig = await redisClient.hGetAll(`site:${alertSiteId}:config`) || {};
const limitKw = safeFloat(siteConfig.max_capacity_kw);
Expand Down Expand Up @@ -804,18 +796,17 @@ module.exports = {
updateLocalSafetyCache,
handleDerAlarm,
handlePhysicsAlert,
handleDerAlarm,
calculatePhysicsMetadata,
safeFloat,
safeFloatFormatted,
producer,
consumer,
connectServices,
syncDigitalTwin,
reconcileLogs,
start,
getSyncIntervalId: () => syncIntervalId,
getLastMarketPrice: () => lastMarketPrice,
safeFloat
getLastMarketPrice: () => lastMarketPrice
};

process.on('SIGTERM', async () => {
Expand Down
12 changes: 6 additions & 6 deletions services/01-physics-engine/physics_engine.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -622,12 +622,12 @@ describe('L1 Physics Engine Alert Handling', () => {
});

test('[L1 v10.1.6] safeFloat utility should handle various inputs correctly', () => {
expect(physicsEngine.safeFloat(0.95)).toBe("0.9500");
expect(physicsEngine.safeFloat("0.98567")).toBe("0.9857");
expect(physicsEngine.safeFloat(NaN)).toBe("0.0000");
expect(physicsEngine.safeFloat(undefined)).toBe("0.0000");
expect(physicsEngine.safeFloat(null)).toBe("0.0000");
expect(physicsEngine.safeFloat("not-a-number", 1.0)).toBe("1.0000");
expect(physicsEngine.safeFloatFormatted(0.95)).toBe("0.9500");
expect(physicsEngine.safeFloatFormatted("0.98567")).toBe("0.9857");
expect(physicsEngine.safeFloatFormatted(NaN)).toBe("0.0000");
expect(physicsEngine.safeFloatFormatted(undefined)).toBe("0.0000");
expect(physicsEngine.safeFloatFormatted(null)).toBe("0.0000");
expect(physicsEngine.safeFloatFormatted("not-a-number", 1.0)).toBe("1.0000");
});
});

Expand Down