跳轉到

2026-W33 週報:把 10 秒缺口的兩端補起來

第三份週報,本週 5 張卡,是目前為止產量最高的一週(五個工作日五張,佇列沒有落後)。

W32 建立了 NFPA 110 的 10 秒預算,但留了一個沒回答的問題:那 10 秒,誰在撐? 本週前後兩端一起補上——dc-08 / dc-09 補前端(電池撐住那 10 秒),dc-06 補後端(燃油決定發電機接手後能撐多久),中間夾著把電分出去的兩張盤卡(dc-07 / dc-07b)。

節奏備註:dc-07 原稿 14238 字元、dc-09 原稿 11929 字元,兩張都超過 10000 上限被迫就地拆出 dc-07bdc-09b。W32 週報說過「排 dc-08 前先想清楚要不要主動拆」——結果拆的不是 dc-08,是它的前後鄰居。規律開始清楚了:只要一張卡同時包含「額定語意」與「測試/合規制度」,就是兩張。 佇列剩餘項目請照這個規則預先掃一遍,不要每次都在寫到一半才發現。

本週卡片

標題 這張卡最關鍵的一個事實
dc-06 日用油箱與儲油槽 續航不是把油量加總除以耗油率——燃油是一張圖不是一個總和。移走任一元件後的最小值才是可用數字(本週實例:31.78 h 的總量,逐一移除後只剩 16.40 h;共用一顆泵時剩 1.03 h
dc-07 低壓主配電盤 — 額定電流體系 同一個物理量有四個合法數值InA / Inc / Ing / RDF)。任何叫 rated_current 的單一欄位都會在某次容量檢討時害到人——它到底是哪一個?
dc-07b 短路耐受與保護協調 Icc 是「一對設備共同擁有的屬性」,不是這面盤的屬性。上游斷路器一換,那 65 kA 就該失效,而現行直覺(額定放在 device 上)沒有任何機制做得到
dc-08 UPS 不斷電系統(雙轉換式) 負載率告警的分母是 n_required × 最小成員容量,不是單機銘牌。同一組 UPS,67.5%90% 都是「正確」的負載率,但只有後者會讓你在故障前就知道要擴容
dc-09 UPS 電池組(VRLA vs 鋰電) runtime 的四個數字差 1.8 倍(規格 8 / 現況 13.1 / N−1 後 9.1 / N−1 且 EOL 7.3 分鐘),而儀表板通常顯示的是第一個

串起來

一、全鏈第一次出現「時間」這個容量維度

dc-01 ~ dc-05 的容量單位全部是功率:kVA、kW、A。它們回答的是「同時能供多少」。本週兩張卡換了單位:

容量單位 換算成時間的方式
dc-06 公升(能量的代理) 可達油量 ÷ 耗油率(負載率) → 小時級
dc-09 W/cell 曲線 → kWh 查表 × Peukert × 老化 × 溫度 → 分鐘級

於是整條備援鏈第一次可以畫在時間軸上,而不是拓撲圖上:

市電消失
  ├─ 0 s ────────────── 11.09 s ─────────────────── 12 h ────────→ 補油卡車
  │   ↑                    ↑                          ↑
  │  電池 dc-09          發電機 dc-05              儲油 dc-06
  │  分鐘級(7.3–13.1)   小時級                    Uptime Tier 要求
  └─ UPS dc-08 是決定「現在用哪一段」的那台機器——它自己不儲能

兩段之間有一個必須明寫的不變式電池 runtime(最壞情況) > 發電機起動到穩定帶載的時間 × 安全係數。本週 dc-09 的加分題就是把它寫成告警規則(10 s × 20 = 200 s)。用本週的數字驗算:N−1 且 EOL 時 7.3 min = 438 s,過關,但餘裕來自運氣不是設計——沒有人算過這條式子,它只是碰巧成立。

這也是模型第一次需要一條「跨三個實體」的規則BatteryBank × RedundancyGroup × Genset)。告警掛在任何單一設備上都寫不出來——這是「DCIM 不能只做設備清單」的第一個具體證據。

二、電力路徑上,誰在誰上游

市電 dc-01 → MV dc-03 → 變壓器 dc-02 ─┐
                                       ├→ ATS dc-04 → LV 主盤 dc-07 ──→ UPS dc-08 ──→(dc-10 STS / dc-11 PDU)
燃料 dc-06 → 發電機 dc-05 ─────────────┘         ↑                        ↑
                                            [保護層 dc-07b]          [電池 dc-09]
                                            疊在盤上的邊               掛在直流匯流排

三件事值得停下來看:

(1)dc-06dc-09 是同一種形狀,但過去三週沒被認出來。 兩者都是儲能節點:不在電力路徑上(電不從油箱流過、也不從電池流過),但決定了路徑能中斷多久。W32 說「發電機是全鏈唯一一台上游不是電的設備」——本週補完:它的上游是 dc-06,而 dc-08 的上游除了電還有 dc-09

一般節點:  上游功率 → [設備] → 下游功率        容量單位 = 功率
儲能節點:  存量 ÷ 抽取率 = 剩餘時間            容量單位 = 能量

