# P&ID 흐름 추적 · From/To 추출 알고리즘 사양서

> 출처: P10-EQP-BLOCK.dxf 협업 역설계로 검증된 규칙 통합 (2026-05-18).
> 참조 자산: `mcp-server/legend_symbols.json`(심볼 시그니처), `mcp-server/block_template_library.json`(블록 템플릿),
> `mcp-server/legend_probe.py`(프로브). 관련 메모리: feedback-trace-reporting-granularity,
> reference-lineno-leader-rule, project-legend-symbol-catalog.

---

## 0. 목적과 핵심 전제 (지배 규칙)

- **목적**: 도면에서 유체 흐름을 추적해 **랜드마크 간 From/To 연결그래프**와 **라인번호·출처**를 추출.
- **답변 단위**: "FCV-10101 어디야?" → "라인 P-10149-40A-F1A-n (출처 T-10100) 상, P-10101 흡입헤더·토출·F-10102A/B 거쳐 FIT-10101 하류". **수동밸브/체크/호스/레듀서 등 commodity는 절대 열거 안 함.**
- **정밀 카운트 아님**: 견적용 개수 검출이 목적이 아니라 연결·위상 파악이 목적.

### 노드 분류

| 분류 | 예 | 처리 |
|---|---|---|
| **랜드마크 노드** | 설비(P-,T-,E-,C-,D-,F-,SC-…), 컨트롤밸브/태그계기(FCV-,FIT-,LIC-…), 모터 | From/To 그래프의 정점 |
| **투명 통과 (commodity)** | 수동 볼/체크/글로브밸브, 레듀서, 플렉시블호스, 스트레이너, 플랜지 | 흐름 안 끊기게 **브리지만**, 노드 아님, 출력에 미열거 |
| **연결 매체** | 배관(pipings) | 엣지. line_number·출처 보유 |

---

## 1. 입력 자산

1. **설비 블록**: 태그명 INSERT (이름=태그). 위치 = `virtual_entities()` 월드 bbox (insert (0,0)+내부절대좌표 / 또는 실좌표 — 변환이 흡수).
2. **심볼 템플릿 라이브러리**: 명명 블록 정의의 정규화 시그니처(상대좌표·길이·각도·토폴로지, 원/호/SOLID 수). 회전·미러 **불변** 종류식별 + 월드기하 방향산출 2계층.
3. **raw 지오메트리**: modelspace LINE/LWPOLYLINE/ARC/CIRCLE/SOLID/HATCH — 실제 그려진 배관·commodity 심볼.
4. **OFFPAGE_CONNECTOR**: 방향성 블록(LINE 5~6) + **본체 bbox 내부 TEXT 2개**(출처설비/서비스, 라인번호/연속키).
5. **라인번호 TEXT + 지시선**: 직접배치 또는 leader(SOLID/선-구성 chevron 화살촉 + stem).

---

## 2. 알고리즘 단계

### Phase 1 — 랜드마크 노드 추출
- 모든 INSERT 순회. 이름이 태그패턴(`^[A-Z0-9]{1,4}-?\d{3,5}` 또는 명시 설비명)이면 **설비 노드**: tag, world bbox 중심, 카테고리(prefix 규칙).
- 태그계기/컨트롤밸브: 심볼(글로브밸브 등) + 인접 태그TEXT(FCV-/FIT-…) → **태그계기 노드**. 태그=인접 라벨, 위치=심볼 월드좌표.
- `-SAME`/중복 복제 = **동일 노드로 병합**(같은 물리설비).

### Phase 2 — 배관망 + commodity 투명 브리지
- raw LINE/LWPOLYLINE → 끝점 그래프(좌표 양자화). 세그먼트 = 잠재 배관.
- **commodity 심볼 인식 → 투명 통과**:
  - 소형 빈 CIRCLE r≈0.28~0.46 (+ bowtie 바/대각) = 볼밸브 → pass-through. (r 필터 하한 0.28 필수; faceted·무원 변형은 클러스터 패턴으로)
  - SOLID 삼각형 + 단일사선 = 체크밸브(방향성) → pass-through
  - 닫힌 LWPOLY 채움 = 글로브 / ARC 돔+stem = 컨트롤·다이어프램 (단 **태그 있으면 랜드마크**, 없으면 commodity)
  - 레듀서/플렉시블호스/스트레이너/플랜지 = pass-through
  - 회전·미러는 `virtual_entities()` 월드기하로 흡수. **블록명은 힌트, 방향근거 금지.**
- **gap-통과 브리지**: 배관 끊김 → 진행 흐름벡터 방향(콘)으로 lookahead, gap에 commodity 심볼 있으면 그 너머 공선 세그먼트로 연결 계속.

