대형 파일 집중 변환: - SatelliteRequest: 134→66 (hex 색상 일괄 변환) - IncidentsView: 141→90, MediaModal: 97→38 - HNSScenarioView: 78→38, HNSView: 49→31 - LoginPage, MapView, PredictionInputSection 등 중소 파일 8개 변환 패턴: hex 색상→text-[#hex], CSS 변수→Tailwind 유틸리티, flex/grid/padding/fontSize/fontWeight/overflow 등 정적 속성 className 이동 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
668 lines
48 KiB
TypeScript
Executable File
668 lines
48 KiB
TypeScript
Executable File
import { useState } from 'react'
|
||
|
||
const boomTabs = [
|
||
{ id: 0, label: '🌐 개요' },
|
||
{ id: 1, label: '📐 배치 이론' },
|
||
{ id: 2, label: '⚙️ 최적화 알고리즘' },
|
||
{ id: 3, label: '🌊 유체역학 모델' },
|
||
{ id: 4, label: '🗺️ 현장 적용' },
|
||
{ id: 5, label: '📚 참고문헌' },
|
||
]
|
||
|
||
export function BoomDeploymentTheoryView() {
|
||
const [activePanel, setActivePanel] = useState(0)
|
||
|
||
const handleExportPDF = () => {
|
||
window.print()
|
||
}
|
||
|
||
return (
|
||
<div className="flex flex-col h-full overflow-hidden bg-bg-0">
|
||
<div className="flex-1 overflow-y-auto scrollbar-thin p-5">
|
||
|
||
{/* 헤더 */}
|
||
<div className="flex items-center justify-between mb-5">
|
||
<div className="flex items-center gap-3">
|
||
<div className="w-[38px] h-[38px] rounded-[9px] flex items-center justify-center text-lg"
|
||
style={{ background: 'linear-gradient(135deg,rgba(249,115,22,.2),rgba(234,179,8,.15))', border: '1px solid rgba(249,115,22,.3)' }}>
|
||
🛡️
|
||
</div>
|
||
<div>
|
||
<div className="text-[15px] font-bold">오일펜스 배치 최적화 알고리즘 이론</div>
|
||
<div className="text-[10px] mt-0.5 text-text-3">Oil Boom Deployment Optimization · 유출유 확산예측 연동 · 방제효율 최대화</div>
|
||
</div>
|
||
</div>
|
||
<button onClick={handleExportPDF}
|
||
className="px-3.5 py-1.5 rounded-md text-[10px] font-semibold cursor-pointer text-primary-blue"
|
||
style={{ border: '1px solid rgba(59,130,246,.3)', background: 'rgba(59,130,246,.08)' }}>
|
||
📤 PDF 내보내기
|
||
</button>
|
||
</div>
|
||
|
||
{/* 내부 네비게이션 */}
|
||
<div className="mb-5">
|
||
<div className="flex gap-[3px] rounded-lg p-1 bg-bg-3 border border-border">
|
||
{boomTabs.map(tab => (
|
||
<button
|
||
key={tab.id}
|
||
onClick={() => setActivePanel(tab.id)}
|
||
className="flex-1 py-2 px-2 text-[12px] font-semibold rounded-md transition-all"
|
||
style={{
|
||
background: activePanel === tab.id ? 'rgba(249,115,22,.15)' : undefined,
|
||
color: activePanel === tab.id ? 'var(--orange)' : 'var(--t3)',
|
||
border: activePanel === tab.id ? '1px solid rgba(249,115,22,.3)' : '1px solid var(--bd)',
|
||
}}
|
||
>
|
||
{tab.label}
|
||
</button>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* ═══ PANEL 0: 개요 ═══ */}
|
||
{activePanel === 0 && <OverviewPanel />}
|
||
{activePanel === 1 && <DeploymentTheoryPanel />}
|
||
{activePanel === 2 && <OptimizationPanel />}
|
||
{activePanel === 3 && <FluidDynamicsPanel />}
|
||
{activePanel === 4 && <FieldApplicationPanel />}
|
||
{activePanel === 5 && <ReferencesPanel />}
|
||
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
/* ──────────── PANEL 0: 개요 ──────────── */
|
||
function OverviewPanel() {
|
||
return (
|
||
<>
|
||
{/* 인트로 카드 */}
|
||
<div className="rounded-xl p-5 mb-4 relative overflow-hidden"
|
||
style={{ background: 'linear-gradient(135deg,rgba(249,115,22,.06),rgba(234,179,8,.04))', border: '1px solid rgba(249,115,22,.2)' }}>
|
||
<div className="absolute top-0 left-0 right-0 h-[3px]" style={{ background: 'linear-gradient(90deg,var(--orange),var(--yellow),var(--cyan))' }} />
|
||
<div className="grid grid-cols-2 gap-5">
|
||
<div>
|
||
<div className="text-[13px] font-bold mb-2">🛡️ 오일펜스 배치 최적화란?</div>
|
||
<div className="text-[11px] leading-[1.8] text-text-2">
|
||
해양 유류오염 발생 시 <b className="text-status-orange">유출유 확산 예측 결과</b>와 실시간 해양환경(조류·풍향·파고)을 연동하여, 제한된 방제자원(오일펜스 길이·방제정 수)으로 <b className="text-primary-cyan">오염 확산 차단 효율을 최대화</b>하는 최적 배치 지점·형태·순서를 자동 산출하는 수치 알고리즘 체계입니다.
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div className="text-[13px] font-bold mb-2">🎯 WING 최적화 목표</div>
|
||
<div className="flex flex-col gap-[5px] text-[11px] text-text-2">
|
||
{[
|
||
{ num: '①', color: 'var(--orange)', bg: 'rgba(249,115,22,.05)', bd: 'rgba(249,115,22,.12)', text: '차단 면적 최대화 — 예측 유출유 확산 경계와 오일펜스 교차 면적 극대화' },
|
||
{ num: '②', color: 'var(--cyan)', bg: 'rgba(6,182,212,.05)', bd: 'rgba(6,182,212,.12)', text: '도달시간 최소화 — 유출유 해안·ESI 민감구역 도달 전 선제적 차단선 구축' },
|
||
{ num: '③', color: 'var(--green)', bg: 'rgba(34,197,94,.05)', bd: 'rgba(34,197,94,.12)', text: '자원 효율 최적화 — 가용 오일펜스 길이·방제정 수·이동시간 제약 충족' },
|
||
{ num: '④', color: 'var(--purple)', bg: 'rgba(168,85,247,.05)', bd: 'rgba(168,85,247,.12)', text: '실패 안전성 확보 — 조류 초과 시 오일펜스 이탈 방지 방향각 자동 보정' },
|
||
].map((item, i) => (
|
||
<div key={i} className="px-2.5 py-1.5 rounded-md" style={{ background: item.bg, border: `1px solid ${item.bd}` }}>
|
||
<span className="font-bold" style={{ color: item.color }}>{item.num}</span> <b>{item.text}</b>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 전체 흐름도 */}
|
||
<div className="rounded-md p-4 mb-4 bg-bg-3 border border-border">
|
||
<div className="text-xs font-bold mb-3.5">⚙️ WING 오일펜스 배치 최적화 전체 흐름</div>
|
||
<div className="flex items-center justify-center gap-0 flex-nowrap overflow-x-auto py-2">
|
||
{[
|
||
{ icon: '🌊', label: '확산예측', sub: 'KOSPS/POSEIDON\nOpenDrift', color: 'var(--orange)', bg: 'rgba(249,115,22,.08)', bd: 'rgba(249,115,22,.2)' },
|
||
{ icon: '📡', label: '환경입력', sub: '조류·풍향\n파고·수심', color: 'var(--cyan)', bg: 'rgba(6,182,212,.08)', bd: 'rgba(6,182,212,.2)' },
|
||
{ icon: '🗺️', label: '차단선 후보', sub: '격자탐색\n후보지점 생성', color: 'var(--yellow)', bg: 'rgba(234,179,8,.08)', bd: 'rgba(234,179,8,.2)' },
|
||
{ icon: '⚙️', label: '최적화 엔진', sub: '다목적\n유전알고리즘', color: 'var(--purple)', bg: 'rgba(168,85,247,.08)', bd: 'rgba(168,85,247,.3)', bold: true },
|
||
{ icon: '✅', label: '배치안 출력', sub: '좌표·형태\n방향·순서', color: 'var(--green)', bg: 'rgba(34,197,94,.08)', bd: 'rgba(34,197,94,.2)' },
|
||
{ icon: '🗺️', label: '지도 표시', sub: 'ESI 중첩\n방제자원 연계', color: 'var(--blue)', bg: 'rgba(59,130,246,.08)', bd: 'rgba(59,130,246,.2)' },
|
||
].map((step, i) => (
|
||
<div key={i} className="flex items-center">
|
||
<div className="text-center min-w-[80px] rounded-lg px-3 py-2.5 text-[9px]" style={{
|
||
background: step.bg,
|
||
border: `${step.bold ? '2px' : '1px'} solid ${step.bd}`,
|
||
}}>
|
||
<div className="text-[15px] mb-1">{step.icon}</div>
|
||
<div className="font-bold" style={{ color: step.color }}>{step.label}</div>
|
||
<div className="text-text-3" style={{ whiteSpace: 'pre-line' }}>{step.sub}</div>
|
||
</div>
|
||
{i < 5 && <div className="px-1.5 text-text-3 text-[14px]">▶</div>}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* 오일펜스 종류 */}
|
||
<div className="grid grid-cols-3 gap-3 mb-4">
|
||
{[
|
||
{ icon: '⛽', title: '고형 오일펜스', color: 'var(--orange)', desc: '단단한 부체와 수중커튼으로 구성. 정적 배치. 항구·좁은 수로에 적합.', specs: ['내조류 한계: 0.5~1.0 knot', '높이: 30~60cm · 수중 30~60cm', '전개속도: 30~60m/hr'] },
|
||
{ icon: '🌊', title: '공기충전식 오일펜스', color: 'var(--blue)', desc: '공기로 부력 확보. 이동·보관 편리. 해상 광역 차단에 주로 사용.', specs: ['내조류 한계: 0.7~1.5 knot', '높이: 45~90cm · 수중 45~90cm', '전개속도: 100~300m/hr'] },
|
||
{ icon: '🔄', title: '자항식 오일펜스', color: 'var(--green)', desc: '방제정 예인 또는 자체 추진. U형·V형 동적 배치. 강조류 해역 적합.', specs: ['운용수심: 5m 이상', 'U형·V형·J형 동적 형태', '내조류: 조류각도 보정으로 극복'] },
|
||
].map((boom, i) => (
|
||
<div key={i} className="rounded-md p-3.5 bg-bg-3 border border-border" style={{ borderTop: `3px solid ${boom.color}` }}>
|
||
<div className="text-[11px] font-bold mb-2" style={{ color: boom.color }}>{boom.icon} {boom.title}</div>
|
||
<div className="text-[10px] mb-2 leading-[1.7] text-text-2">{boom.desc}</div>
|
||
<div className="flex flex-col gap-[3px] text-[9px] text-text-2">
|
||
{boom.specs.map((spec, j) => (
|
||
<div key={j} className="px-[7px] py-[3px] rounded-[3px] text-text-2" style={{ background: `${boom.color}11` }}>{spec}</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* 핵심 제약조건 */}
|
||
<div className="rounded-md p-3.5 bg-bg-3 border border-border">
|
||
<div className="text-[11px] font-bold mb-2.5 text-status-red">⚠️ 최적화 핵심 제약조건</div>
|
||
<div className="grid grid-cols-3 gap-2.5 text-[10px] text-text-2">
|
||
{[
|
||
{ icon: '🌊', title: '조류 제약', color: 'var(--red)', bg: 'rgba(239,68,68,.05)', bd: 'rgba(239,68,68,.15)', lines: ['조류속도 > 임계유속 시', '오일펜스 하단 통과 발생', 'U<0.7 knot 유지 필수', '임계각도 자동 계산 적용'] },
|
||
{ icon: '📏', title: '자원 제약', color: 'var(--yellow)', bg: 'rgba(234,179,8,.05)', bd: 'rgba(234,179,8,.15)', lines: ['가용 오일펜스 총 길이', '방제정 척수·이동시간', '앵커링 가능 수심 조건', '연결부 허용 장력'] },
|
||
{ icon: '⏱️', title: '시간 제약', color: 'var(--blue)', bg: 'rgba(59,130,246,.05)', bd: 'rgba(59,130,246,.15)', lines: ['유출유 도달 예측시간', '오일펜스 전개 소요시간', '방제정 현장 도착시간', '조석 변환 주기 고려'] },
|
||
].map((c, i) => (
|
||
<div key={i} className="p-2.5 rounded-[7px]" style={{ background: c.bg, border: `1px solid ${c.bd}` }}>
|
||
<div className="font-bold mb-[5px]" style={{ color: c.color }}>{c.icon} {c.title}</div>
|
||
<div className="leading-[1.7] text-text-2">
|
||
{c.lines.map((l, j) => <span key={j}>{j > 0 && <br />}{l}</span>)}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</>
|
||
)
|
||
}
|
||
|
||
/* ──────────── PANEL 1: 배치 이론 ──────────── */
|
||
function DeploymentTheoryPanel() {
|
||
return (
|
||
<>
|
||
{/* 차단 효율 이론 */}
|
||
<div className="rounded-md p-4 mb-3.5 bg-bg-3 border border-border">
|
||
<div className="text-xs font-bold mb-3">📐 오일펜스 차단 효율 이론 (Boom Containment Efficiency)</div>
|
||
<div className="grid grid-cols-2 gap-3.5">
|
||
<div>
|
||
<div className="text-[11px] font-bold mb-2 text-status-orange">① 차단 효율 함수 E(θ, U)</div>
|
||
<div className="text-[10px] leading-[1.8] mb-2 text-text-2">
|
||
오일펜스의 차단 효율은 <b>조류 유속(U)</b>과 <b>오일펜스 방향각(θ)</b>의 함수입니다. 조류가 오일펜스에 수직으로 입사할수록 차단 효율이 낮아지며, 임계유속 초과 시 기름이 오일펜스 하부로 통과합니다.
|
||
</div>
|
||
<div className="rounded-md p-2.5 text-[10px] leading-[2.1] bg-bg-0 font-mono" style={{ border: '1px solid rgba(249,115,22,.2)' }}>
|
||
E(θ,U) = 1 − <span className="text-status-red">F<sub>loss</sub>(U<sub>n</sub>)</span><br />
|
||
U<sub>n</sub> = U · sin(θ) <span className="text-[9px] text-text-3">(법선방향 유속)</span><br />
|
||
E = 1 (U<sub>n</sub> ≤ U<sub>c</sub>)<br />
|
||
E = max(0, 1 − (U<sub>n</sub>/U<sub>c</sub>)²) (U<sub>n</sub> > U<sub>c</sub>)<br />
|
||
<span className="text-[9px] text-text-3">U<sub>c</sub>: 임계유속(약 0.35m/s = 0.7 knot)</span>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div className="text-[11px] font-bold mb-2 text-primary-cyan">② 최적 방향각 θ* 산정</div>
|
||
<div className="text-[10px] leading-[1.8] mb-2 text-text-2">
|
||
오일펜스 방향각은 조류 방향에 따라 최적화됩니다. 차단 면적과 오일 수집 효율의 <b>트레이드오프</b>를 고려하여, 일반적으로 조류에 대해 <b className="text-primary-cyan">30°~45° 예각 배치</b>가 최적입니다.
|
||
</div>
|
||
<div className="rounded-md p-2.5 text-[10px] leading-[2.1] bg-bg-0 font-mono" style={{ border: '1px solid rgba(6,182,212,.2)' }}>
|
||
θ* = arcsin(U<sub>c</sub> / U) <span className="text-[9px] text-text-3">(임계조건)</span><br />
|
||
θ<sub>opt</sub> = argmax [A<sub>block</sub>(θ) · E(θ,U)]<br />
|
||
실용범위: 15° ≤ θ ≤ 60°<br />
|
||
<span className="text-[9px] text-text-3">단, θ < arcsin(U<sub>c</sub>/U) 이면 기름 통과 발생</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* V형·U형·J형 배치 패턴 */}
|
||
<div className="rounded-md p-4 mb-3.5 bg-bg-3 border border-border">
|
||
<div className="text-xs font-bold mb-3">🔷 오일펜스 배치 형태별 이론</div>
|
||
<div className="grid grid-cols-3 gap-3">
|
||
{/* V형 */}
|
||
<div className="rounded-lg p-3.5 bg-bg-0" style={{ border: '1px solid rgba(6,182,212,.2)', borderTop: '3px solid var(--cyan)' }}>
|
||
<div className="text-[11px] font-bold mb-2 text-primary-cyan">V형 (Chevron)</div>
|
||
<div className="flex items-center justify-center p-4 rounded-md mb-2" style={{ background: 'rgba(6,182,212,.04)' }}>
|
||
<svg width="180" height="120" viewBox="0 0 100 65" className="overflow-visible">
|
||
<defs><marker id="arr1" markerWidth="6" markerHeight="6" refX="3" refY="3" orient="auto"><path d="M0,0 L0,6 L6,3 z" fill="rgba(6,182,212,.7)" /></marker></defs>
|
||
<line x1="10" y1="15" x2="50" y2="55" stroke="rgba(249,115,22,.8)" strokeWidth="2.5" />
|
||
<line x1="90" y1="15" x2="50" y2="55" stroke="rgba(249,115,22,.8)" strokeWidth="2.5" />
|
||
<circle cx="50" cy="10" r="4" fill="rgba(249,115,22,.5)" />
|
||
<text x="50" y="7" textAnchor="middle" fill="rgba(249,115,22,.8)" fontSize="7" fontFamily="monospace">집유점</text>
|
||
<line x1="50" y1="58" x2="50" y2="65" stroke="rgba(6,182,212,.7)" strokeWidth="1.5" markerEnd="url(#arr1)" />
|
||
<text x="58" y="64" fill="rgba(6,182,212,.7)" fontSize="6">조류</text>
|
||
</svg>
|
||
</div>
|
||
<div className="text-[10px] leading-[1.7] mb-[7px] text-text-2">조류 방향 정면에서 양측으로 펼친 V형. 기름을 중앙 집유점으로 유도. 회수선 배치 용이.</div>
|
||
<div className="rounded-[5px] p-[7px] text-[9px] leading-[1.9] font-mono" style={{ background: 'rgba(6,182,212,.05)' }}>
|
||
A<sub>V</sub> = L²·sin(2α)/2<br />
|
||
<span className="text-text-3">α: 반개각, L: 편측 길이</span><br />
|
||
최적 α = 30°~45°
|
||
</div>
|
||
</div>
|
||
|
||
{/* U형 */}
|
||
<div className="rounded-lg p-3.5 bg-bg-0" style={{ border: '1px solid rgba(34,197,94,.2)', borderTop: '3px solid var(--green)' }}>
|
||
<div className="text-[11px] font-bold mb-2 text-status-green">U형 (Horseshoe)</div>
|
||
<div className="flex items-center justify-center p-4 rounded-md mb-2" style={{ background: 'rgba(34,197,94,.04)' }}>
|
||
<svg width="180" height="120" viewBox="0 0 100 65" className="overflow-visible">
|
||
<defs><marker id="arr2" markerWidth="6" markerHeight="6" refX="3" refY="3" orient="auto"><path d="M0,0 L0,6 L6,3 z" fill="rgba(6,182,212,.7)" /></marker></defs>
|
||
<path d="M15,10 L15,45 Q50,65 85,45 L85,10" fill="none" stroke="rgba(34,197,94,.8)" strokeWidth="2.5" />
|
||
<circle cx="50" cy="52" r="4" fill="rgba(34,197,94,.5)" />
|
||
<text x="50" y="62" textAnchor="middle" fill="rgba(34,197,94,.8)" fontSize="7" fontFamily="monospace">회수선</text>
|
||
<line x1="50" y1="0" x2="50" y2="7" stroke="rgba(6,182,212,.7)" strokeWidth="1.5" markerEnd="url(#arr2)" />
|
||
<text x="58" y="5" fill="rgba(6,182,212,.7)" fontSize="6">조류</text>
|
||
</svg>
|
||
</div>
|
||
<div className="text-[10px] leading-[1.7] mb-[7px] text-text-2">말굽형으로 기름을 완전 포위. 폐쇄형 구조로 회수 효율 최고. 저조류 해역 적합.</div>
|
||
<div className="rounded-[5px] p-[7px] text-[9px] leading-[1.9] font-mono" style={{ background: 'rgba(34,197,94,.05)' }}>
|
||
A<sub>U</sub> = π·r²/2 + 2r·h<br />
|
||
<span className="text-text-3">r: 반경, h: 직선부 길이</span><br />
|
||
전제: U < 0.5 knot
|
||
</div>
|
||
</div>
|
||
|
||
{/* J형 */}
|
||
<div className="rounded-lg p-3.5 bg-bg-0" style={{ border: '1px solid rgba(168,85,247,.2)', borderTop: '3px solid var(--purple)' }}>
|
||
<div className="text-[11px] font-bold mb-2 text-primary-purple">J형 (Skimming)</div>
|
||
<div className="flex items-center justify-center p-4 rounded-md mb-2" style={{ background: 'rgba(168,85,247,.04)' }}>
|
||
<svg width="180" height="120" viewBox="0 0 100 65" className="overflow-visible">
|
||
<defs><marker id="arr3" markerWidth="6" markerHeight="6" refX="3" refY="3" orient="auto"><path d="M0,0 L0,6 L6,3 z" fill="rgba(6,182,212,.7)" /></marker></defs>
|
||
<line x1="80" y1="8" x2="80" y2="48" stroke="rgba(168,85,247,.8)" strokeWidth="2.5" />
|
||
<path d="M80,48 Q55,60 30,35" fill="none" stroke="rgba(168,85,247,.8)" strokeWidth="2.5" />
|
||
<circle cx="79" cy="48" r="4" fill="rgba(168,85,247,.5)" />
|
||
<text x="67" y="43" fill="rgba(168,85,247,.8)" fontSize="7" fontFamily="monospace">회수</text>
|
||
<line x1="50" y1="0" x2="50" y2="7" stroke="rgba(6,182,212,.7)" strokeWidth="1.5" markerEnd="url(#arr3)" />
|
||
<text x="58" y="5" fill="rgba(6,182,212,.7)" fontSize="6">조류</text>
|
||
</svg>
|
||
</div>
|
||
<div className="text-[10px] leading-[1.7] mb-[7px] text-text-2">직선+곡선 조합. 기름을 한쪽으로 편향 유도하여 집유. 강조류·연안 배치에 최적.</div>
|
||
<div className="rounded-[5px] p-[7px] text-[9px] leading-[1.9] font-mono" style={{ background: 'rgba(168,85,247,.05)' }}>
|
||
θ<sub>J</sub> = arcsin(U<sub>c</sub>/U) + δ<br />
|
||
<span className="text-text-3">δ: 안전여유각(5°~10°)</span><br />
|
||
활용: U > 0.7 knot
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 다단 배치 이론 */}
|
||
<div className="rounded-md p-3.5 bg-bg-3 border border-border">
|
||
<div className="text-[11px] font-bold mb-2.5">🔢 다단계 차단선(Multi-Boom) 배치 이론</div>
|
||
<div className="grid grid-cols-2 gap-3">
|
||
<div className="text-[10px] leading-[1.8] text-text-2">
|
||
단일 오일펜스로 차단 불가한 경우 <b>직렬 다단 배치</b>로 누적 차단 효율을 향상합니다. n개 직렬 배치 시 누적 차단 효율:
|
||
<div className="mt-2 rounded-[5px] p-[9px] leading-[2] bg-bg-0 font-mono">
|
||
E<sub>total</sub> = 1 − ∏(1−E<sub>i</sub>)<br />
|
||
<span className="text-[9px] text-text-3">E<sub>i</sub>: i번째 오일펜스 단독 차단효율</span>
|
||
</div>
|
||
</div>
|
||
<div className="flex flex-col gap-[5px] text-[9px] text-text-2">
|
||
{[
|
||
{ color: 'var(--green)', bg: 'rgba(34,197,94,.05)', bd: 'rgba(34,197,94,.12)', label: '2단 직렬', text: ': E_total = E₁+E₂−E₁·E₂ (예: 70%+70% → 91%)' },
|
||
{ color: 'var(--cyan)', bg: 'rgba(6,182,212,.05)', bd: 'rgba(6,182,212,.12)', label: '단간 거리', text: ': 부표 집적 방지를 위해 ≥ 200m 이격 권장' },
|
||
{ color: 'var(--purple)', bg: 'rgba(168,85,247,.05)', bd: 'rgba(168,85,247,.12)', label: '배치 우선순위', text: ': ESI 고등급 구역 보호 → 취수원 → 어항 순' },
|
||
{ color: 'var(--orange)', bg: 'rgba(249,115,22,.05)', bd: 'rgba(249,115,22,.12)', label: '조석 변화', text: ': 창조·낙조 전환 시 오일펜스 방향 재조정 필요' },
|
||
].map((item, i) => (
|
||
<div key={i} className="px-[9px] py-1.5 rounded-[5px] text-text-2" style={{ background: item.bg, border: `1px solid ${item.bd}` }}>
|
||
<b style={{ color: item.color }}>{item.label}</b>{item.text}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</>
|
||
)
|
||
}
|
||
|
||
/* ──────────── PANEL 2: 최적화 알고리즘 ──────────── */
|
||
function OptimizationPanel() {
|
||
return (
|
||
<>
|
||
{/* 다목적 최적화 개요 */}
|
||
<div className="rounded-xl p-4 mb-3.5 relative overflow-hidden"
|
||
style={{ background: 'linear-gradient(135deg,rgba(168,85,247,.06),rgba(59,130,246,.04))', border: '1px solid rgba(168,85,247,.2)' }}>
|
||
<div className="absolute top-0 left-0 right-0 h-[3px]" style={{ background: 'linear-gradient(90deg,var(--purple),var(--blue))' }} />
|
||
<div className="text-[13px] font-bold mb-2">⚙️ 다목적 최적화 문제 (Multi-Objective Optimization)</div>
|
||
<div className="text-[11px] leading-[1.8] text-text-2">
|
||
오일펜스 배치 최적화는 <b className="text-primary-purple">상충하는 복수 목적함수</b>를 동시에 만족해야 하는 전형적인 다목적 최적화 문제입니다. 차단 효율 최대화와 자원 사용 최소화는 서로 트레이드오프 관계를 가지며, <b className="text-primary-cyan">파레토 최적(Pareto Optimal) 해집합</b>에서 의사결정자가 선택합니다.
|
||
</div>
|
||
</div>
|
||
|
||
{/* 목적함수 정의 */}
|
||
<div className="rounded-md p-4 mb-3.5 bg-bg-3 border border-border">
|
||
<div className="text-xs font-bold mb-3">📊 목적함수 및 제약조건 정의</div>
|
||
<div className="grid grid-cols-2 gap-3 mb-3">
|
||
<div className="rounded-lg p-3 bg-bg-0" style={{ border: '1px solid rgba(34,197,94,.2)' }}>
|
||
<div className="text-[11px] font-bold mb-2 text-status-green">🎯 목적함수 F(x)</div>
|
||
<div className="rounded-[5px] p-[9px] text-[10px] leading-[2.2] font-mono" style={{ background: 'rgba(34,197,94,.04)' }}>
|
||
<b className="text-status-green">최대화:</b><br />
|
||
f₁(x) = Σ A<sub>blocked,i</sub> · w<sub>ESI,i</sub> <span className="text-[9px] text-text-3">(가중 차단면적)</span><br />
|
||
f₂(x) = T<sub>deadline</sub> − T<sub>deploy</sub> <span className="text-[9px] text-text-3">(여유시간)</span><br />
|
||
<b className="text-status-red">최소화:</b><br />
|
||
f₃(x) = Σ L<sub>boom,j</sub> <span className="text-[9px] text-text-3">(총 오일펜스 사용량)</span><br />
|
||
f₄(x) = Σ D<sub>vessel,k</sub> <span className="text-[9px] text-text-3">(방제정 총 이동거리)</span>
|
||
</div>
|
||
</div>
|
||
<div className="rounded-lg p-3 bg-bg-0" style={{ border: '1px solid rgba(239,68,68,.2)' }}>
|
||
<div className="text-[11px] font-bold mb-2 text-status-red">🚫 제약조건 G(x)</div>
|
||
<div className="rounded-[5px] p-[9px] text-[10px] leading-[2.2] font-mono" style={{ background: 'rgba(239,68,68,.04)' }}>
|
||
g₁: U·sin(θ<sub>i</sub>) ≤ U<sub>c</sub> ∀i <span className="text-[9px] text-text-3">(임계유속)</span><br />
|
||
g₂: Σ L<sub>j</sub> ≤ L<sub>max</sub> <span className="text-[9px] text-text-3">(자원 한계)</span><br />
|
||
g₃: T<sub>deploy,i</sub> ≤ T<sub>arrive,i</sub> <span className="text-[9px] text-text-3">(시간 제약)</span><br />
|
||
g₄: d(p<sub>i</sub>, shore) ≥ d<sub>min</sub> <span className="text-[9px] text-text-3">(연안 이격)</span><br />
|
||
g₅: h(p<sub>i</sub>) ≥ h<sub>min</sub> <span className="text-[9px] text-text-3">(수심 조건)</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* ESI 가중치 */}
|
||
<div className="rounded-lg p-3 bg-bg-0" style={{ border: '1px solid rgba(234,179,8,.2)' }}>
|
||
<div className="text-[10px] font-bold mb-2 text-status-yellow">🏖️ ESI 가중치 w<sub>ESI</sub> 설계</div>
|
||
<div className="grid grid-cols-5 gap-[5px] text-[9px] text-text-2">
|
||
{[
|
||
{ grade: 'ESI 1~2', desc: '노출암반', w: 'w = 0.2', color: 'var(--green)', bg: 'rgba(34,197,94,.06)' },
|
||
{ grade: 'ESI 3~4', desc: '모래해변', w: 'w = 0.4', color: 'var(--cyan)', bg: 'rgba(6,182,212,.06)' },
|
||
{ grade: 'ESI 5~6', desc: '자갈·조약', w: 'w = 0.6', color: 'var(--yellow)', bg: 'rgba(234,179,8,.06)' },
|
||
{ grade: 'ESI 7~8', desc: '갯벌·조간대', w: 'w = 0.85', color: 'var(--orange)', bg: 'rgba(249,115,22,.06)' },
|
||
{ grade: 'ESI 9~10', desc: '맹그로브·습지', w: 'w = 1.0', color: 'var(--red)', bg: 'rgba(239,68,68,.08)', bd: 'rgba(239,68,68,.2)' },
|
||
].map((esi, i) => (
|
||
<div key={i} className="p-1.5 rounded text-center" style={{ background: esi.bg, border: esi.bd ? `1px solid ${esi.bd}` : undefined }}>
|
||
<div className="font-bold" style={{ color: esi.color }}>{esi.grade}</div>
|
||
<div className="text-text-3">{esi.desc}</div>
|
||
<div className="font-bold">{esi.w}</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* NSGA-II 알고리즘 */}
|
||
<div className="rounded-md p-4 mb-3.5 bg-bg-3 border border-border">
|
||
<div className="text-xs font-bold mb-3 text-primary-purple">🧬 NSGA-II (Non-dominated Sorting Genetic Algorithm II)</div>
|
||
<div className="grid grid-cols-2 gap-3">
|
||
<div>
|
||
<div className="text-[10px] leading-[1.8] mb-2 text-text-2">
|
||
WING의 오일펜스 배치 최적화는 다목적 유전알고리즘 <b className="text-primary-purple">NSGA-II</b>(Deb et al., 2002)를 핵심 엔진으로 사용합니다. 파레토 전면(Pareto Front)을 탐색하여 차단 효율과 자원 효율의 최적 해집합을 제공합니다.
|
||
</div>
|
||
<div className="flex flex-col gap-1 text-[9px] text-text-2">
|
||
{[
|
||
'염색체 구조 : [배치지점 좌표, 방향각θ, 길이L, 형태, 배치순서]',
|
||
'집단 크기 : 100~200개체 · 세대수 50~200',
|
||
'교배 연산 : SBX(Simulated Binary Crossover) · ηc=20',
|
||
'변이 연산 : 다항식 변이(Polynomial Mutation) · ηm=20',
|
||
'선택 방식 : 비지배 정렬 + 혼잡도 거리(Crowding Distance)',
|
||
].map((item, i) => (
|
||
<div key={i} className="px-2 py-[5px] rounded text-text-2" style={{ background: 'rgba(168,85,247,.05)', border: '1px solid rgba(168,85,247,.12)' }}>
|
||
<b className="text-primary-purple">{item.split(' : ')[0]}</b> : {item.split(' : ')[1]}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div className="text-[10px] font-bold mb-[7px] text-text-2">NSGA-II 5단계 진화 루프</div>
|
||
<div className="flex flex-col gap-[5px] text-[9px] text-text-2">
|
||
{[
|
||
{ step: '①', title: '초기 집단 생성', desc: '확산예측 결과 기반 랜덤 + 휴리스틱 배치안 혼합 초기화' },
|
||
{ step: '②', title: '적합도 평가', desc: '유출유 확산 시뮬레이터로 각 배치안의 차단면적·도달시간 계산' },
|
||
{ step: '③', title: '비지배 정렬', desc: '목적함수 공간에서 파레토 전면 계층(F₁, F₂, F₃…) 분류' },
|
||
{ step: '④', title: '교배·변이', desc: 'SBX + 다항식 변이로 자식 세대 생성. 제약조건 위반 수리(repair)' },
|
||
{ step: '⑤', title: '엘리트 선택', desc: '부모+자식 2N 집단에서 비지배 정렬+혼잡도 기준으로 N개 선택 → 수렴까지 반복' },
|
||
].map((item, i) => (
|
||
<div key={i} className="flex gap-2 px-[9px] py-1.5 rounded-[5px]" style={{ background: i === 4 ? 'rgba(168,85,247,.05)' : 'rgba(168,85,247,.04)', border: i === 4 ? '1px solid rgba(168,85,247,.12)' : undefined }}>
|
||
<span className="min-w-[20px] font-extrabold text-primary-purple">{item.step}</span>
|
||
<div className="leading-[1.6] text-text-2"><b>{item.title}</b> : {item.desc}</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 보조 알고리즘 비교 */}
|
||
<div className="rounded-md p-3.5 bg-bg-3 border border-border">
|
||
<div className="text-[11px] font-bold mb-2.5">🔬 보조 최적화 알고리즘 비교 적용</div>
|
||
<div className="overflow-x-auto">
|
||
<table className="w-full text-[10px] border-collapse">
|
||
<thead>
|
||
<tr style={{ background: 'rgba(255,255,255,.03)', borderBottom: '1px solid var(--bdL)' }}>
|
||
{['알고리즘', '유형', '장점', '단점', 'WING 활용'].map(h => (
|
||
<th key={h} className="py-[7px] px-2.5 font-semibold text-text-3" style={{ textAlign: h === '알고리즘' ? 'left' : 'center' }}>{h}</th>
|
||
))}
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{[
|
||
{ name: 'NSGA-II', color: 'var(--purple)', type: '다목적 GA', pros: '파레토 전면 탐색\n다양성 유지 우수', cons: '계산비용 높음\n수렴 느림', wing: '메인 엔진', wingColor: 'var(--cyan)' },
|
||
{ name: 'PSO', color: 'var(--orange)', type: '입자군집', pros: '빠른 수렴\n구현 단순', cons: '조기수렴\n다목적 취약', wing: '단일목적 빠른 배치', wingColor: 'var(--t2)' },
|
||
{ name: 'SA', color: 'var(--blue)', type: '모의담금질', pros: '전역 탈출 우수\n국소최적 회피', cons: '매개변수 민감\n느린 수렴', wing: '긴급 단순 배치', wingColor: 'var(--t2)' },
|
||
{ name: 'Greedy+휴리스틱', color: 'var(--green)', type: '결정론적', pros: '즉시 결과\n해석 용이', cons: '전역최적 미보장', wing: '실시간 초기 제안', wingColor: 'var(--green)' },
|
||
].map((row, i) => (
|
||
<tr key={i} style={{ borderBottom: '1px solid rgba(255,255,255,.04)', background: i % 2 === 1 ? 'rgba(255,255,255,.01)' : undefined }}>
|
||
<td className="py-[7px] px-2.5 font-bold" style={{ color: row.color }}>{row.name}</td>
|
||
<td className="py-[7px] px-2.5 text-center text-text-2">{row.type}</td>
|
||
<td className="py-[7px] px-2.5 text-center text-text-2 whitespace-pre-line">{row.pros}</td>
|
||
<td className="py-[7px] px-2.5 text-center text-text-2 whitespace-pre-line">{row.cons}</td>
|
||
<td className="py-[7px] px-2.5 text-center" style={{ color: row.wingColor }}>{row.wing}</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</>
|
||
)
|
||
}
|
||
|
||
/* ──────────── PANEL 3: 유체역학 모델 ──────────── */
|
||
function FluidDynamicsPanel() {
|
||
return (
|
||
<>
|
||
{/* 유동 수치 모델 */}
|
||
<div className="rounded-md p-4 mb-3.5 bg-bg-3 border border-border">
|
||
<div className="text-xs font-bold mb-3">🌊 오일펜스 주변 유동 수치 모델</div>
|
||
<div className="grid grid-cols-2 gap-3.5">
|
||
<div>
|
||
<div className="text-[11px] font-bold mb-2 text-primary-blue">① 오일펜스 항력 모델</div>
|
||
<div className="text-[10px] leading-[1.8] mb-2 text-text-2">오일펜스에 작용하는 항력은 조류속도의 제곱에 비례합니다. 오일펜스 구조 변형(catenary형태)을 고려한 동적 항력 계산.</div>
|
||
<div className="rounded-md p-2.5 text-[10px] leading-[2.1] bg-bg-0 font-mono" style={{ border: '1px solid rgba(59,130,246,.2)' }}>
|
||
F<sub>D</sub> = ½ · ρ · C<sub>D</sub> · A · U<sub>n</sub>²<br />
|
||
T = F<sub>D</sub> · L / (2·sin(α))<br />
|
||
<span className="text-[9px] text-text-3">C<sub>D</sub>: 항력계수(≈1.2), A: 수중 투영면적</span><br />
|
||
<span className="text-[9px] text-text-3">T: 연결부 장력, α: 체인각도</span>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div className="text-[11px] font-bold mb-2 text-status-orange">② 기름 통과(Splash-over) 조건</div>
|
||
<div className="text-[10px] leading-[1.8] mb-2 text-text-2">조류 유속이 임계값을 초과하면 기름이 파도를 타고 오일펜스를 넘어가는 Splash-over가 발생합니다.</div>
|
||
<div className="rounded-md p-2.5 text-[10px] leading-[2.1] bg-bg-0 font-mono" style={{ border: '1px solid rgba(249,115,22,.2)' }}>
|
||
Fr = U<sub>n</sub> / √(g·Δρ/ρ·h)<br />
|
||
Splash-over: Fr > 0.5~0.6<br />
|
||
<span className="text-[9px] text-text-3">Fr: 수정 Froude수, h: 오일펜스 수중깊이</span><br />
|
||
<span className="text-[9px] text-text-3">Δρ/ρ: 기름-해수 밀도비 (~0.15)</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Catenary 변형 모델 */}
|
||
<div className="rounded-md p-4 mb-3.5 bg-bg-3 border border-border">
|
||
<div className="text-xs font-bold mb-3">🔗 오일펜스 현수선(Catenary) 변형 모델</div>
|
||
<div className="grid grid-cols-2 gap-3.5">
|
||
<div className="text-[10px] leading-[1.8] text-text-2">
|
||
조류와 바람에 의해 오일펜스는 현수선(Catenary) 형태로 변형됩니다. 실제 차단 길이가 설계 길이보다 짧아지며, 최적화 알고리즘에서 <b>변형 후 유효 차단 길이</b> L<sub>eff</sub>를 계산합니다.
|
||
<div className="mt-2 rounded-[5px] p-[9px] leading-[2] bg-bg-0 font-mono">
|
||
y(x) = a·cosh(x/a) − a<br />
|
||
L<sub>arc</sub> = 2a·sinh(L<sub>span</sub>/(2a))<br />
|
||
L<sub>eff</sub> = L<sub>span</sub> · cos(φ<sub>max</sub>)<br />
|
||
<span className="text-[9px] text-text-3">a: catenary 파라미터, φ: 최대 편향각</span>
|
||
</div>
|
||
</div>
|
||
<div className="flex flex-col gap-1.5 text-[9px] text-text-2">
|
||
<div className="text-[10px] font-bold mb-1 text-text-2">변형 단계별 유효 차단 길이 보정</div>
|
||
{[
|
||
{ cond: 'U < 0.3 knot', result: 'L_eff ≈ L (직선 유지)', bg: 'rgba(34,197,94,.05)', bd: 'rgba(34,197,94,.12)' },
|
||
{ cond: '0.3~0.7 knot', result: 'L_eff = 0.8~0.95 L (경미 변형)', bg: 'rgba(234,179,8,.05)', bd: 'rgba(234,179,8,.12)' },
|
||
{ cond: '0.7~1.0 knot', result: 'L_eff = 0.5~0.8 L (Catenary 현저)', bg: 'rgba(249,115,22,.05)', bd: 'rgba(249,115,22,.12)' },
|
||
{ cond: 'U > 1.0 knot', result: '기름 통과 위험 · 배치 재계산', bg: 'rgba(239,68,68,.05)', bd: 'rgba(239,68,68,.12)', danger: true },
|
||
].map((item, i) => (
|
||
<div key={i} className="px-[9px] py-1.5 rounded-[5px]" style={{ background: item.bg, border: `1px solid ${item.bd}`, color: item.danger ? 'var(--red)' : 'var(--t2)' }}>
|
||
{item.cond} → {item.result}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 유막 포집 모델 */}
|
||
<div className="rounded-md p-3.5 bg-bg-3 border border-border">
|
||
<div className="text-[11px] font-bold mb-2.5">🛢️ 오일펜스 내 유막 포집 동역학</div>
|
||
<div className="grid grid-cols-2 gap-3 text-[10px] text-text-2">
|
||
<div>
|
||
<div className="font-bold mb-1.5 text-primary-cyan">포집 기름 체적 변화율</div>
|
||
<div className="rounded-[5px] p-[9px] leading-[2] bg-bg-0 font-mono">
|
||
dV<sub>oil</sub>/dt = Q<sub>in</sub> − Q<sub>out</sub> − Q<sub>loss</sub><br />
|
||
Q<sub>in</sub> = U<sub>oil</sub>·h<sub>oil</sub>·L<sub>eff</sub><br />
|
||
Q<sub>out</sub> = Q<sub>skim</sub> (회수기 흡입량)<br />
|
||
Q<sub>loss</sub> = 증발+소산 손실
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div className="font-bold mb-1.5 text-status-orange">최적 회수 타이밍</div>
|
||
<div className="text-[10px] leading-[1.7] text-text-2">포집 기름 체적이 오일펜스 저장 용량의 70~80%에 도달하면 Skimmer 회수 작업을 개시합니다. 이를 초과하면 오일 overflow 발생. WING이 실시간 체적 모니터링 후 회수 알람 발령.</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</>
|
||
)
|
||
}
|
||
|
||
/* ──────────── PANEL 4: 현장 적용 ──────────── */
|
||
function FieldApplicationPanel() {
|
||
const steps = [
|
||
{ num: 1, color: 'var(--orange)', bg: 'rgba(249,115,22,.05)', bd: 'rgba(249,115,22,.15)', numBg: 'rgba(249,115,22,.15)', numBd: 'rgba(249,115,22,.3)', title: '확산예측 결과 분석 — 위협 구역 및 도달시간 산출', desc: 'KOSPS·POSEIDON·OpenDrift 앙상블 예측 결과에서 유출유 확산 경계선(Pollution Boundary)과 각 ESI 구역별 도달 예상시간(T_arrive)을 산출합니다. 신뢰도 70% 이상 예측 경계를 기준으로 차단 전략 영역을 설정합니다.' },
|
||
{ num: 2, color: 'var(--cyan)', bg: 'rgba(6,182,212,.05)', bd: 'rgba(6,182,212,.15)', numBg: 'rgba(6,182,212,.15)', numBd: 'rgba(6,182,212,.3)', title: '해양환경 조건 확인 — 조류·파고·수심·기상 입력', desc: 'CHARRY 채리모델 조류예측값, KMA UM 풍속·풍향, 수심격자, NGSST 수온을 자동 연계하여 각 후보 배치지점의 U_n(법선방향 유속)을 계산합니다. 임계유속 0.7 knot를 초과하는 지점은 자동으로 J형·다단 배치로 변환합니다.' },
|
||
{ num: 3, color: 'var(--yellow)', bg: 'rgba(234,179,8,.05)', bd: 'rgba(234,179,8,.15)', numBg: 'rgba(234,179,8,.15)', numBd: 'rgba(234,179,8,.3)', title: '후보 차단선 격자 탐색 — 배치 가능지점 생성', desc: '확산 예측 경계를 따라 500m 간격 격자로 후보 배치지점을 생성합니다. 각 지점에서 조류 조건·수심·해안선 이격·방제정 접근 가능성을 동시 검토합니다. ESI 고등급 구역 전방 2km 이내 지점에 우선 가중치를 부여합니다.' },
|
||
{ num: 4, color: 'var(--purple)', bg: 'rgba(168,85,247,.05)', bd: 'rgba(168,85,247,.15)', numBg: 'rgba(168,85,247,.15)', numBd: 'rgba(168,85,247,.3)', title: 'NSGA-II 최적화 실행 — 파레토 최적 배치안 산출', desc: '후보 배치지점·방향각·형태 조합을 염색체로 인코딩하여 NSGA-II 다목적 유전알고리즘을 실행합니다. 수렴 후 파레토 전면에서 3~5개 추천 배치안을 제시하며, 의사결정자가 차단 효율과 자원 사용량의 트레이드오프를 보고 선택합니다.' },
|
||
{ num: 5, color: 'var(--green)', bg: 'rgba(34,197,94,.05)', bd: 'rgba(34,197,94,.15)', numBg: 'rgba(34,197,94,.15)', numBd: 'rgba(34,197,94,.3)', title: '실시간 재최적화 — 조석 변환·조류 변화 대응', desc: '창조→낙조 전환 시(약 6시간 주기) 조류 방향 역전에 따른 오일펜스 재배치 알람을 발령합니다. 확산 예측이 갱신될 때마다 배치 최적화를 자동 재실행하여 방제대응 체계를 동적으로 업데이트합니다.' },
|
||
]
|
||
|
||
return (
|
||
<>
|
||
{/* 배치 5단계 절차 */}
|
||
<div className="rounded-md p-4 mb-3.5 bg-bg-3 border border-border">
|
||
<div className="text-xs font-bold mb-3">🗺️ WING 오일펜스 배치 의사결정 5단계</div>
|
||
<div className="flex flex-col gap-2">
|
||
{steps.map(step => (
|
||
<div key={step.num} className="flex gap-3 items-start p-3 rounded-lg" style={{ background: step.bg, border: `1px solid ${step.bd}` }}>
|
||
<div className="min-w-[36px] h-[36px] rounded-[9px] flex items-center justify-center font-extrabold text-sm flex-shrink-0" style={{ background: step.numBg, border: `1px solid ${step.numBd}`, color: step.color }}>{step.num}</div>
|
||
<div className="text-[10px] text-text-2">
|
||
<div className="font-bold mb-1">{step.title}</div>
|
||
<div className="leading-[1.7] text-text-2">{step.desc}</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* 해역별 적용 특성 */}
|
||
<div className="rounded-md p-3.5 bg-bg-3 border border-border">
|
||
<div className="text-[11px] font-bold mb-2.5">📍 해역별 적용 특성 및 전략</div>
|
||
<div className="grid grid-cols-3 gap-2.5 text-[9px] text-text-2">
|
||
{[
|
||
{ icon: '🌊', title: '서해 (조차 대형)', color: 'var(--blue)', bg: 'rgba(59,130,246,.05)', bd: 'rgba(59,130,246,.12)', desc: '최대 조차 9m (인천), 조류 최대 3~5 knot. J형 배치 주력. 조석 전환 재배치 필수. 앵커링 수심 급변화 주의.' },
|
||
{ icon: '🌿', title: '남해 (다도해)', color: 'var(--green)', bg: 'rgba(34,197,94,.05)', bd: 'rgba(34,197,94,.12)', desc: '복잡한 해안선·섬. 조류 1~2 knot. V형·U형 복합 배치. 좁은 수로 통제 우선. ESI 고등급 갯벌 보호.' },
|
||
{ icon: '🏔️', title: '동해 (심해형)', color: 'var(--orange)', bg: 'rgba(249,115,22,.05)', bd: 'rgba(249,115,22,.12)', desc: '조차 소(0.3m), 너울·파고 높음. 조류 0.5~1 knot. V형 집중. 고파랑 시 배치 제한. 수온약층 고려.' },
|
||
].map((area, i) => (
|
||
<div key={i} className="p-2.5 rounded-[7px]" style={{ background: area.bg, border: `1px solid ${area.bd}` }}>
|
||
<div className="font-bold mb-[5px]" style={{ color: area.color }}>{area.icon} {area.title}</div>
|
||
<div className="leading-[1.7] text-text-2">{area.desc}</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</>
|
||
)
|
||
}
|
||
|
||
/* ──────────── PANEL 5: 참고문헌 ──────────── */
|
||
function ReferencesPanel() {
|
||
const categories = [
|
||
{
|
||
title: '⚙️ 최적화 알고리즘',
|
||
color: 'var(--purple)',
|
||
bg: 'rgba(168,85,247,.1)',
|
||
bd: 'rgba(168,85,247,.25)',
|
||
refs: [
|
||
{ title: 'A Fast and Elitist Multiobjective Genetic Algorithm: NSGA-II', author: 'Deb, K., Pratap, A., Agarwal, S., & Meyarivan, T. | IEEE Trans. Evol. Comput. 6(2):182–197, 2002', desc: 'NSGA-II 원전 · 비지배 정렬 + 혼잡도 거리 · 파레토 전면 탐색 알고리즘 · WING 다목적 최적화 엔진의 핵심 이론 기반' },
|
||
{ title: 'An Emergency Scheduling Model for Oil Containment Boom in Dynamically Changing Marine Oil Spills', author: 'Xu, Y., Zhang, L., Zheng, P., Liu, G., & Zhao, D. | Ningbo University | Systems 2025, 13, 716', desc: 'IMOGWO 다목적 최적화 · 오일필름 동적 모델 · 경제·생태 손실 정량화 · 주산해역 케이스 스터디', highlight: true },
|
||
{ title: '등록특허 10-1567431 기반 유출유 확산예측-방제 연동 시스템', author: '이문진 외 | 한국해양과학기술원 | 2015', desc: 'KOSPS 기반 확산예측-오일펜스 배치 연동 체계 원전 · ESI 방제정보지도 연동 · 취송류 경험식 기반 방향각 산정 근거' },
|
||
]
|
||
},
|
||
{
|
||
title: '🌊 유체역학 이론',
|
||
color: 'var(--blue)',
|
||
bg: 'rgba(59,130,246,.1)',
|
||
bd: 'rgba(59,130,246,.25)',
|
||
refs: [
|
||
{ title: 'Oil Boom Failure: Critical Velocity and Boom Design', author: 'Leibovich, S. | Annual Review of Fluid Mechanics 8:177–197, 1976', desc: '오일펜스 임계유속 이론 원전 · Froude수 기반 Splash-over 조건 · 방향각-차단효율 관계식' },
|
||
{ title: 'Dynamic Behavior of Oil Containment Booms in Currents', author: 'Delvigne, G.A.L. | Spill Science & Technology Bulletin 5(3-4):181–196, 1999', desc: '조류 중 오일펜스 항력·변형 동역학 · Catenary 형태 해석 · 실용 배치 설계 기준' },
|
||
{ title: 'Oil Boom Containment Efficiency in Waves and Currents', author: 'Wicks, M. | Proceedings of the Joint Conference on Prevention and Control of Oil Spills, 1969', desc: '파랑+조류 복합 환경에서 오일펜스 효율 실험 · V형·U형 성능 비교 기초 자료' },
|
||
{ title: 'Experimental, Numerical and Optimisation Study of Oil Spill Containment Boom', author: 'Muttin, F., Guyot, F., Nouchi, S. & Variot, B. | Coastal Environment V, WIT Press, 2004', desc: '유체-구조 상호작용 · 1.5D/2.5D 구조모델 · 유전알고리즘 최적화 · SPH 수치해법 · ERIKA·PRESTIGE 사고 검증', highlight: true },
|
||
]
|
||
},
|
||
{
|
||
title: '📐 오일펜스 배치 설계',
|
||
color: 'var(--cyan)',
|
||
bg: 'rgba(6,182,212,.1)',
|
||
bd: 'rgba(6,182,212,.25)',
|
||
refs: [
|
||
{ title: 'Optimization of an Oil Boom Arrangement', author: 'Fang, J. & Wong, K.-F.V. | Department of Mechanical Engineering, University of Miami', desc: '오일펜스 배치 형태(V형·U형·J형) 최적화 연구 · 조류 방향각과 차단효율 관계 수치 분석 · 방제정 예인각도별 성능 비교' },
|
||
]
|
||
},
|
||
{
|
||
title: '🗺️ 방제 운용 기준',
|
||
color: 'var(--green)',
|
||
bg: 'rgba(34,197,94,.1)',
|
||
bd: 'rgba(34,197,94,.25)',
|
||
refs: [
|
||
{ title: '기름오염방제시 오일펜스 사용지침 (ITOPF 방제기술정보문서 3)', author: 'ITOPF | 해양경찰청·해양환경관리공단 번역 | © 2011 ITOPF Ltd.', desc: '커튼형·펜스형·해안용 분류 · 6가지 기름 유실 메커니즘 · 힘 계산 공식 F=100·A·V² · 유속별 최대 설치각도 표', highlight: true },
|
||
{ title: 'NOAA ESI 방제정보지도 기반 오일펜스 우선 배치 전략', author: 'NOAA Office of Response and Restoration | Open Water Oil Identification Manual, 2013', desc: 'ESI 1~10 등급별 방제 우선순위 · 오일펜스 가중 배치 전략 · 취수원·어항 보호 기준' },
|
||
{ title: '해양경찰청 해양오염방제 업무매뉴얼 — 오일펜스 전개 절차', author: '해양경찰청 해양오염대응국 | 방제업무 기본지침, 최신판', desc: '국내 해역 오일펜스 운용 법적 기준 · 방제정 연계 전개 절차 · 서해/남해/동해 해역별 운용 특성' },
|
||
]
|
||
},
|
||
]
|
||
|
||
return (
|
||
<>
|
||
<div className="text-xs font-bold mb-1">📚 오일펜스 배치 최적화 이론 근거 문헌</div>
|
||
<div className="text-[10px] mb-3.5 text-text-3">총 12편 · 4개 카테고리</div>
|
||
|
||
{categories.map((cat, ci) => (
|
||
<div key={ci} className="mb-4">
|
||
<div className="text-[10px] font-bold mb-[7px] flex items-center gap-1.5" style={{ color: cat.color }}>
|
||
<span className="px-[7px] py-0.5 rounded" style={{ background: cat.bg, border: `1px solid ${cat.bd}` }}>{cat.title}</span>
|
||
</div>
|
||
<div className="flex flex-col gap-[5px] text-[9px] text-text-2">
|
||
{cat.refs.map((ref, ri) => (
|
||
<div key={ri} className="p-[9px] px-3 rounded-[7px] grid gap-2" style={{
|
||
gridTemplateColumns: '24px 1fr',
|
||
background: 'var(--bg3)',
|
||
border: ref.highlight ? `1px solid ${cat.bd}` : '1px solid var(--bd)',
|
||
borderLeft: ref.highlight ? `3px solid ${cat.color}` : undefined,
|
||
}}>
|
||
<div className="w-[22px] h-[22px] rounded flex items-center justify-center text-[10px] flex-shrink-0" style={{
|
||
background: ref.highlight ? `${cat.bg}` : `${cat.bg.replace('.1', '.12')}`,
|
||
fontWeight: ref.highlight ? 700 : undefined,
|
||
color: ref.highlight ? cat.color : undefined,
|
||
}}>
|
||
{ri + 1 === 1 ? '①' : ri + 1 === 2 ? '②' : ri + 1 === 3 ? '③' : '④'}
|
||
</div>
|
||
<div>
|
||
<div className="font-bold mb-0.5">{ref.title}</div>
|
||
<div className="leading-[1.6] text-text-3">{ref.author}</div>
|
||
<div className="mt-0.5 text-text-2">{ref.desc}</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</>
|
||
)
|
||
}
|