FuelTankBatteryString 應該共用一個抽象capacity / usable(非全部可取)/ depletion_rate(load) / autonomy() 為推導值 / EOL 或補充機制)。兩張卡各自發明了一套算法,但骨架幾乎同構——見下面連貫性檢視第 2 點。

(2)dc-07b 是全鏈第一張「不是節點也不是設備」的卡。 Icw / Icc 掛在盤上勉強算屬性,但 SelectivityPair 明確是邊的屬性:選擇性是「上游這顆與下游那顆的關係」,換掉任一端就整格失效。W32 從 (genset, ats) 輪替第一次碰到邊上的約束,本週是第二次,而且更根本——保護協調整層都住在邊上。 現在的 DeviceRegistry 設計(節點字典 + upstream_id)沒有地方掛它,因為邊還沒有身分。這件事已經被三張卡點名(dc-03b 互鎖規則、dc-05c ATS 輪替、dc-07b 選擇性),不能再拖。

(3)冷卻鏈這週沒被點名,但欠款還在。 W32 留下的問題(冰水主機/CRAH 接 UPS 後面還是直吃發電機)本週沒有新資訊,因為五張卡全在電力鏈上。dc-08 給了一個新的判準:接在 UPS 後面的東西,其 kW 全數計入 n_minus_1_kw() 的分子。1 MW 機房把冷卻掛進 UPS,UPS 群組要大一輪(且電池 runtime 全部重算)。這不再只是「切換時會不會跳」的可靠度問題,而是直接改變 UPS 採購容量的成本問題——問 facility 的急迫度因此升一級。

三、本週母題:上限有多個競爭來源,而「誰綁死的」比「上限是多少」更有用

三週三個母題,一次比一次抽象:

週次 母題
W31 單一欄位裝不下一個事實(數字要帶著量測條件)
W32 有些事實根本不住在欄位裡(住在序列、存量、事實與規則的多對多裡)
W33 上限是多個候選取 min/max,回傳裸數字就丟掉了最有用的資訊

本週五張卡全部是這個形狀,沒有例外:

上限 候選來源
dc-06 autonomy_hours 逐一移除每個 tank/component 的情境 min
dc-07 max_continuous 元件額定×80%×溫度降額 / 群組 Ing = Inc × RDF min
dc-07b available_fault_ka 每個合法 SystemConfig(tie 開/閉、幾台並聯) max
dc-08 usable_kw rating_kwrating_kva × output_pf min
dc-09 runtime_min 曲線查表 → Peukert → 老化 → 溫度(連乘不是取 min,但同樣多來源) 連乘

只有 dc-07 的骨架明確要求回傳 (284.0, "assembly_ing") 而非裸浮點數,理由講得最清楚:

「怎麼放寬」取決於誰綁死的。

這句話對另外四張同樣成立,但另外四張都回傳裸數字。這是本週最該立刻推廣的一個模式,而且它有一個立即可驗證的好處——dc-07 驗收表裡那四行:

rdf=0.8, MCCB   → (284.0, "assembly_ing")
rdf=0.8, LVPCB  → (284.0, "assembly_ing")   ← 花錢換 100% 額定斷路器,上限完全沒動
rdf=1.0, MCCB   → (320.0, "device")         ← 綁死的來源換人了
rdf=1.0, LVPCB  → (355.0, "device")

第二行是一張採購決策的成本表。 只回傳 284.0,你不會知道那筆升級的錢是白花的。

四、第三次撞上「只有故障當下才會被用到的功能」

功能 平時可觀測嗎 模型只能存什麼
dc-05c 發電機起動能力 ❌ 停著的機所有讀值都正常 last_test_at + 到期告警
dc-06 燃油品質 ❌ 沒有感測器會叫你 next_polish_due(純日期推導)
dc-07b ZSI 邏輯正確性 ❌ 正常運轉時不會動作 last_zsi_functional_test_at

三張卡,三個不同子系統,同一個結論:

凡是「只有故障當下才會被使用的功能」,都必須有一個測試日期欄位;沒有它,資料庫會沉默地說謊——而且所有儀表板都是綠的。

出現三次就不該再各自實作第四次。這已經夠格抽成一個共用結構(PeriodicVerification),細節見連貫性檢視第 1 點。

還有一個更尖銳的變形,本週第一次出現: dc-07b 演算 2 的 tie 閉合違規是事前可算的——不必等故障、不必做測試,只要枚舉配置就知道「這個配置本身違規」。這跟上面三個「必須靠測試才知道」剛好相反,而且更好:

測試型:  last_verified_at 過期 → 告警「你不知道它還行不行」
可算型:  Isc(config) > Icw    → 告警「這個配置是違規的」,而且應該由互鎖從實體上封死

可算型的正確歸宿不是告警,是互鎖——連回 dc-03b 那套 R1–R7 守衛規則。能在實體上封死的,不要放進監控系統當成一條會被人 ack 掉的告警。

五、「保護等級降級」是狀態不是屬性——第三次

降級狀態 誰造成的 會不會忘記還原
dc-03b interlock_defeated 人為,為了維修
dc-07b erms_engaged 人為,為了人身安全 (且會讓選擇性整層失效)
dc-08 mode == STATIC_BYPASS / MAINTENANCE_BYPASS 半自動/人為

三者共同的形狀完全一致:設備看起來正常、遙測全綠、但保護等級已經降級,而且降級是人為操作造成的、有正當理由、且極易忘記還原。