### Phase 3 — 위상 인식 (흐름방향·분기·합류·루프)
- **흐름방향 = 의미적**: 발원지(OFFPAGE/탱크/펌프)에서 누적 흐름벡터. **DXF 선 정점순서 무시, 역행 금지, 방문엣지 가드(진동 방지).**
- **FLOW_DIRECTION 화살표**: 만나면 그 위치 흐름방향을 **월드기하 apex→tail 벡터로 확정**(이름 아님). 의미적 추정보다 우선.
- **엘보**: 직각 굽힘은 끝점공유로 따라감(수평→수직 등).
- **TEE = 분기/합류**: 한 점에 3선 이상. 분기 시 본류+지선 분할.
- **재순환(킥백) 루프**: 한 지선이 이미 방문한 흡입측으로 복귀 = **정상 사이클**. 무한루프 아님 → `recirculation` 엣지로 표기, 그 지선 종료.
- **헤더/매니폴드**: 수평선에 수직 분기 다수 → 합류 매니폴드. 좌단 블라인드플랜지=막힘(흐름 반대편).
- **병렬 트레인**: 동일 x/y의 평행 분기→합류 (예 F-10102A/B 수직병렬).
- **OFFPAGE 짝**: 본체 내부 라인번호/연속키 TEXT 추출. **동일 연속키 가진 커넥터끼리 페어** → 라인 점프(시트/위치). 방향 = 커넥터 월드기하.

### Phase 4 — 라인번호 ↔ 배관 귀속
- **케이스 A 직접배치**: 텍스트가 배관 복도 근접(수평배관 위 평행, **또는 수직배관 옆 span중간 가로배치**). 박스 내 최근접 세그먼트.
- **케이스 B 지시선**: 화살촉(SOLID **또는** 1.0바+0.66대각 선-구성 chevron) → **tip 좌표가 닿는 배관 = 대상.** **tip↔배관 매칭 허용오차 극소(sub-0.5u, ≈0)** — 밀집부 오매칭 방지. stem(LINE/LWPOLY/SPLINE/LEADER 무관) → landing → 텍스트 역추적도 성립.
- **모호 분해**: 근접 평행 수직 2개 등 기하로 불가 시 → **흐름-토폴로지 컨텍스트**(어느 배관이 추적경로/설비에 실제 연결)로 결정.

### Phase 5 — 랜드마크 From/To 환원
- 투명 commodity 체인 **붕괴(collapse)**: 랜드마크→(commodity*)→랜드마크 를 **랜드마크↔랜드마크 단일 엣지**로.
- 엣지 = `from_tag`(상류 랜드마크), `to_tag`(하류 랜드마크). **From/To에 배관번호 안 넣음.**
- 상/하류 = 흐름방향으로 결정. 미결정 시 무방향(from/to 임의·플래그).
- 배관번호·출처 = **pipings 레코드/엣지 속성**(권위). 랜드마크 위치질의는 랜드마크→인접배관→line_number+OFFPAGE출처 **조인 조립**.

---

## 3. 출력 스키마 (`pid_equipment` 정합)

| 카테고리 | from_tag / to_tag | line_number |
|---|---|---|
| equipment / control_valve / motor / instrument | 인접 **랜드마크 태그** ↔ 랜드마크 태그 | (옵션) 얹힌 라인 단일 참조, From/To 불포함 |
| pipings | 라인 양끝 랜드마크 / OFFPAGE 짝 | 그 배관 라인번호 (권위) |

- `recirculation`/`bypass` 엣지는 타입 플래그로 구분(정상 위상, 누락·추측 금지).
- 재실행 결정성: 기존 From/To 초기화 후 재계산하되 **수동 import 잠금(ConnectionLocked) 보존**.

---

## 4. 핵심 불변식 (검증된 함정 회피)

1. 흐름방향은 의미적(발원지 기준), 선 정점순서·블록명 불신. 방문엣지 가드 필수(self-oscillation 방지).
2. 회전/미러 → 종류는 불변시그니처, 방향은 `virtual_entities()` 월드기하. 동일형상 다른이름(FLOW_DIR 3종 등) 주의.
3. 볼밸브 r≈0.28~0.46 (소형 지배, 도면 다수). 펌프 케이싱 r≈2~3 (≥4.5 아님).
4. 재순환선 = 정상 사이클. 끊지 말고 루프로 표기.
5. 화살촉 tip↔배관 = sub-0.5u 준-정확. 화살촉은 SOLID 또는 선-구성 chevron.
6. From/To = 랜드마크↔랜드마크. 배관번호는 엣지/pipings 속성. commodity 미열거(답변 단위 전제).

---

## 5. 정직한 한계 / 미검증

- raw 패턴매칭은 퍼지: 재현율 100% 보장 못 함(회전·스케일·작도변형·배관병합).
- 밀집부(토출·HATCH다수)는 좁은 허용오차 없이는 오탐.
- 지시선 변형: SOLID·선-구성 chevron 검증됨. **SPLINE 곡선 stem 미검증**.
- 직접배치 평행수직 모호 → 토폴로지 컨텍스트 의존(자동화 시 흐름추적 선행 필요).
- OFFPAGE 짝 매칭은 연속키 텍스트 정확추출 전제(밀집·회전 텍스트 노이즈 가능).

