1444 lines
60 KiB
TypeScript
Executable File
1444 lines
60 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-base">
|
||
<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-[42px] h-[42px] rounded-[10px] bg-bg-elevated border border-stroke flex items-center justify-center text-heading-3">
|
||
🛡️
|
||
</div>
|
||
<div>
|
||
<div className="text-title-2 font-bold">오일펜스 배치 최적화 알고리즘 이론</div>
|
||
<div className="text-label-2 mt-0.5 text-fg-disabled">
|
||
Oil Boom Deployment Optimization · 유출유 확산예측 연동 · 방제효율 최대화
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<button
|
||
onClick={handleExportPDF}
|
||
className="px-3.5 py-1.5 rounded-sm text-label-2 font-semibold cursor-pointer text-color-accent"
|
||
style={{
|
||
border: '1px solid rgba(6,182,212,.3)',
|
||
background: 'rgba(6,182,212,.08)',
|
||
}}
|
||
>
|
||
📤 PDF 내보내기
|
||
</button>
|
||
</div>
|
||
|
||
{/* 내부 네비게이션 */}
|
||
<div className="mb-5">
|
||
<div className="flex gap-0.5 rounded-lg p-1 bg-bg-card border border-stroke">
|
||
{boomTabs.map((tab) => (
|
||
<button
|
||
key={tab.id}
|
||
onClick={() => setActivePanel(tab.id)}
|
||
className={`flex-1 py-2 px-1 text-label-1 font-medium rounded-md transition-all duration-150 cursor-pointer border ${
|
||
activePanel === tab.id
|
||
? 'border-stroke-light bg-bg-elevated text-fg'
|
||
: 'border-transparent bg-bg-card text-fg-disabled hover:text-fg-sub'
|
||
}`}
|
||
>
|
||
{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-[10px] p-[14px] mb-4 bg-bg-card border border-stroke">
|
||
<div className="grid grid-cols-2 gap-5">
|
||
<div>
|
||
<div className="text-title-4 font-bold mb-2">오일펜스 배치 최적화란?</div>
|
||
<div className="text-label-2 leading-[1.8] text-fg-sub">
|
||
해양 유류오염 발생 시 <b className="text-color-accent">유출유 확산 예측 결과</b>와
|
||
실시간 해양환경(조류·풍향·파고)을 연동하여, 제한된 방제자원(오일펜스 길이·방제정
|
||
수)으로 <b className="text-color-accent">오염 확산 차단 효율을 최대화</b>하는 최적
|
||
배치 지점·형태·순서를 자동 산출하는 수치 알고리즘 체계입니다.
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div className="text-title-4 font-bold mb-2">WING 최적화 목표</div>
|
||
<div className="flex flex-col gap-[5px] text-label-2 text-fg-sub">
|
||
{[
|
||
{
|
||
num: '①',
|
||
color: 'var(--color-accent)',
|
||
bg: 'rgba(6,182,212,.05)',
|
||
bd: 'rgba(6,182,212,.12)',
|
||
text: '차단 면적 최대화 — 예측 유출유 확산 경계와 오일펜스 교차 면적 극대화',
|
||
},
|
||
{
|
||
num: '②',
|
||
color: 'var(--color-accent)',
|
||
bg: 'rgba(6,182,212,.05)',
|
||
bd: 'rgba(6,182,212,.12)',
|
||
text: '도달시간 최소화 — 유출유 해안·ESI 민감구역 도달 전 선제적 차단선 구축',
|
||
},
|
||
{
|
||
num: '③',
|
||
color: 'var(--color-accent)',
|
||
bg: 'rgba(6,182,212,.05)',
|
||
bd: 'rgba(6,182,212,.12)',
|
||
text: '자원 효율 최적화 — 가용 오일펜스 길이·방제정 수·이동시간 제약 충족',
|
||
},
|
||
{
|
||
num: '④',
|
||
color: 'var(--color-accent)',
|
||
bg: 'rgba(6,182,212,.05)',
|
||
bd: 'rgba(6,182,212,.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-card border border-stroke">
|
||
<div className="text-label-1 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(--color-accent)',
|
||
bg: 'rgba(6,182,212,.08)',
|
||
bd: 'rgba(6,182,212,.2)',
|
||
},
|
||
{
|
||
icon: '📡',
|
||
label: '환경입력',
|
||
sub: '조류·풍향\n파고·수심',
|
||
color: 'var(--color-accent)',
|
||
bg: 'rgba(6,182,212,.08)',
|
||
bd: 'rgba(6,182,212,.2)',
|
||
},
|
||
{
|
||
icon: '🗺️',
|
||
label: '차단선 후보',
|
||
sub: '격자탐색\n후보지점 생성',
|
||
color: 'var(--color-caution)',
|
||
bg: 'rgba(6,182,212,.08)',
|
||
bd: 'rgba(6,182,212,.2)',
|
||
},
|
||
{
|
||
icon: '⚙️',
|
||
label: '최적화 엔진',
|
||
sub: '다목적\n유전알고리즘',
|
||
color: 'var(--color-accent)',
|
||
bg: 'rgba(6,182,212,.08)',
|
||
bd: 'rgba(6,182,212,.3)',
|
||
bold: true,
|
||
},
|
||
{
|
||
icon: '✅',
|
||
label: '배치안 출력',
|
||
sub: '좌표·형태\n방향·순서',
|
||
color: 'var(--color-accent)',
|
||
bg: 'rgba(6,182,212,.08)',
|
||
bd: 'rgba(6,182,212,.2)',
|
||
},
|
||
{
|
||
icon: '🗺️',
|
||
label: '지도 표시',
|
||
sub: 'ESI 중첩\n방제자원 연계',
|
||
color: 'var(--color-info)',
|
||
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-caption"
|
||
style={{
|
||
background: step.bg,
|
||
border: `${step.bold ? '2px' : '1px'} solid ${step.bd}`,
|
||
}}
|
||
>
|
||
<div className="text-title-2 mb-1">{step.icon}</div>
|
||
<div className="font-bold" style={{ color: step.color }}>
|
||
{step.label}
|
||
</div>
|
||
<div className="text-fg-disabled" style={{ whiteSpace: 'pre-line' }}>
|
||
{step.sub}
|
||
</div>
|
||
</div>
|
||
{i < 5 && <div className="px-1.5 text-fg-disabled text-title-3">▶</div>}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* 오일펜스 종류 */}
|
||
<div className="grid grid-cols-3 gap-3 mb-4">
|
||
{[
|
||
{
|
||
icon: '⛽',
|
||
title: '고형 오일펜스',
|
||
color: 'var(--color-accent)',
|
||
desc: '단단한 부체와 수중커튼으로 구성. 정적 배치. 항구·좁은 수로에 적합.',
|
||
specs: [
|
||
'내조류 한계: 0.5~1.0 knot',
|
||
'높이: 30~60cm · 수중 30~60cm',
|
||
'전개속도: 30~60m/hr',
|
||
],
|
||
},
|
||
{
|
||
icon: '🌊',
|
||
title: '공기충전식 오일펜스',
|
||
color: 'var(--color-info)',
|
||
desc: '공기로 부력 확보. 이동·보관 편리. 해상 광역 차단에 주로 사용.',
|
||
specs: [
|
||
'내조류 한계: 0.7~1.5 knot',
|
||
'높이: 45~90cm · 수중 45~90cm',
|
||
'전개속도: 100~300m/hr',
|
||
],
|
||
},
|
||
{
|
||
icon: '🔄',
|
||
title: '자항식 오일펜스',
|
||
color: 'var(--color-accent)',
|
||
desc: '방제정 예인 또는 자체 추진. U형·V형 동적 배치. 강조류 해역 적합.',
|
||
specs: ['운용수심: 5m 이상', 'U형·V형·J형 동적 형태', '내조류: 조류각도 보정으로 극복'],
|
||
},
|
||
].map((boom, i) => (
|
||
<div key={i} className="rounded-md p-3.5 bg-bg-card border border-stroke">
|
||
<div className="text-label-2 font-bold mb-2" style={{ color: boom.color }}>
|
||
{boom.icon} {boom.title}
|
||
</div>
|
||
<div className="text-label-2 mb-2 leading-[1.7] text-fg-sub">{boom.desc}</div>
|
||
<div className="flex flex-col gap-[3px] text-caption text-fg-sub">
|
||
{boom.specs.map((spec, j) => (
|
||
<div
|
||
key={j}
|
||
className="px-[7px] py-[3px] rounded-[3px] text-fg-sub"
|
||
style={{ background: `${boom.color}11` }}
|
||
>
|
||
{spec}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* 핵심 제약조건 */}
|
||
<div className="rounded-md p-3.5 bg-bg-card border border-stroke">
|
||
<div className="text-label-2 font-bold mb-2.5 text-color-caution">
|
||
⚠️ 최적화 핵심 제약조건
|
||
</div>
|
||
<div className="grid grid-cols-3 gap-2.5 text-label-2 text-fg-sub">
|
||
{[
|
||
{
|
||
icon: '🌊',
|
||
title: '조류 제약',
|
||
color: 'var(--color-info)',
|
||
bg: 'rgba(59,130,246,.05)',
|
||
bd: 'rgba(59,130,246,.15)',
|
||
lines: [
|
||
'조류속도 > 임계유속 시',
|
||
'오일펜스 하단 통과 발생',
|
||
'U<0.7 knot 유지 필수',
|
||
'임계각도 자동 계산 적용',
|
||
],
|
||
},
|
||
{
|
||
icon: '📏',
|
||
title: '자원 제약',
|
||
color: 'var(--color-caution)',
|
||
bg: 'rgba(6,182,212,.05)',
|
||
bd: 'rgba(6,182,212,.15)',
|
||
lines: [
|
||
'가용 오일펜스 총 길이',
|
||
'방제정 척수·이동시간',
|
||
'앵커링 가능 수심 조건',
|
||
'연결부 허용 장력',
|
||
],
|
||
},
|
||
{
|
||
icon: '⏱️',
|
||
title: '시간 제약',
|
||
color: 'var(--color-info)',
|
||
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-fg-sub">
|
||
{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-card border border-stroke">
|
||
<div className="text-label-1 font-bold mb-3">
|
||
📐 오일펜스 차단 효율 이론 (Boom Containment Efficiency)
|
||
</div>
|
||
<div className="grid grid-cols-2 gap-3.5">
|
||
<div>
|
||
<div className="text-label-2 font-bold mb-2 text-color-accent">
|
||
① 차단 효율 함수 E(θ, U)
|
||
</div>
|
||
<div className="text-label-2 leading-[1.8] mb-2 text-fg-sub">
|
||
오일펜스의 차단 효율은 <b>조류 유속(U)</b>과 <b>오일펜스 방향각(θ)</b>의 함수입니다.
|
||
조류가 오일펜스에 수직으로 입사할수록 차단 효율이 낮아지며, 임계유속 초과 시 기름이
|
||
오일펜스 하부로 통과합니다.
|
||
</div>
|
||
<div
|
||
className="rounded-md p-2.5 text-label-2 leading-[2.1] bg-bg-base font-mono"
|
||
style={{ border: '1px solid rgba(6,182,212,.2)' }}
|
||
>
|
||
E(θ,U) = 1 −{' '}
|
||
<span className="text-color-info">
|
||
F<sub>loss</sub>(U<sub>n</sub>)
|
||
</span>
|
||
<br />U<sub>n</sub> = U · sin(θ){' '}
|
||
<span className="text-caption text-fg-disabled">(법선방향 유속)</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-caption text-fg-disabled">
|
||
U<sub>c</sub>: 임계유속(약 0.35m/s = 0.7 knot)
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div className="text-label-2 font-bold mb-2 text-color-accent">
|
||
② 최적 방향각 θ* 산정
|
||
</div>
|
||
<div className="text-label-2 leading-[1.8] mb-2 text-fg-sub">
|
||
오일펜스 방향각은 조류 방향에 따라 최적화됩니다. 차단 면적과 오일 수집 효율의{' '}
|
||
<b>트레이드오프</b>를 고려하여, 일반적으로 조류에 대해{' '}
|
||
<b className="text-color-accent">30°~45° 예각 배치</b>가 최적입니다.
|
||
</div>
|
||
<div
|
||
className="rounded-md p-2.5 text-label-2 leading-[2.1] bg-bg-base font-mono"
|
||
style={{ border: '1px solid rgba(6,182,212,.2)' }}
|
||
>
|
||
θ* = arcsin(U<sub>c</sub> / U){' '}
|
||
<span className="text-caption text-fg-disabled">(임계조건)</span>
|
||
<br />θ<sub>opt</sub> = argmax [A<sub>block</sub>(θ) · E(θ,U)]
|
||
<br />
|
||
실용범위: 15° ≤ θ ≤ 60°
|
||
<br />
|
||
<span className="text-caption text-fg-disabled">
|
||
단, θ < 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-card border border-stroke">
|
||
<div className="text-label-1 font-bold mb-3">🔷 오일펜스 배치 형태별 이론</div>
|
||
<div className="grid grid-cols-3 gap-3">
|
||
{/* V형 */}
|
||
<div
|
||
className="rounded-lg p-3.5 bg-bg-base"
|
||
style={{
|
||
border: '1px solid rgba(6,182,212,.2)',
|
||
}}
|
||
>
|
||
<div className="text-label-2 font-bold mb-2 text-color-accent">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(6,182,212,.8)"
|
||
strokeWidth="2.5"
|
||
/>
|
||
<line
|
||
x1="90"
|
||
y1="15"
|
||
x2="50"
|
||
y2="55"
|
||
stroke="rgba(6,182,212,.8)"
|
||
strokeWidth="2.5"
|
||
/>
|
||
<circle cx="50" cy="10" r="4" fill="rgba(6,182,212,.5)" />
|
||
<text
|
||
x="50"
|
||
y="7"
|
||
textAnchor="middle"
|
||
fill="rgba(6,182,212,.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-label-2 leading-[1.7] mb-[7px] text-fg-sub">
|
||
조류 방향 정면에서 양측으로 펼친 V형. 기름을 중앙 집유점으로 유도. 회수선 배치 용이.
|
||
</div>
|
||
<div
|
||
className="rounded-[5px] p-[7px] text-caption leading-[1.9] font-mono"
|
||
style={{ background: 'rgba(6,182,212,.05)' }}
|
||
>
|
||
A<sub>V</sub> = L²·sin(2α)/2
|
||
<br />
|
||
<span className="text-fg-disabled">α: 반개각, L: 편측 길이</span>
|
||
<br />
|
||
최적 α = 30°~45°
|
||
</div>
|
||
</div>
|
||
|
||
{/* U형 */}
|
||
<div
|
||
className="rounded-lg p-3.5 bg-bg-base"
|
||
style={{
|
||
border: '1px solid rgba(6,182,212,.2)',
|
||
}}
|
||
>
|
||
<div className="text-label-2 font-bold mb-2 text-color-accent">U형 (Horseshoe)</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="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(6,182,212,.8)"
|
||
strokeWidth="2.5"
|
||
/>
|
||
<circle cx="50" cy="52" r="4" fill="rgba(6,182,212,.5)" />
|
||
<text
|
||
x="50"
|
||
y="62"
|
||
textAnchor="middle"
|
||
fill="rgba(6,182,212,.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-label-2 leading-[1.7] mb-[7px] text-fg-sub">
|
||
말굽형으로 기름을 완전 포위. 폐쇄형 구조로 회수 효율 최고. 저조류 해역 적합.
|
||
</div>
|
||
<div
|
||
className="rounded-[5px] p-[7px] text-caption leading-[1.9] font-mono"
|
||
style={{ background: 'rgba(6,182,212,.05)' }}
|
||
>
|
||
A<sub>U</sub> = π·r²/2 + 2r·h
|
||
<br />
|
||
<span className="text-fg-disabled">r: 반경, h: 직선부 길이</span>
|
||
<br />
|
||
전제: U < 0.5 knot
|
||
</div>
|
||
</div>
|
||
|
||
{/* J형 */}
|
||
<div
|
||
className="rounded-lg p-3.5 bg-bg-base"
|
||
style={{
|
||
border: '1px solid rgba(6,182,212,.2)',
|
||
}}
|
||
>
|
||
<div className="text-label-2 font-bold mb-2 text-color-accent">J형 (Skimming)</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="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(6,182,212,.8)"
|
||
strokeWidth="2.5"
|
||
/>
|
||
<path
|
||
d="M80,48 Q55,60 30,35"
|
||
fill="none"
|
||
stroke="rgba(6,182,212,.8)"
|
||
strokeWidth="2.5"
|
||
/>
|
||
<circle cx="79" cy="48" r="4" fill="rgba(6,182,212,.5)" />
|
||
<text x="67" y="43" fill="rgba(6,182,212,.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-label-2 leading-[1.7] mb-[7px] text-fg-sub">
|
||
직선+곡선 조합. 기름을 한쪽으로 편향 유도하여 집유. 강조류·연안 배치에 최적.
|
||
</div>
|
||
<div
|
||
className="rounded-[5px] p-[7px] text-caption leading-[1.9] font-mono"
|
||
style={{ background: 'rgba(6,182,212,.05)' }}
|
||
>
|
||
θ<sub>J</sub> = arcsin(U<sub>c</sub>/U) + δ<br />
|
||
<span className="text-fg-disabled">δ: 안전여유각(5°~10°)</span>
|
||
<br />
|
||
활용: U > 0.7 knot
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 다단 배치 이론 */}
|
||
<div className="rounded-md p-3.5 bg-bg-card border border-stroke">
|
||
<div className="text-label-2 font-bold mb-2.5">🔢 다단계 차단선(Multi-Boom) 배치 이론</div>
|
||
<div className="grid grid-cols-2 gap-3">
|
||
<div className="text-label-2 leading-[1.8] text-fg-sub">
|
||
단일 오일펜스로 차단 불가한 경우 <b>직렬 다단 배치</b>로 누적 차단 효율을 향상합니다.
|
||
n개 직렬 배치 시 누적 차단 효율:
|
||
<div className="mt-2 rounded-[5px] p-[9px] leading-[2] bg-bg-base font-mono">
|
||
E<sub>total</sub> = 1 − ∏(1−E<sub>i</sub>)<br />
|
||
<span className="text-caption text-fg-disabled">
|
||
E<sub>i</sub>: i번째 오일펜스 단독 차단효율
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<div className="flex flex-col gap-[5px] text-caption text-fg-sub">
|
||
{[
|
||
{
|
||
color: 'var(--color-accent)',
|
||
bg: 'rgba(6,182,212,.05)',
|
||
bd: 'rgba(6,182,212,.12)',
|
||
label: '2단 직렬',
|
||
text: ': E_total = E₁+E₂−E₁·E₂ (예: 70%+70% → 91%)',
|
||
},
|
||
{
|
||
color: 'var(--color-accent)',
|
||
bg: 'rgba(6,182,212,.05)',
|
||
bd: 'rgba(6,182,212,.12)',
|
||
label: '단간 거리',
|
||
text: ': 부표 집적 방지를 위해 ≥ 200m 이격 권장',
|
||
},
|
||
{
|
||
color: 'var(--color-accent)',
|
||
bg: 'rgba(6,182,212,.05)',
|
||
bd: 'rgba(6,182,212,.12)',
|
||
label: '배치 우선순위',
|
||
text: ': ESI 고등급 구역 보호 → 취수원 → 어항 순',
|
||
},
|
||
{
|
||
color: 'var(--color-accent)',
|
||
bg: 'rgba(6,182,212,.05)',
|
||
bd: 'rgba(6,182,212,.12)',
|
||
label: '조석 변화',
|
||
text: ': 창조·낙조 전환 시 오일펜스 방향 재조정 필요',
|
||
},
|
||
].map((item, i) => (
|
||
<div
|
||
key={i}
|
||
className="px-[9px] py-1.5 rounded-[5px] text-fg-sub"
|
||
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-[10px] p-[14px] mb-4 bg-bg-card border border-stroke">
|
||
<div className="text-title-4 font-bold mb-2">
|
||
다목적 최적화 문제 (Multi-Objective Optimization)
|
||
</div>
|
||
<div className="text-label-2 leading-[1.8] text-fg-sub">
|
||
오일펜스 배치 최적화는 <b className="text-color-accent">상충하는 복수 목적함수</b>를
|
||
동시에 만족해야 하는 전형적인 다목적 최적화 문제입니다. 차단 효율 최대화와 자원 사용
|
||
최소화는 서로 트레이드오프 관계를 가지며,{' '}
|
||
<b className="text-color-accent">파레토 최적(Pareto Optimal) 해집합</b>에서 의사결정자가
|
||
선택합니다.
|
||
</div>
|
||
</div>
|
||
|
||
{/* 목적함수 정의 */}
|
||
<div className="rounded-md p-4 mb-3.5 bg-bg-card border border-stroke">
|
||
<div className="text-label-1 font-bold mb-3">📊 목적함수 및 제약조건 정의</div>
|
||
<div className="grid grid-cols-2 gap-3 mb-3">
|
||
<div
|
||
className="rounded-lg p-3 bg-bg-base"
|
||
style={{ border: '1px solid rgba(6,182,212,.2)' }}
|
||
>
|
||
<div className="text-label-2 font-bold mb-2 text-color-accent">🎯 목적함수 F(x)</div>
|
||
<div
|
||
className="rounded-[5px] p-[9px] text-label-2 leading-[2.2] font-mono"
|
||
style={{ background: 'rgba(6,182,212,.04)' }}
|
||
>
|
||
<b className="text-color-accent">최대화:</b>
|
||
<br />
|
||
f₁(x) = Σ A<sub>blocked,i</sub> · w<sub>ESI,i</sub>{' '}
|
||
<span className="text-caption text-fg-disabled">(가중 차단면적)</span>
|
||
<br />
|
||
f₂(x) = T<sub>deadline</sub> − T<sub>deploy</sub>{' '}
|
||
<span className="text-caption text-fg-disabled">(여유시간)</span>
|
||
<br />
|
||
<b className="text-color-info">최소화:</b>
|
||
<br />
|
||
f₃(x) = Σ L<sub>boom,j</sub>{' '}
|
||
<span className="text-caption text-fg-disabled">(총 오일펜스 사용량)</span>
|
||
<br />
|
||
f₄(x) = Σ D<sub>vessel,k</sub>{' '}
|
||
<span className="text-caption text-fg-disabled">(방제정 총 이동거리)</span>
|
||
</div>
|
||
</div>
|
||
<div
|
||
className="rounded-lg p-3 bg-bg-base"
|
||
style={{ border: '1px solid rgba(59,130,246,.2)' }}
|
||
>
|
||
<div className="text-label-2 font-bold mb-2 text-color-caution">🚫 제약조건 G(x)</div>
|
||
<div
|
||
className="rounded-[5px] p-[9px] text-label-2 leading-[2.2] font-mono"
|
||
style={{ background: 'rgba(59,130,246,.04)' }}
|
||
>
|
||
g₁: U·sin(θ<sub>i</sub>) ≤ U<sub>c</sub> ∀i{' '}
|
||
<span className="text-caption text-fg-disabled">(임계유속)</span>
|
||
<br />
|
||
g₂: Σ L<sub>j</sub> ≤ L<sub>max</sub>{' '}
|
||
<span className="text-caption text-fg-disabled">(자원 한계)</span>
|
||
<br />
|
||
g₃: T<sub>deploy,i</sub> ≤ T<sub>arrive,i</sub>{' '}
|
||
<span className="text-caption text-fg-disabled">(시간 제약)</span>
|
||
<br />
|
||
g₄: d(p<sub>i</sub>, shore) ≥ d<sub>min</sub>{' '}
|
||
<span className="text-caption text-fg-disabled">(연안 이격)</span>
|
||
<br />
|
||
g₅: h(p<sub>i</sub>) ≥ h<sub>min</sub>{' '}
|
||
<span className="text-caption text-fg-disabled">(수심 조건)</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* ESI 가중치 */}
|
||
<div
|
||
className="rounded-lg p-3 bg-bg-base"
|
||
style={{ border: '1px solid rgba(6,182,212,.2)' }}
|
||
>
|
||
<div className="text-label-2 font-bold mb-2 text-color-caution">
|
||
🏖️ ESI 가중치 w<sub>ESI</sub> 설계
|
||
</div>
|
||
<div className="grid grid-cols-5 gap-[5px] text-caption text-fg-sub">
|
||
{[
|
||
{
|
||
grade: 'ESI 1~2',
|
||
desc: '노출암반',
|
||
w: 'w = 0.2',
|
||
color: 'var(--color-accent)',
|
||
bg: 'rgba(6,182,212,.06)',
|
||
},
|
||
{
|
||
grade: 'ESI 3~4',
|
||
desc: '모래해변',
|
||
w: 'w = 0.4',
|
||
color: 'var(--color-accent)',
|
||
bg: 'rgba(6,182,212,.06)',
|
||
},
|
||
{
|
||
grade: 'ESI 5~6',
|
||
desc: '자갈·조약',
|
||
w: 'w = 0.6',
|
||
color: 'var(--color-caution)',
|
||
bg: 'rgba(6,182,212,.06)',
|
||
},
|
||
{
|
||
grade: 'ESI 7~8',
|
||
desc: '갯벌·조간대',
|
||
w: 'w = 0.85',
|
||
color: 'var(--color-accent)',
|
||
bg: 'rgba(6,182,212,.06)',
|
||
},
|
||
{
|
||
grade: 'ESI 9~10',
|
||
desc: '맹그로브·습지',
|
||
w: 'w = 1.0',
|
||
color: 'var(--color-info)',
|
||
bg: 'rgba(59,130,246,.08)',
|
||
bd: 'rgba(59,130,246,.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-fg-disabled">{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-card border border-stroke">
|
||
<div className="text-label-1 font-bold mb-3 text-color-accent">
|
||
🧬 NSGA-II (Non-dominated Sorting Genetic Algorithm II)
|
||
</div>
|
||
<div className="grid grid-cols-2 gap-3">
|
||
<div>
|
||
<div className="text-label-2 leading-[1.8] mb-2 text-fg-sub">
|
||
WING의 오일펜스 배치 최적화는 다목적 유전알고리즘{' '}
|
||
<b className="text-color-accent">NSGA-II</b>(Deb et al., 2002)를 핵심 엔진으로
|
||
사용합니다. 파레토 전면(Pareto Front)을 탐색하여 차단 효율과 자원 효율의 최적 해집합을
|
||
제공합니다.
|
||
</div>
|
||
<div className="flex flex-col gap-1 text-caption text-fg-sub">
|
||
{[
|
||
'염색체 구조 : [배치지점 좌표, 방향각θ, 길이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-fg-sub"
|
||
style={{
|
||
background: 'rgba(6,182,212,.05)',
|
||
border: '1px solid rgba(6,182,212,.12)',
|
||
}}
|
||
>
|
||
<b className="text-color-accent">{item.split(' : ')[0]}</b> :{' '}
|
||
{item.split(' : ')[1]}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div className="text-label-2 font-bold mb-[7px] text-fg-sub">
|
||
NSGA-II 5단계 진화 루프
|
||
</div>
|
||
<div className="flex flex-col gap-[5px] text-caption text-fg-sub">
|
||
{[
|
||
{
|
||
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(6,182,212,.05)' : 'rgba(6,182,212,.04)',
|
||
border: i === 4 ? '1px solid rgba(6,182,212,.12)' : undefined,
|
||
}}
|
||
>
|
||
<span className="min-w-[20px] font-extrabold text-color-accent">{item.step}</span>
|
||
<div className="leading-[1.6] text-fg-sub">
|
||
<b>{item.title}</b> : {item.desc}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 보조 알고리즘 비교 */}
|
||
<div className="rounded-md p-3.5 bg-bg-card border border-stroke">
|
||
<div className="text-label-2 font-bold mb-2.5">🔬 보조 최적화 알고리즘 비교 적용</div>
|
||
<div className="overflow-x-auto">
|
||
<table className="w-full text-label-2 border-collapse">
|
||
<thead>
|
||
<tr
|
||
style={{
|
||
background: 'rgba(255,255,255,.03)',
|
||
borderBottom: '1px solid var(--stroke-light)',
|
||
}}
|
||
>
|
||
{['알고리즘', '유형', '장점', '단점', 'WING 활용'].map((h) => (
|
||
<th
|
||
key={h}
|
||
className="py-[7px] px-2.5 font-semibold text-fg-disabled"
|
||
style={{ textAlign: h === '알고리즘' ? 'left' : 'center' }}
|
||
>
|
||
{h}
|
||
</th>
|
||
))}
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{[
|
||
{
|
||
name: 'NSGA-II',
|
||
color: 'var(--color-accent)',
|
||
type: '다목적 GA',
|
||
pros: '파레토 전면 탐색\n다양성 유지 우수',
|
||
cons: '계산비용 높음\n수렴 느림',
|
||
wing: '메인 엔진',
|
||
wingColor: 'var(--color-accent)',
|
||
},
|
||
{
|
||
name: 'PSO',
|
||
color: 'var(--color-accent)',
|
||
type: '입자군집',
|
||
pros: '빠른 수렴\n구현 단순',
|
||
cons: '조기수렴\n다목적 취약',
|
||
wing: '단일목적 빠른 배치',
|
||
wingColor: 'var(--fg-sub)',
|
||
},
|
||
{
|
||
name: 'SA',
|
||
color: 'var(--color-info)',
|
||
type: '모의담금질',
|
||
pros: '전역 탈출 우수\n국소최적 회피',
|
||
cons: '매개변수 민감\n느린 수렴',
|
||
wing: '긴급 단순 배치',
|
||
wingColor: 'var(--fg-sub)',
|
||
},
|
||
{
|
||
name: 'Greedy+휴리스틱',
|
||
color: 'var(--color-accent)',
|
||
type: '결정론적',
|
||
pros: '즉시 결과\n해석 용이',
|
||
cons: '전역최적 미보장',
|
||
wing: '실시간 초기 제안',
|
||
wingColor: 'var(--color-accent)',
|
||
},
|
||
].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-fg-sub">{row.type}</td>
|
||
<td className="py-[7px] px-2.5 text-center text-fg-sub whitespace-pre-line">
|
||
{row.pros}
|
||
</td>
|
||
<td className="py-[7px] px-2.5 text-center text-fg-sub 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-card border border-stroke">
|
||
<div className="text-label-1 font-bold mb-3">🌊 오일펜스 주변 유동 수치 모델</div>
|
||
<div className="grid grid-cols-2 gap-3.5">
|
||
<div>
|
||
<div className="text-label-2 font-bold mb-2 text-color-info">① 오일펜스 항력 모델</div>
|
||
<div className="text-label-2 leading-[1.8] mb-2 text-fg-sub">
|
||
오일펜스에 작용하는 항력은 조류속도의 제곱에 비례합니다. 오일펜스 구조
|
||
변형(catenary형태)을 고려한 동적 항력 계산.
|
||
</div>
|
||
<div
|
||
className="rounded-md p-2.5 text-label-2 leading-[2.1] bg-bg-base 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-caption text-fg-disabled">
|
||
C<sub>D</sub>: 항력계수(≈1.2), A: 수중 투영면적
|
||
</span>
|
||
<br />
|
||
<span className="text-caption text-fg-disabled">T: 연결부 장력, α: 체인각도</span>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div className="text-label-2 font-bold mb-2 text-color-accent">
|
||
② 기름 통과(Splash-over) 조건
|
||
</div>
|
||
<div className="text-label-2 leading-[1.8] mb-2 text-fg-sub">
|
||
조류 유속이 임계값을 초과하면 기름이 파도를 타고 오일펜스를 넘어가는 Splash-over가
|
||
발생합니다.
|
||
</div>
|
||
<div
|
||
className="rounded-md p-2.5 text-label-2 leading-[2.1] bg-bg-base font-mono"
|
||
style={{ border: '1px solid rgba(6,182,212,.2)' }}
|
||
>
|
||
Fr = U<sub>n</sub> / √(g·Δρ/ρ·h)
|
||
<br />
|
||
Splash-over: Fr > 0.5~0.6
|
||
<br />
|
||
<span className="text-caption text-fg-disabled">
|
||
Fr: 수정 Froude수, h: 오일펜스 수중깊이
|
||
</span>
|
||
<br />
|
||
<span className="text-caption text-fg-disabled">Δρ/ρ: 기름-해수 밀도비 (~0.15)</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Catenary 변형 모델 */}
|
||
<div className="rounded-md p-4 mb-3.5 bg-bg-card border border-stroke">
|
||
<div className="text-label-1 font-bold mb-3">🔗 오일펜스 현수선(Catenary) 변형 모델</div>
|
||
<div className="grid grid-cols-2 gap-3.5">
|
||
<div className="text-label-2 leading-[1.8] text-fg-sub">
|
||
조류와 바람에 의해 오일펜스는 현수선(Catenary) 형태로 변형됩니다. 실제 차단 길이가 설계
|
||
길이보다 짧아지며, 최적화 알고리즘에서 <b>변형 후 유효 차단 길이</b> L<sub>eff</sub>를
|
||
계산합니다.
|
||
<div className="mt-2 rounded-[5px] p-[9px] leading-[2] bg-bg-base 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-caption text-fg-disabled">
|
||
a: catenary 파라미터, φ: 최대 편향각
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<div className="flex flex-col gap-1.5 text-caption text-fg-sub">
|
||
<div className="text-label-2 font-bold mb-1 text-fg-sub">
|
||
변형 단계별 유효 차단 길이 보정
|
||
</div>
|
||
{[
|
||
{
|
||
cond: 'U < 0.3 knot',
|
||
result: 'L_eff ≈ L (직선 유지)',
|
||
bg: 'rgba(6,182,212,.05)',
|
||
bd: 'rgba(6,182,212,.12)',
|
||
},
|
||
{
|
||
cond: '0.3~0.7 knot',
|
||
result: 'L_eff = 0.8~0.95 L (경미 변형)',
|
||
bg: 'rgba(6,182,212,.05)',
|
||
bd: 'rgba(6,182,212,.12)',
|
||
},
|
||
{
|
||
cond: '0.7~1.0 knot',
|
||
result: 'L_eff = 0.5~0.8 L (Catenary 현저)',
|
||
bg: 'rgba(6,182,212,.05)',
|
||
bd: 'rgba(6,182,212,.12)',
|
||
},
|
||
{
|
||
cond: 'U > 1.0 knot',
|
||
result: '기름 통과 위험 · 배치 재계산',
|
||
bg: 'rgba(59,130,246,.05)',
|
||
bd: 'rgba(59,130,246,.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(--color-info)' : 'var(--fg-sub)',
|
||
}}
|
||
>
|
||
{item.cond} → {item.result}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 유막 포집 모델 */}
|
||
<div className="rounded-md p-3.5 bg-bg-card border border-stroke">
|
||
<div className="text-label-2 font-bold mb-2.5">🛢️ 오일펜스 내 유막 포집 동역학</div>
|
||
<div className="grid grid-cols-2 gap-3 text-label-2 text-fg-sub">
|
||
<div>
|
||
<div className="font-bold mb-1.5 text-color-accent">포집 기름 체적 변화율</div>
|
||
<div className="rounded-[5px] p-[9px] leading-[2] bg-bg-base 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-color-accent">최적 회수 타이밍</div>
|
||
<div className="text-label-2 leading-[1.7] text-fg-sub">
|
||
포집 기름 체적이 오일펜스 저장 용량의 70~80%에 도달하면 Skimmer 회수 작업을
|
||
개시합니다. 이를 초과하면 오일 overflow 발생. WING이 실시간 체적 모니터링 후 회수 알람
|
||
발령.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</>
|
||
);
|
||
}
|
||
|
||
/* ──────────── PANEL 4: 현장 적용 ──────────── */
|
||
function FieldApplicationPanel() {
|
||
const steps = [
|
||
{
|
||
num: 1,
|
||
color: 'var(--color-accent)',
|
||
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: 'KOSPS·POSEIDON·OpenDrift 앙상블 예측 결과에서 유출유 확산 경계선(Pollution Boundary)과 각 ESI 구역별 도달 예상시간(T_arrive)을 산출합니다. 신뢰도 70% 이상 예측 경계를 기준으로 차단 전략 영역을 설정합니다.',
|
||
},
|
||
{
|
||
num: 2,
|
||
color: 'var(--color-accent)',
|
||
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(--color-caution)',
|
||
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: '확산 예측 경계를 따라 500m 간격 격자로 후보 배치지점을 생성합니다. 각 지점에서 조류 조건·수심·해안선 이격·방제정 접근 가능성을 동시 검토합니다. ESI 고등급 구역 전방 2km 이내 지점에 우선 가중치를 부여합니다.',
|
||
},
|
||
{
|
||
num: 4,
|
||
color: 'var(--color-accent)',
|
||
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: 'NSGA-II 최적화 실행 — 파레토 최적 배치안 산출',
|
||
desc: '후보 배치지점·방향각·형태 조합을 염색체로 인코딩하여 NSGA-II 다목적 유전알고리즘을 실행합니다. 수렴 후 파레토 전면에서 3~5개 추천 배치안을 제시하며, 의사결정자가 차단 효율과 자원 사용량의 트레이드오프를 보고 선택합니다.',
|
||
},
|
||
{
|
||
num: 5,
|
||
color: 'var(--color-accent)',
|
||
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: '창조→낙조 전환 시(약 6시간 주기) 조류 방향 역전에 따른 오일펜스 재배치 알람을 발령합니다. 확산 예측이 갱신될 때마다 배치 최적화를 자동 재실행하여 방제대응 체계를 동적으로 업데이트합니다.',
|
||
},
|
||
];
|
||
|
||
return (
|
||
<>
|
||
{/* 배치 5단계 절차 */}
|
||
<div className="rounded-md p-4 mb-3.5 bg-bg-card border border-stroke">
|
||
<div className="text-label-1 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-body-2 flex-shrink-0"
|
||
style={{
|
||
background: step.numBg,
|
||
border: `1px solid ${step.numBd}`,
|
||
color: step.color,
|
||
}}
|
||
>
|
||
{step.num}
|
||
</div>
|
||
<div className="text-label-2 text-fg-sub">
|
||
<div className="font-bold mb-1">{step.title}</div>
|
||
<div className="leading-[1.7] text-fg-sub">{step.desc}</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* 해역별 적용 특성 */}
|
||
<div className="rounded-md p-3.5 bg-bg-card border border-stroke">
|
||
<div className="text-label-2 font-bold mb-2.5">📍 해역별 적용 특성 및 전략</div>
|
||
<div className="grid grid-cols-3 gap-2.5 text-caption text-fg-sub">
|
||
{[
|
||
{
|
||
icon: '🌊',
|
||
title: '서해 (조차 대형)',
|
||
color: 'var(--color-info)',
|
||
bg: 'rgba(59,130,246,.05)',
|
||
bd: 'rgba(59,130,246,.12)',
|
||
desc: '최대 조차 9m (인천), 조류 최대 3~5 knot. J형 배치 주력. 조석 전환 재배치 필수. 앵커링 수심 급변화 주의.',
|
||
},
|
||
{
|
||
icon: '🌿',
|
||
title: '남해 (다도해)',
|
||
color: 'var(--color-accent)',
|
||
bg: 'rgba(6,182,212,.05)',
|
||
bd: 'rgba(6,182,212,.12)',
|
||
desc: '복잡한 해안선·섬. 조류 1~2 knot. V형·U형 복합 배치. 좁은 수로 통제 우선. ESI 고등급 갯벌 보호.',
|
||
},
|
||
{
|
||
icon: '🏔️',
|
||
title: '동해 (심해형)',
|
||
color: 'var(--color-accent)',
|
||
bg: 'rgba(6,182,212,.05)',
|
||
bd: 'rgba(6,182,212,.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-fg-sub">{area.desc}</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</>
|
||
);
|
||
}
|
||
|
||
/* ──────────── PANEL 5: 참고문헌 ──────────── */
|
||
function ReferencesPanel() {
|
||
const categories = [
|
||
{
|
||
title: '⚙️ 최적화 알고리즘',
|
||
color: 'var(--color-accent)',
|
||
bg: 'rgba(6,182,212,.1)',
|
||
bd: 'rgba(6,182,212,.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(--color-info)',
|
||
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(--color-accent)',
|
||
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(--color-accent)',
|
||
bg: 'rgba(6,182,212,.1)',
|
||
bd: 'rgba(6,182,212,.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-label-1 font-bold mb-1">📚 오일펜스 배치 최적화 이론 근거 문헌</div>
|
||
<div className="text-label-2 mb-3.5 text-fg-disabled">총 12편 · 4개 카테고리</div>
|
||
|
||
{categories.map((cat, ci) => (
|
||
<div key={ci} className="mb-4">
|
||
<div
|
||
className="text-label-2 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-caption text-fg-sub">
|
||
{cat.refs.map((ref, ri) => (
|
||
<div
|
||
key={ri}
|
||
className="p-[9px] px-3 rounded-[7px] grid gap-2"
|
||
style={{
|
||
gridTemplateColumns: '24px 1fr',
|
||
background: 'var(--bg-card)',
|
||
border: ref.highlight ? `1px solid ${cat.bd}` : '1px solid var(--stroke-default)',
|
||
}}
|
||
>
|
||
<div
|
||
className="w-[22px] h-[22px] rounded flex items-center justify-center text-label-2 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-fg-disabled">{ref.author}</div>
|
||
<div className="mt-0.5 text-fg-sub">{ref.desc}</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</>
|
||
);
|
||
}
|