dc-08 的骨架把它做得最好(operating_mode 是狀態機不是布林,告警依 mode 分岔,MAINTENANCE_BYPASS 降為 INFO 但仍要計時),dc-07b 只寫了 erms_engaged: booldc-03b 至今沒有實作。

收斂方向:一個共用的「降級狀態」實體 —— engaged_at / engaged_by / work_order_id / expected_restore_at / 逾時升級規則。三次都自己寫一遍,第四次(dc-10 STS 的手動旁路)還是會再寫一遍。

六、本週兩張卡都有「來源分歧」,而且都指向同一種話術

本軌跡第一次連續兩張卡出現來源分歧段,值得並排看:

分歧 分歧的本質
dc-08 ECO / eConversion 算不算不打折的保護?Schneider 說等同 Class 1,Vertiv 說是 marketing hype 且 VFD 保證不了 Class 1 兩邊可能在講不同的切換(高效模式→逆變器 vs 逆變器→旁路),毫秒數不可直接比
dc-09 VRLA 撐幾年?型錄 10 年 design life vs Schneider WP229 的 3–6 年 service life(TCO 取 4 年) 兩邊講的是不同的量:理想條件的設計值 vs 含溫度/循環/DoD 的實際值

共同點不是「廠商在騙人」,是「兩個數字量的不是同一件事,而行銷語言剛好讓它們長得一樣」。 這正是 W31 母題(數字必須帶著量測條件)在採購場景的實戰版——也是為什麼 dc-09 要求 design_life_yearsservice_life_est_years 必須是兩個欄位。塞進同一欄的系統,連「我們是不是被坑了」都問不出口。

這兩個分歧本身就是下週最好的兩個提問(見下面 Q3)。

自我測驗

Q1(事實回憶). 一面 3200 A 低壓盤,某饋出迴路裝 400 A MCCB、Inc = 355 A、該區段實測 RDF = 0.8。這條迴路的設計連續電流 Ib = 300 A。過得了嗎?如果改用 100% 額定的 LVPCB 呢?

答案

過不了,而且換 LVPCB 也救不了。

候選 1(元件)  :400 × 0.8(MCCB 的 80% 連續負載規則)× 1.0(35°C)= 320 A
候選 2(群組)  :Ing = Inc × RDF = 355 × 0.8                        = 284 A
max_continuous  = min(320, 284) = 284 A ← 綁死的是「群組」
Ib = 300 > 284  → violation

換成 LVPCB(100% 額定)後候選 1 變成 400 A,但 min(400, 284) 還是 284——綁死的來源根本不是元件。這就是為什麼回傳值必須是 (284.0, "assembly_ing") 而不是 284.0只有帶著來源鍵,你才知道那筆升級的錢是白花的。

RDF 提到 1.0(要求廠商逐路 Ib 做溫升驗證)才是有效的介入:min(320, 355) = (320.0, "device"),綁死的來源換人了。

再加一個容易忽略的方向——環境溫度會反超成為瓶頸ambient_c = 45rdf = 1.0 時,320 × 0.95 × 0.95 = (288.8, "device"),仍然擋不住 300 A。機電空間夏天沒做降額,跟 RDF 沒寫,是兩個獨立的坑。

對你的模型Circuit 需要 in_a / inc_a / ing_a 三個電流欄位加上所屬 Section.rdf,並加 CHECK in_a >= inc_a >= ing_aAssembly → Section → Circuit 三層不能扁平化,因為 RDF 與相互加熱是區段層級的性質——少了 SectionΣ(Inc × RDF) ≤ InA 這條唯一的容量真相根本寫不出來。

Q2(事實回憶). 廠商報「這組電池滿載 8 分鐘」。你的負載只有一半,能撐 16 分鐘嗎?

答案

不是 16,是 19.0 分鐘——比直覺多,而且方向是反的。

Peukert:t₂ = t₁ × (I₁/I₂)^k,VRLA 的 k ≈ 1.2–1.3,取 1.25
t = 8 × 2^1.25 = 8 × 2.378 = 19.0 min

放得慢,電池的有效容量變大。很多人記得「電池不是線性的」,但把方向記反了——以為打折,其實是紅利。真正的打折在別的地方:

打折 係數 8 分鐘變成
老化(IEEE 485 aging factor 1.25 = 1/0.8) ×0.8 EOL 時全部再砍兩成
溫度(低溫內阻上升) 再少幾 %
負載率(Peukert) 視方向 半載 19.0、90% 載 9.1

所以同一組電池有四個都「正確」的 runtime:規格 8 / 現況(67.5% 載)13.1 / N−1 後(90% 載)9.1 / N−1 且 EOL 7.3 分鐘最大與最小差 1.8 倍,而儀表板通常顯示的是那個 8。

對你的模型:容量必須存曲線discharge_rating: [(minutes, w_per_cell)])不是純量,runtime 是查表+插值+Peukert+老化的衍生值。而且插值不准外推w_per_cell_at(60) 應該 raise)——外推放電曲線是實務大忌,表格外面的地方廠商沒測過。

Q3(應用). 一面 LV 盤標 Icw = 50 kA / 1 s,接在 2000 kVA / 380 V / Z = 6% 的變壓器下游。單台運轉時剛好過關。什麼情況下這面盤會超過額定?這件事需要等到故障才知道嗎?