---

## 6. 구현 상태 · 다음 세션 인계 (2026-05-18 2차 세션 종료 시점)

### 산출물 (확정·보존)
- 사양서: `mcp-server/pid_trace_algorithm.md` (이 문서)
- 구현체: `mcp-server/pid_tracer.py` — Phase 1·1b·2·3·4·5 구현 (미드스트림 라우팅 미완)
- 카탈로그: `mcp-server/legend_symbols.json`, `mcp-server/block_template_library.json`, `mcp-server/legend_probe.py`
- 출력: `mcp-server/storage/P10-EQP-BLOCK_connections.json` (C# 소비 포맷 호환)
- C# 소비경로: `AnalyzeConnectionsAsync` 이미 `<prefix>_connections.json` edges 소비 (이전 세션 구현)

### 정답 레퍼런스 (협업 검증된 ground truth)
- P-10101 시스템 전체 위상 = **흡입 T-10100/T-10101 합류 + 킥백 재순환 루프 +
  토출 → 병렬필터 F-10102A/B → MASS_FLOW_METER(FIT-10101) → FCV(+바이패스) → E-10103**
- 주의: 핸드오프 기억상 "FCV-10101" 이나 도면 실제 인접 버블은 **FCV-10116** (도면 권위)

### 현재 어디까지 (2026-05-18 2차 세션 (a) 보강)

**현재 트레이서 실측 출력:**
- 랜드마크 57, OFFPAGE 시드 1, 볼앵커 372, raw 14,764, 라인번호귀속 227
- `OFFPAGE T-10100` 시드: T-10100 노드만 (공급선 P-10101 미완주)
- `P-10101 discharge` 시드: **P-10101 → F-10102B → F-10102A → FIT-10101** (여기서 멈춤)

**완료·검증:**
1. ✅ **OFFPAGE 발원 소스 시드**: OFFPAGE_CONNECTOR 4종 중 LEFT_TO_RIGHT 처리.
   apex = 본체 주축 두 극단 중 '뾰족한'(정점 1개) 쪽, 방향 = 뒷변중점→apex (월드기하
   권위). 본체 근접 TEXT 분류 → 출처태그 `T-10100` 발원 노드 등록 + 라인번호
   `P-10149-40A-F1A-n` 시드 부착. (공급선이 P-10101 까지 *완주*는 미달 — §아래)
2. ⬆️ **랜드마크 통과-후-재개**: `resume_nozzle` — 블록 실체범위(SOLID/원반경 포함
   `world_extent`) 밖, **안정적 시드 흐름축** 투영 최대 미방문 노즐로 재개. 결과:
   `P-10101 → F-10102B → F-10102A → FIT-10101` (이전: F-10102A 에서 멈춤).
3. ⬆️ **명명 심볼 = 태그계기 합성**: MASS_FLOW_METER/CONTROL_VALVE_GLOBE/
   FLOW_METER_VARIABLE-AREA 를 commodity 에서 제외, 인접 함수코드+번호 버블 TEXT 로
   태그 합성 (`FIT`+`10101`→**FIT-10101**, `FCV`+`10116`→**FCV-10116**). 블록명은
   종류 힌트, 태그는 인접 TEXT 권위.
4. ⬆️ **일반 전방 gap-브리지**: commodity/작도 끊김을 흐름축 콘(dot>0.85+수직오프셋
   최소)으로 점프, 착지점에서 정상 conn 재전진(역행 억제).
5. ⬆️ **볼앵커 과포함 정밀화**: r 0.28~0.46 + 동반 단선(≤2.6) ≥2 조건 → 560→372.
6. 🧩 **Phase 4·5 실코드화**: 라인번호 귀속(SOLID tip sub-0.5u / 직접배치, 227건),
   재순환 엣지·OFFPAGE 라인번호 엣지 속성.

**남은 미완 (다음 세션 계속 (a)2/(a)3):**
- **밀집 미드스트림 라우팅**: `FIT-10101 → FCV-10116 → E-10103` 장거리 런이
  계기 테이크오프·샘플 분기 fitting 기하에 갇힘. 계기-테이크오프 거부 +
  헤더/TEE 추종 필요.
- **공급선 P-10101 완주**: OFFPAGE 시드가 흡입헤더까지 라우팅 미완(하향 드리프트).
- TEE 분기 본류/지선 분할·OFFPAGE 동일연속키 짝 매칭 = 미구현(골격).

**(b) C# 연동·DB 반영 (착수 전):**
1. 트레이서 의미 엣지 산출 후 `AnalyzeConnectionsAsync` 를 P10-EQP-BLOCK 출력 연결
2. From/To = 랜드마크↔랜드마크만(배관번호 미내장, pipings/엣지 속성) — §3 스키마대로 DB 반영
3. 수동 import 잠금(ConnectionLocked) 보존 회귀 확인

검증 진입점(SEEDS)·정답 위상은 `pid_tracer.py` 주석 및 본 문서 §2·메모리에 보존됨.