答案

不需要等,事前純算就知道;而且正確的處置不是告警,是互鎖。

單台:Isc = 2,000,000 / (√3 × 380 × 0.06) = 2,000,000 / 39.49 ≈ 50.6 kA
      50.6 kA vs Icw 50 kA → 勉強過(本身已經沒有餘裕)

tie 閉合、兩台變壓器並聯:≈ 101.2 kA
      101.2 > 50 → 這面盤在該配置下「本身就是違規的」

冗餘在這一刻變成負債:平常 tie 閉合是為了維護時不斷電(dc-03b 的 concurrent maintainability),但它同時讓可用故障電流翻倍。你為了提高可用度做的事,讓保護裝置失去承受能力。

對你的模型available_fault_ka 不是常數,是系統配置的函數。要有 SystemConfiguration 維度(tie 位置、幾台電源並聯),耐受檢查跑所有配置取 max

check(["tie_open"], 50.6)                    → []
check(["tie_open", "tie_closed(2)"], 50.6)   → ["tie_closed(2)"]

這產生一條事前可算、不必等故障的告警——但更好的歸宿是把它交給 dc-03b 那套 R1–R7 守衛規則,用三選二互鎖從實體上封死該配置。能在硬體上封死的,不要放進監控系統當成一條會被人 ack 掉的告警。

第二層追問:若這面盤標的不是 Icw 而是 Icc(條件式短路耐受,例如 65 kA 但綁定上游 SCPD 型號 XYZ-1),現場換成 ABC-9 之後這個 65 kA 還算數嗎? 不算。而且模型必須回 ("UNKNOWN", 原因) 而非 fallback 到 65——條件式額定是一對設備共同擁有的屬性Icc 這一列必須外鍵指向上游 SCPD 的型號,否則換一顆斷路器不會有任何機制讓那筆評等失效。

Q4(應用). 4 台 500 kW UPS 組成 N+1(n_required = 3),負載 1350 kW。監控畫面顯示每台 67.5%、一切正常。掉一台之後,電池還撐得住發電機起動嗎?把數字算完,然後說出這條規則為什麼不能掛在任何一台設備上。

答案

算得出來,也過得了關——但這個「過關」不是設計出來的,是碰巧的。

容量:capacity_kw    = 4 × 500 = 2000 kW
      n_minus_1_kw   = n_required × 最小成員 usable_kw = 3 × 500 = 1500 kW
      load_pct       = 1350 / 1500 = 90.0%          ← 不是 67.5%
      per_unit_now   = 1350 / 4 / 500 = 67.5%       ← 畫面上顯示的那個
      per_unit_after_fail = 1350 / 3 / 500 = 90.0%

電池:67.5% 載 → 8 × (1/0.675)^1.25 = 13.1 min
      90.0% 載 → 8 × (1/0.900)^1.25 =  9.1 min
      EOL 再 ×0.8                    =  7.3 min = 438 s

對照 W32 的發電機時序:
      開放轉換總時序 11.09 s
      最壞情況(三次 crank 到 overcrank lockout)78 s
      438 / 78 = 5.6 倍餘裕 → 過關

三個要點:

  1. 90.0% 才是有用的數字,67.5% 是安慰劑。 群組負載率的分母必須是 n_required × 最小成員容量,因為 N+1 的意義就是「掉一台之後還要撐住」。用 capacity_kw 當分母的系統,會在容量真正見底的前一刻都顯示綠燈。順帶注意 load_kw = 1200load_pct 恰為 80.0,而告警規則是 > 80 ——邊界值不觸發,這是驗收表刻意埋的一行。

  2. 餘裕是運氣不是設計。 438 s vs 78 s 看起來很寬,但沒有人算過這條式子。三個獨立變化都會吃掉它:電池過了 EOL 沒換(dc-09 演算 2:32 °C 環境下 10 年設計壽命實際只有 5.8 年)、負載長高、或發電機起動時間隨電池老化變長(W32 的 crank_time 漂移)。三者都在往同一個方向走,而且互相獨立。

  3. 這條規則跨三個實體,所以不能掛在任何一台設備上。

RedundancyGroup.n_required  ─┐
BatteryBank.discharge_rating ├→ 「N−1 時 EOL runtime < 起動時間 × 安全係數」→ CRITICAL
Genset.start_sequence        ─┘

對你的模型:告警規則本身必須是一等公民(有 id、有輸入實體清單、有版本),而不是散在各個設備型別的 method 裡。這是 dc-05c 「規則必須是資料列不是 enum 值」的第二次出現,只是這次驅動它的不是合規制度,是物理。

Q5(建模判斷). 本週五張卡都在算 min / max,其中三個地方回傳裸數字會直接害到人。哪三個?該用什麼統一形狀取代?

答案

三個地方:

位置 現在回傳 丟掉了什麼 代價
dc-08 usable_kw() 500.0 綁死的是 rating_kw 還是 rating_kva × pf 想擴容時不知道該換機還是改功因,可能買錯
dc-06 concurrently_maintainable_hours() 16.40 是哪個元件被移走造成的? 不知道該去修哪一顆泵——這正是這個數字唯一的用途
dc-09 runtime_min() 7.3 是哪一層打折造成的(負載率/老化/溫度)? 分不清「該換電池」還是「該降負載」還是「該修空調」

dc-07max_continuous() 是唯一做對的(回 (284.0, "assembly_ing")),dc-07beffective_withstand_ka() 做對了一半(有理由字串,但把 "UNKNOWN" 塞進 float 的位置)。

統一形狀——一個帶出處的量,同時解決「誰綁死的」與「這個值可能不存在」:

from dataclasses import dataclass
from enum import Enum

class Unknown(Enum):
    UNKNOWN = "unknown"          # 唯一的 sentinel,不要用字串

@dataclass(frozen=True)
class Bound:
    value: float | Unknown
    binding_source: str          # "assembly_ing" / "pump-1_removed" / "aging" / ...
    reason: str = ""             # UNKNOWN 時必填
    candidates: dict[str, float] | None = None   # 全部候選,除錯與「放寬要花多少錢」用

    def __float__(self):         # 誘惑最大的一行,刻意不提供
        raise TypeError("Bound 不可隱式轉 float——請顯式取 .value 並處理 UNKNOWN")

三個設計決定值得說清楚:

  1. UNKNOWN 必須是型別層級的東西,不是 "UNKNOWN" 字串也不是 None dc-07verify_ina()rdf is None 時要回 UNKNOWN 而非 True——未驗證不等於通過。台灣兩套盤體標準並行,代表模型必須能誠實表達「這面盤沒有這個數字」,而不是塞 0、塞 in_a、或塞一個型錄猜來的值。用字串當 sentinel,早晚有人 float() 它。

  2. 拒絕隱式轉 float 是刻意的摩擦。 只要能隱式轉,呼叫端就會退化回裸數字,三個月後又是一堆 if x > y

  3. candidates 不是除錯用的裝飾。 Q1 那四行對照表就是從它印出來的——「換 100% 額定斷路器上限完全沒動」這件事只有保留全部候選才看得見,而它是一筆採購決策。

一句話總結本週的建模躍遷:W31 教你「一個欄位裝不下一個事實」,W32 教你「有些事實不住在欄位裡」,W33 教你「有些答案不是一個數,是一個數加上它的來歷」。

間隔複習

本週正式排程生效——這是本軌跡第一次真的做間隔重複。

週次 抽兩週前 抽四週前
W33(本週) W31 的卡(dc-01 ~ dc-03b)✅ 尚無(W29 無卡片)
W34 W32 的卡(dc-04 ~ dc-05c 尚無(W30 無卡片)
W35 W33 的卡(dc-06 ~ dc-09 W31 的卡(第二次)
W36 W34 W32 的卡(第二次)

抽中的是 dc-02 變壓器(W31,2026-07-29)。挑它的理由不是隨機:它在 W31 留下的一個結論,本週才第一次真的兌現。

複習題(W31 dc-02 × 本週 dc-07b). dc-02impedance_pct 是「跨設備約束的來源,不是裝飾欄位」。這句話在本週具體長成了什麼?如果有人把變壓器從 6% 阻抗換成 5%,你的系統該有什麼反應?

答案

兌現的方式是 Q3 那條式子——dc-07b 演算 1 整段就是 dc-02 那句話的可執行版本:

Isc = kVA / (√3 × V × Z%)
2000 kVA / 380 V / 6%  → 50.6 kA
2000 kVA / 380 V / 5%  → 60.8 kA   ← 阻抗變小,故障電流變大

dc-02 的欄位,決定了 dc-07 那面盤的合法性。 這是全模型第一次「A 的欄位決定 B 的合法性」,W31 當時只能寫成一句叮嚀,本週才有第二個設備可以真的接上去。

換成 5% 阻抗後,系統該有的反應(三層,缺一不可):

  1. 立刻重算所有下游盤的耐受檢查,且要跑遍所有 SystemConfiguration:單台 60.8 kA 已經超過 Icw = 50 kA——這面盤在單台運轉下就違規了,不必等 tie 閉合。
  2. 把所有 Icc(條件式額定)標記為 stale,因為條件式額定綁定的是特定上游 SCPD 與特定的預期故障電流。
  3. 把所有 SelectivityPair 標記為 invalidated_by 這次變更——選擇性是在特定故障電流下驗證的,partial(limit_ka=35) 在 60.8 kA 下當然也不成立。

反過來看更值得記:阻抗的變壓器壓降小、效率看起來好,採購時容易被當成優點;但它同時把下游的短路電流頂上去,可能讓整面盤與所有斷路器規格失效。「更好的變壓器」讓下游變違規——這種反直覺的耦合,正是為什麼跨設備約束必須是可執行的驗證規則,而不是留在單線圖的註解裡。

對你的模型:需要一條「變更影響分析」的路徑——改一個欄位,能查出所有依賴它的推導值與驗證結論。這比告警更早一步:它作用在變更審查的當下,而不是事故的當下。

四週前(W29):尚無。 本軌跡起始於 2026-07-28,W29 無卡片。W35 起雙軌全開(同時抽兩週前與四週前),屆時每份週報會多兩題。

本週的 model code 連貫性檢視

先結算舊帳:

來源 項目 狀態
W31 #2 邊存在兩端 ⚠️ 仍未收斂,本週 SelectivityPair 讓它變成真正的痛點
W31 #3 id 參照 vs 物件內含 ⚠️ 仍未收斂,本週又製造了兩種新用法(見下面 #3)
W32 #4 DeviceRegistry 缺席 🔴 W32 說「必須在 dc-07dc-08 之前」——期限已過,節點數如預期翻倍
W32 #7 Measured 值物件 ⚠️ 本週出現第四、五個實例(w_per_cell_at 的量測條件、ambient_c_monthly_avg

本週新增六處。與前兩週不同的是:本週沒有任何一處會產生錯誤數字——五張卡的驗收表彼此對得上,dc-09 甚至主動接回 dc-08 演算 3 的 67.5% → 90%,數字一致。問題全部是結構性的,代價會在 dc-10 ~ dc-16 那七張卡(節點數再翻一倍)一次付清。

🔴 1. Assembly 這個名字現在指向兩個不同的類別

dc-07 定義 Assembly(id, ina_a, verified_to, sections)dc-07b 開頭寫「接續 dc-07Assembly / Section / Circuit 往上疊保護層」,然後重新定義了一個 Assembly(id, ratings, upstream_scpd_model)——欄位完全不重疊。

兩張卡是同一天前後拆出來的,這個碰撞幾乎是拆卡的必然副作用,但它是本週唯一一個「兩份 code 直接無法合併」的地方

收斂建議:一個 Assembly,兩組關注點分開掛:

@dataclass
class Assembly:
    id: str
    ina_a: float
    verified_to: Verified
    sections: list[Section] = field(default_factory=list)
    # 保護層(dc-07b)
    ratings: list[ShortCircuitRating] = field(default_factory=list)
    upstream_scpd_model: str | None = None

effective_withstand_ka()max_continuous() 是同一個物件的兩個問題——一個問「平常能過多少電」,一個問「出事時撐不撐得住」,本來就該在同一個型別上。

🔴 2. FuelTankBatteryString 是同構的,卻各寫了一套

兩者的結構幾乎逐項對應,但沒有任何共用抽象:

概念 dc-06 dc-09
銘牌容量 capacity_l discharge_rating 曲線
可用量(≠ 全部) usable_l()(DAY 扣 critical_low/BULK 扣沉積層) energy_kwh()0.8 DoD
抽取率 consumption_lph()(負載率的函數) runtime_min() 的 Peukert
續航 autonomy_hours() runtime_min()
劣化 燃油週轉/polishing service_life_years()
補充 DayTankCycle 補油循環 充電(尚未建模)

收斂建議:抽一個 EnergyStore 協定 —— usable() / depletion_rate(load) / autonomy()(一律推導,不存欄位)/ degradation_state()。好處具體有三個:

  1. 儀表板可以一視同仁畫「還能撐多久」,不必對每個型別寫一個分支;
  2. dc-20 儲冷槽(第二輪冷卻鏈)第三次撞上同一個形狀時,直接實作協定即可;
  3. 「續航是推導值不是欄位」這條規則只需寫一次,而不是每張卡重新叮嚀一遍——它已經被叮嚀三次了。

🟡 3. 實體關聯的風格,本週又多了兩種

W31 #3 至今未收斂,本週把樣本數推到五種:

dc-06 : FuelPath.source / dest      → 物件內含
dc-07 : Assembly.sections           → 物件內含
dc-07b: SelectivityPair.upstream    → 物件內含(Breaker)
dc-08 : RedundancyGroup.members     → 物件內含(Ups)
dc-09 : BatteryBank.ups_id          → 字串 id      ← 第一個回到 id 參照的

dc-09 是本週唯一用 id 的,而且它是對的——電池與 UPS 是跨卡引用,物件內含在這裡會製造循環(Ups 要知道自己的電池、BatteryBank 要知道自己的 UPS)。

收斂建議:訂一條可以照著做的規則,不要再每張卡臨場決定——

持久化層一律 id 參照;DeviceRegistry.resolve() 產生的執行期圖才用物件參照。跨卡(跨子系統)的關聯永遠是 id。

DeviceRegistry 已經被 W31、W32 各點名一次,本週是第三次。它的缺席現在有了具體代價SelectivityPair 需要邊有身分,BatteryBank.ups_id 是個沒有任何東西保證存在的裸字串,跟 W32 說的 TestEvent.initiating_ats_id 一模一樣。

🟡 4. 告警各自為政,形狀已經有三種

dc-06 : next_polish_due 到期        → 只在敘述裡,無 code
dc-07b: check() 回違規配置名稱清單   → list[str]
dc-08 : alarms()                    → list[tuple[str, str]](裸字串 severity)
dc-09 : 加分題的 CRITICAL 規則       → 只在敘述裡,無 code

收斂建議Alarm(severity: Severity, code: str, message: str, entity_ids: list[str], raised_at)。三個理由:

  • severity 用 Enum,否則 "CRITICAL""Critical" 遲早並存;
  • code 是穩定識別碼,message 才是人看的——沒有 code 就沒辦法做去重、抑制與 runbook 對應,而 dc-08 已經有一條「同一件事分 CRITICAL / INFO 兩種嚴重度」的規則(static vs maintenance bypass),正是最需要穩定 code 的形狀;
  • entity_ids複數,因為 Q4 那條規則同時讀三個實體。單數 entity_id 現在就已經裝不下。

🟡 5. 「上限取 min」的回傳形狀不一致(=自我測驗 Q5)

不重複展開,結論是 Bound 值物件。與前四點的差別:這一點改動最小、收益最立即(Q1 那張採購對照表直接從 candidates 印出來),建議第一個做。

🟡 6. 命名漂移:usable_* 這個前綴已經有四種說法

W32 #6 講的是單位後綴_s / _ms / _min),本週是動詞

dc-06 : usable_l()          ← 銘牌扣掉不可用
dc-07 : max_continuous()    ← 同一件事
dc-08 : usable_kw()         ← 同一件事
dc-09 : deliverable_kw()    ← 同一件事

四個名字、一個概念。收斂建議:一律 usable_<unit>()max_continuousdeliverable_kw 改名。同時把 W32 #6 的單位後綴一起補完——兩者都是純機械改動,合併成一次 rename 比分兩次便宜


優先序(本週沒有會產生錯誤數字的項目,所以排序依據換成「拖下去多貴」):

  1. #1 Assembly 碰撞 — 兩份 code 現在無法合併,且只影響兩張卡,趁小修掉。
  2. #5 Bound 值物件 — 改動最小、收益最立即,而且 dc-11 PDU 與 dc-14 rack PDU 必然重複「上限多來源」的形狀(dc-07 卡片自己已經預告了)。
  3. #3 DeviceRegistry — 已經欠三週。dc-10 ~ dc-16 有七張卡在排隊,每多寫一張,回頭改的成本就多一份
  4. 2 EnergyStore / #4 Alarm — 可以等,但要在第二輪冷卻鏈(dc-20 儲冷槽)之前做完。

  5. 6 命名 — 純機械,跟 W32 #6 合併成一次 rename。

本週該問 facility 的問題

五張卡共提出 15 個問題,去重後選 3 個下週真的拿去問人的。挑選標準沿用前兩週:「現在問還來得及改,晚問就固化了」。本週三題剛好落在三個不同的固化時點——採購(已快來不及)/ commissioning(唯一機會)/ 現在就能問(但沒人會主動說)

Q1 — 問 盤體供應商 / 機電設計單位(⚠️ 採購階段,最急)

「低壓盤的驗證標準是 CNS 13542 還是 CNS 15783-1(IEC 61439)?請給溫升測試報告編號。 若是前者,Inc / Ing / RDF 這三個數字從哪裡拿?規格書裡有沒有寫 RDF、寫多少?當初有沒有逐路提供 Ib,還是讓廠商套用假定負載係數? 各饋出斷路器是 80% 還是 100% 額定?機電空間夏季實測溫度多少,有沒有做過 35 °C 以上的降額? 另外短路側:每面盤標的是 Icw(無條件)還是 Icc(條件式)?若是 Icc,指定的上游 SCPD 是哪個型號,現場裝的是不是那一顆Icw 標 1 s 還是 3 s?」

為什麼是這題:一次拿到決定整面盤能不能用的全部參數,而且每一個都是採購當下固化、事後改不了的

  • 驗證標準決定欄位是否存在。 台灣兩套標準並行,若是 CNS 13542,Inc / Ing / RDF 在制度上根本不存在——你的容量檢查只能回 UNKNOWN。這不是資料缺漏,是「這面盤沒有這個數字」,模型必須誠實表達,不能塞 0 或塞 In
  • RDF 是唯一能事前介入的點。 Q1 的答案顯示 RDF = 0.81.0 少 11% 的可用電流,而事後任何遙測都看不見它。正確的做法是把一行字寫進採購規格:「本盤所有饋出迴路須以 RDF = 1.0 驗證,或依附表逐路 Ib 進行溫升驗證」。這行字現在寫進去是免費的,蓋完之後是換盤。
  • Icc 是一對設備的屬性。 若標的是 Icc 而現場上游換了型號,那個 65 kA 就該失效——但沒有任何機制會通知你。現在問,是為了知道要不要建那條外鍵。

Q2 — 問 控制系統承包商 / 電驛設定承包商(⚠️ commissioning 前,唯一機會)

「短路電流計算有沒有涵蓋 tie 閉合 + 兩台變壓器並聯?算出來幾 kA?有沒有三選二互鎖從實體上禁止該配置?電動機反饋算進去了嗎? 有沒有做過 arc flash study(IEEE 1584-2018)?主斷路器有 ERMS 或 ZSI 嗎?ZSI 做過注入電流的功能測試嗎(不是導通測試)?ERMS 有沒有就地狀態指示、有沒有進事件記錄? UPS 側:靜態旁路的過載額定跟上游斷路器協調過了嗎? UPS 供不出跳脫電流的情境(dc-08 演算 5)下,該跳的是下游分路,不是 UPS 旁路進線。」

為什麼是這題,而且為什麼期限最硬

  • tie 閉合的違規是純算的(Q3:50.6 → 101.2 kA),不必等故障。但互鎖必須在 commissioning 前設定進去——蓋完之後補裝要停整面盤。
  • ZSI 的正確性在正常運轉時完全不可觀測,唯一的驗證機會是 commissioning 的注入電流測試。導通測試(量線通不通)與功能測試(注入電流看限制訊號有沒有發出去)完全不是同一件事,而承包商常只做前者。沒有這筆測試記錄,last_zsi_functional_test_at 這個欄位從第一天起就是空的。
  • ERMS 與選擇性正面衝突:開了 ERMS,清除時間從 0.3 s 降到 0.05 s(入射能量 40 → 6 cal/cm²),但選擇性整層失效——一個下游故障會跳掉整面盤。這個取捨必須有人做決定並留下記錄,不能讓它變成「維修人員自己判斷、事後忘記還原」。

Q3 — 問 UPS 供應商 / 電池供應商 / 維運

旁路輸入來自哪一面盤?跟整流器輸入同一面嗎? 若是,這台 UPS 的『兩個輸入』在故障域上其實是一個。 有沒有啟用 ECO / eConversion?誰有權切、有沒有進事件記錄? 另外——貴司報的切換毫秒數,是哪兩個模式之間的?(高效模式→逆變器,還是逆變器→旁路?) 電池:廠商報的 runtime 是幾分鐘率、在幾度、算不算老化係數(IEEE 485 的 1.25)? 電池間全年溫度曲線有紀錄嗎、月均幾度? 還有——你報的是 design life 還是 service life?請分開講。 最後:電池室的樓板承重上限是多少?

為什麼是這題:本週兩個「來源分歧」都在這裡,而且兩個分歧都是問對問題就能當場化解的

  • ECO 模式:Schneider 說 eConversion 等同 Class 1,Vertiv 說傳統 ECO 是 marketing hype 且 VFD 保證不了 Class 1。兩邊很可能在講不同的切換,所以問法不是「ECO 安不安全」(會得到行銷答案),而是「你報的毫秒數是哪兩個模式之間的」。這句話問完,對方要嘛給你精確定義,要嘛答不出來——兩種結果都有用。
  • design life vs service life:型錄 10 年、Schneider WP229 說 service life 3–6 年、TCO 模型取 4 年。這不是誰錯,是兩個不同的量。問清楚才能解釋「為什麼型錄寫 10 年、我們四年就要換」,也才知道 design_life_yearsservice_life_est_years 各該填什麼。而 32 °C 環境下 10 年設計壽命實際只剩 5.8 年10 × 2^(−7/9))——溫度曲線是這個計算唯一的輸入,沒有它就永遠只能吵。
  • 旁路同源:這是稽核報表不是告警——它不會變化,但會讓「兩條 feed」的冗餘顯示成綠燈。跟 W32 補考題(dc-01 兩路市電同變電所、dc-05 三台機共用油管)是同一個錯誤的第三次出現:用數量冒充獨立性。
  • 樓板承重:11,340 kg 對舊建物翻新是真實限制,也是鋰電最常被選中的理由。這題決定的是選型,晚問就等於已經選好了。

這週沒選上、但別忘記的

  • 儲油槽幾座、各多少公升?移走其中一座之後,全站在設計負載下還剩幾小時?(要這個數字,不是總和——本週實例:31.78 h 的總量,逐一移除後只剩 16.40 h。)
  • 輸油泵與燃油控制盤的電源從哪來? 是不是全部吊在同一面盤或同一個 ATS 上?(共用一顆泵時續航從 16.40 h 掉到 1.03 h。)
  • 燃油多久採樣化驗、多久 polishing?上次報告在哪、我們的油幾歲? critical low 是接成警報還是引擎停機?(年週轉率僅 11.8%,換完一輪要 8.5 年,而柴油可用期只有 6–12 個月——這是一個沒有感測器會叫你的故障。
  • UPS 的 upsOutputSource(RFC 1628)分不出 static bypass 與 maintenance bypass(都是 4)。問廠商有沒有私有 OID 或 Modbus 暫存器能分辨——分不出來就等於「負載未受保護」這條 CRITICAL 告警會在計畫性維修時每次誤報,然後被人關掉。

下週

  • 佇列下一張:dc-09b 電池測試制度與消防合規(IEEE 1188 內阻/容量門檻、commissioning baseline、NFPA 855 與 UL 9540A、熱失控偵測)。它會第四次撞上「只有故障當下才會被用到的功能需要測試日期欄位」——在寫它之前把 PeriodicVerification 抽出來,讓 dc-09b 成為第一個直接複用的卡,而不是第四次重寫。
  • 連貫性檢視的 #1 與 #5 請在 dc-09b 之前修完(各約 20 分鐘),#3 DeviceRegistry 必須在 dc-10 之前——那之後有七張卡在排隊,欠三週的帳每拖一張就多一份。
  • dc-09b 之後是 dc-10 STS,它會第四次出現「保護等級降級是狀態不是屬性」(手動旁路)。第 5 節那個共用實體,最晚在那之前要有。
  • ⚠️ commissioning 隨時可能開始。 本週 Q2 已經把該在現場拿到的東西列清楚了——ZSI 注入電流功能測試、tie 閉合互鎖驗證、arc flash 標籤。加上 W32 的 Q3(ATS 五個計時器設定值 + NFPA 110 §7.13.4.1.4 分段時序記錄),這兩份清單合起來就是 commissioning 現場的驗收單,一次性且不可重現。