wing-ops/frontend/src/tabs/prediction/components/OilSpillTheoryView.tsx

3389 lines
155 KiB
TypeScript
Executable File
Raw Blame 히스토리

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState, useRef } from 'react';
import { sanitizeHtml } from '@common/utils/sanitize';
const theoryTabs: { id: number; icon: string; name: string; nameColor?: string }[] = [
{ id: 0, icon: '🌊', name: '시스템 개요' },
{ id: 7, icon: '🔷', name: 'KOSPS', nameColor: '#06b6d4' },
{ id: 8, icon: '🔴', name: 'POSEIDON', nameColor: '#ef4444' },
{ id: 9, icon: '🔵', name: 'OpenDrift', nameColor: '#3b82f6' },
{ id: 1, icon: '🧭', name: '입자추적법' },
{ id: 2, icon: '🔁', name: '풍화 프로세스' },
{ id: 3, icon: '🌊', name: '해양환경 입력' },
{ id: 4, icon: '✅', name: '모델 검증' },
{ id: 5, icon: '⚡', name: '앙상블', nameColor: '#a855f7' },
{ id: 6, icon: '🚀', name: '발전 방향' },
];
export function OilSpillTheoryView() {
const [activePanel, setActivePanel] = useState(0);
const contentRef = useRef<HTMLDivElement>(null);
const handleExportPDF = () => {
if (!contentRef.current) return;
// 컨텐츠를 복제하고 PDF 버튼 제거
const clone = contentRef.current.cloneNode(true) as HTMLElement;
clone.querySelectorAll('[data-html2pdf-ignore]').forEach((el) => el.remove());
// XSS 방지: innerHTML을 살균 처리
const content = sanitizeHtml(clone.innerHTML);
// 현재 페이지의 모든 스타일시트를 복사
const styles = Array.from(document.querySelectorAll('style, link[rel="stylesheet"]'))
.map((el) => el.outerHTML)
.join('\n');
// document.write 대신 Blob URL 사용 (보안 강화)
const fullHtml = `<!DOCTYPE html>
<html><head><meta charset="utf-8"/>
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:;">
<title>유출유 확산 모델 이론 및 검증</title>
${styles}
<style>
:root {
--t1: #ffffff !important;
--t2: #d0d6e6 !important;
--t3: #a8b0c8 !important;
}
@media print {
@page { size: A4; margin: 10mm; }
body { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; }
}
body { background: var(--bg-base); color: var(--fg-default); font-family: 'Noto Sans KR', sans-serif; padding: 20px 24px; }
</style>
</head><body>${content}</body></html>`;
const blob = new Blob([fullHtml], { type: 'text/html; charset=utf-8' });
const url = URL.createObjectURL(blob);
const win = window.open(url, '_blank');
if (win) {
win.addEventListener('afterprint', () => URL.revokeObjectURL(url));
setTimeout(() => {
win.document.title = '유출유_확산모델_이론_및_검증';
win.print();
}, 500);
}
setTimeout(() => URL.revokeObjectURL(url), 30000);
};
return (
<div className="flex flex-col h-full overflow-hidden bg-bg-base">
<div
className="flex-1 overflow-y-auto scrollbar-thin p-5"
ref={contentRef}
style={{ scrollbarGutter: 'stable' }}
>
{/* 헤더 */}
<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 text-fg"> </div>
<div className="flex items-center gap-2.5 mt-1 flex-wrap">
<span className="text-label-2 font-medium text-fg-sub">🔷 KOSPS</span>
<span className="text-label-2 font-medium text-fg-sub">🔴 POSEIDON</span>
<span className="text-label-2 font-medium text-fg-sub">🔵 OpenDrift</span>
<span className="text-label-2 font-medium text-fg-sub"> </span>
<span className="text-label-2 text-fg-default"> </span>
</div>
</div>
</div>
<button
onClick={handleExportPDF}
data-html2pdf-ignore
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="flex gap-0.5 rounded-lg p-1 mb-5 bg-bg-card border border-stroke">
{theoryTabs.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-default hover:text-fg-sub'
}`}
>
{tab.icon} {tab.name}
</button>
))}
</div>
{activePanel === 0 && <SystemOverviewPanel />}
{activePanel === 7 && <KospsPanel />}
{activePanel === 8 && <PoseidonPanel />}
{activePanel === 9 && <OpenDriftPanel />}
{activePanel === 1 && <LagrangianPanel />}
{activePanel === 2 && <WeatheringPanel />}
{activePanel === 3 && <OceanInputPanel />}
{activePanel === 4 && <VerificationPanel />}
{activePanel === 5 && <EnsemblePanel />}
{activePanel === 6 && <RoadmapPanel />}
</div>
</div>
);
}
/* ═══ 공통 스타일 유틸 ═══ */
const card = 'rounded-[10px] p-[14px] mb-4';
const cardBg = 'bg-bg-card border border-stroke';
const labelStyle = (color: string) =>
({ fontSize: 'var(--font-size-title-3)', fontWeight: 700, color, marginBottom: '10px' }) as const;
const bodyText = 'text-label-1 text-fg-sub leading-[1.8]';
const codeBox = 'bg-bg-base rounded-sm p-[10px] font-mono text-label-1 leading-loose';
const tag = (color: string) =>
({
padding: '3px 8px',
borderRadius: '4px',
fontSize: 'var(--font-size-label-2)',
color,
background: `${color}14`,
border: `1px solid ${color}30`,
}) as const;
/* ═══ 패널 0: 시스템 개요 ═══ */
function SystemOverviewPanel() {
return (
<div>
{/* 시스템 개요 */}
<div className={`${card} ${cardBg}`}>
{/* 1행: 유출유 확산 모델이란? + 활용 목적 */}
<div className="grid grid-cols-2 gap-4 mb-4">
<div>
<div className="text-title-3 font-bold text-fg mb-2">📋 ?</div>
<div className="text-label-1 text-fg-sub leading-[1.8]">
<b> </b> ··
· .{' '}
<b> </b> ·· .
</div>
</div>
<div>
<div className="text-title-3 font-bold text-fg mb-2">🎯 </div>
<div className="flex flex-col gap-1.5 text-label-1 text-fg-sub">
<div className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-md bg-bg-base">
<span className="text-fg font-bold"></span> {' '}
<b> </b>
</div>
<div className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-md bg-bg-base">
<span className="text-fg font-bold"></span> <b> </b>
</div>
<div className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-md bg-bg-base">
<span className="text-fg font-bold"></span> <b> </b>{' '}
</div>
</div>
</div>
</div>
{/* 2행: 해양환경 데이터 수집 + 유류 물성 반영 + 수치 시뮬레이션 */}
<div className="grid grid-cols-3 gap-4">
<div>
<div className="text-title-3 font-bold text-fg mb-2">🌊 </div>
<div className="text-label-2 mb-2.5 text-fg-sub leading-[1.7]">
<b> · </b> .
</div>
<div className="flex flex-col gap-1 text-label-2 text-fg-sub">
<div className="px-2 py-1 rounded bg-bg-base">🌬 (KMA·ECMWF)</div>
<div className="px-2 py-1 rounded bg-bg-base">🌊 · (KHOA )</div>
<div className="px-2 py-1 rounded bg-bg-base">🌡 · (NIFS )</div>
</div>
</div>
<div>
<div className="text-title-3 font-bold text-fg mb-2">🧮 </div>
<div className="text-label-2 mb-2.5 text-fg-sub leading-[1.7]">
<b> </b> ·· .
</div>
<div className="flex flex-col gap-1 text-label-2 text-fg-sub">
<div className="px-2 py-1 rounded bg-bg-base">📍 ~ </div>
<div className="px-2 py-1 rounded bg-bg-base"> 1 </div>
<div className="px-2 py-1 rounded bg-bg-base">🗺 · </div>
</div>
</div>
<div>
<div className="text-title-3 font-bold text-fg mb-2">🛢 </div>
<div className="text-label-2 mb-2.5 text-fg-sub leading-[1.7]">
<b>· </b> .
</div>
<div className="flex flex-wrap gap-1">
{['비중(API)', '점도', '증발률', '용해도', '인화점', '유화특성'].map((label) => (
<span
key={label}
className="px-2 py-0.5 rounded text-label-2 text-fg-sub bg-bg-base"
>
{label}
</span>
))}
</div>
</div>
</div>
</div>
{/* 모델 비교 카드 */}
<div className={`${card} ${cardBg}`}>
<div className="flex items-center justify-between mb-3.5">
<div style={labelStyle('var(--fg-default)')}>🤖 WING </div>
<span className="text-label-2 text-fg-default">3 · </span>
</div>
<div className="grid grid-cols-3 gap-2.5 mb-3.5">
{[
{
name: 'KOSPS',
sub: 'Korea Oil Spill Prediction System',
color: 'var(--color-info)',
icon: '🔷',
desc: '한국 해역 특화 상시 운용 체계. CHARRY 조류모델 + fBm 난류확산 + 풍화 5단계로 구성된 국산 유출유 확산 시스템.',
org: '한국해양과학기술원(KIOST)',
year: '2011년 (운용체계), 특허 2015년',
strength: '한국 서해·남해 조류 정확도',
weakness: '3D 수직혼합 미반영',
},
{
name: 'POSEIDON',
sub: '입자추적 최적화 예측 시스템',
color: 'var(--color-info)',
icon: '🔵',
desc: '뜰개(Drifter) 관측 경로 기반 매개변수 자동최적화. MOHID 3D 해양순환모델과 진화알고리즘을 결합한 차세대 시스템.',
org: '한국환경연구원, (주)아라종합기술, 한국해양기상기술',
year: '특허: 등록 10-1868791 (2018)',
strength: 'GA/DE/PSO 자동 매개변수 최적화',
weakness: '실시간 뜰개 운용 필요',
},
{
name: 'OpenDrift',
sub: 'Python 기반 오픈소스 라그랑지안 프레임워크',
color: 'var(--color-info)',
icon: '🔵',
desc: '노르웨이 기상청 개발 오픈소스. OpenOil 모듈로 유출유 거동 모의. 전 세계 해양모델(NEMO·ROMS·HYCOM)과 범용 연동.',
org: '노르웨이 기상청(MET Norway)',
year: '논문: Dagestad et al., GMD 11, 2018',
strength: '오픈소스·모듈 확장·3D 지원',
weakness: '한국 해역 조류 정밀도',
},
].map((m) => (
<div
key={m.name}
className="rounded-[10px] p-3.5"
style={{
background: 'var(--bg-base)',
border: `1px solid ${m.color}40`,
}}
>
<div className="flex items-center gap-2 mb-2.5">
<div
className="w-7 h-7 rounded-md flex items-center justify-center text-title-3"
style={{ background: `${m.color}25` }}
>
{m.icon}
</div>
<div>
<div className="text-label-1 font-bold" style={{ color: m.color }}>
{m.name}
</div>
<div className="text-label-2 text-fg-default">{m.sub}</div>
</div>
</div>
<div className="text-label-2 mb-2 text-fg-sub leading-[1.7]">{m.desc}</div>
<div className="flex flex-col gap-0.5 text-label-2 text-fg-sub">
<div
className="px-1.5 py-0.5 rounded"
style={{ background: `${m.color}0F`, color: 'var(--fg-sub)' }}
>
📍 : {m.org}
</div>
<div
className="px-1.5 py-0.5 rounded"
style={{ background: `${m.color}0F`, color: 'var(--fg-sub)' }}
>
🗓 {m.year}
</div>
<div
className="px-1.5 py-0.5 rounded"
style={{ background: `${m.color}0F`, color: 'var(--fg-sub)' }}
>
🎯 : {m.strength}
</div>
<div
className="px-1.5 py-0.5 rounded"
style={{ background: 'rgba(59,130,246,.06)', color: 'var(--fg-sub)' }}
>
: {m.weakness}
</div>
</div>
</div>
))}
</div>
{/* 상세 비교 테이블 */}
<div className="overflow-x-auto">
<table
className="w-full border-collapse"
style={{ fontSize: 'var(--font-size-label-2)' }}
>
<thead>
<tr
style={{
background: 'rgba(255,255,255,.03)',
borderBottom: '1px solid var(--stroke-light)',
}}
>
<th
className="py-2 px-3 text-left text-fg-default font-medium"
style={{ width: '15%' }}
>
</th>
<th
className="py-2 px-3 text-center text-color-info font-bold"
style={{ width: '28%' }}
>
🔷 KOSPS
</th>
<th
className="py-2 px-3 text-center text-color-info font-bold"
style={{ width: '28%' }}
>
🔵 POSEIDON
</th>
<th
className="py-2 px-3 text-center text-color-info font-bold"
style={{ width: '29%' }}
>
🔵 OpenDrift
</th>
</tr>
</thead>
<tbody>
{[
{
label: '개발기관',
k: '한국해양과학기술원<br><span style="font-size:9px;color:var(--fg-disabled)">(KIOST, 구 KORDI)</span>',
p: '한국환경연구원<br>(주)아라종합기술<br>한국해양기상기술<br><span style="font-size:9px;color:var(--fg-disabled)">컨소시엄</span>',
o: '노르웨이 기상청<br><span style="font-size:9px;color:var(--fg-disabled)">(MET Norway · OpenOil)</span>',
},
{
label: '핵심 근거<br><span style="font-size:9px">(논문·특허)</span>',
k: '<div style="margin-bottom:3px;color:var(--color-accent)">① 등록특허 10-1567431 (2015)</div><div style="color:var(--fg-disabled)">이문진·김혜진 외, 유출유 확산 예측 방법</div><div style="margin-top:3px;color:var(--color-accent)">② 해양환경안전학회지 17(4) (2011)</div><div style="color:var(--fg-disabled)">김혜진·이문진 외, KOSPS 상시 운용 체계</div><div style="margin-top:3px;color:var(--color-accent)">③ 해양환경안전학회 춘계 발표 (2008)</div><div style="color:var(--fg-disabled)">이문진 외, CHARRY 조류모델 실시간 적용</div>',
p: '<div style="margin-bottom:3px;color:var(--color-info)">① 등록특허 10-1868791 (2018)</div><div style="color:var(--fg-disabled)">김도연·김충기 외, 입자추적 최적화 방법</div><div style="margin-top:3px;color:var(--color-info)">② 한국지구과학회지 39(5) (2018)</div><div style="color:var(--fg-disabled)">이재호·임병준·김도연 외, MOHID 동아시아 검증</div><div style="margin-top:3px;color:var(--color-info)">③ Ocean Science Journal (2019)</div><div style="color:var(--fg-disabled)">김충기 외, 한반도 인근 해역 POSEIDON 성능 평가</div>',
o: '<div style="margin-bottom:3px;color:var(--color-accent)">① Geosci. Model Dev. 11 (2018)</div><div style="color:var(--fg-disabled)">Dagestad et al., OpenDrift v1.0</div><div style="margin-top:3px;color:var(--color-accent)">② J. Geophys. Res. Oceans (2013)</div><div style="color:var(--fg-disabled)">Röhrs et al., OpenOil 유출유 검증</div><div style="margin-top:3px;color:var(--color-accent)">③ Mar. Pollut. Bull. (2019)</div><div style="color:var(--fg-disabled)">Nordam et al., 3D 유출유 수직분포 모의</div>',
},
{
label: '추적 방식',
k: '라그랑지안 입자추적<br><span style="font-size:9px;color:var(--fg-disabled)">Monte Carlo + fBm 난류확산</span>',
p: '라그랑지안 입자추적<br><span style="font-size:9px;color:var(--fg-disabled)">뜰개 동화 최적화</span>',
o: '라그랑지안 입자추적<br><span style="font-size:9px;color:var(--fg-disabled)">2D/3D 선택적 운용</span>',
},
{
label: '조류 예측',
k: '<span style="color:var(--color-accent);font-weight:600">CHARRY 모델</span><br><span style="font-size:9px;font-weight:400">조화분석+수치모델 결합</span>',
p: '<span style="color:var(--color-info);font-weight:600">MOHID 조석모듈</span><br><span style="font-size:9px;font-weight:400">3D 완전 연산</span>',
o: '<span style="color:var(--color-accent);font-weight:600">외부 모델 입력</span><br><span style="font-size:9px;font-weight:400">NEMO·ROMS·HYCOM 등</span>',
},
{
label: '취송류',
k: '이문진·강용균(2000) 경험식<br>V=0.029×Vw, θ=θw+18.6°',
p: 'MOHID 바람응력 직접 계산<br>Drag coefficient 적용',
o: 'wind_drift_factor 설정<br>기본 3% (OpenOil 표준)',
},
{
label: '해양 강제력',
k: '<span style="color:var(--color-accent)">HYCOM 해류·KMA UM 바람</span><br>NGSST 수온·검조소 조위',
p: '<span style="color:var(--color-info)">MOHID 내부 계산</span><br>KMA/ECMWF 기상 입력',
o: '<span style="color:var(--color-accent)">다중 모델 지원</span><br>NorESM·TOPAZ·GFS 등',
},
{
label: '수심 격자',
k: '15초 등간격 (약 463m)<br>3,225,600 격자',
p: '동아시아 9km<br>한반도 3km Nesting',
o: '입력 모델 해상도 종속<br>~1/12° (약 9km)',
},
{
label: '매개변수\n최적화',
k: '취송류 경험식 계수<br>수동 보정',
p: '<span style="color:var(--color-info);font-weight:600;font-size:10px">GA · DE · HS · PSO<br>뜰개 관측 자동 최적화</span>',
o: '설정파일 기반<br>수동 또는 DA 연동',
},
{
label: '풍화 모델',
k: '<span style="color:var(--color-accent);font-weight:600">5단계 통합</span><br>퍼짐(Fay 1969)·증발(Stiver&amp;Mackay)<br>소산·유상화(Mackay 1980)·침강',
p: '<span style="color:var(--color-info);font-weight:600">물리기반 전산</span><br>MOHID Lagrangian 모듈<br>유류 물성 DB 연동',
o: '<span style="color:var(--color-accent);font-weight:600">OpenOil 표준</span><br>NOAA ADIOS2 유류 DB<br>2000종+ 유류 물성 지원',
},
{
label: '난류 확산',
k: 'fBm 기반<br>σ²=At<sup>m</sup> (m≈2H, 0.45~2.46)',
p: 'Stochastic 확산<br>확산계수 실측 보정',
o: 'Euler-Maruyama 기법<br>수평/수직 확산계수 설정',
},
{
label: '3D 지원',
k: '<span style="font-size:9px;padding:2px 6px;border-radius:3px;background:rgba(59,130,246,.1);color:var(--color-info);font-weight:700">미지원 (2D)</span>',
p: '<span style="font-size:9px;padding:2px 6px;border-radius:3px;background:rgba(59,130,246,.12);color:var(--color-info);font-weight:700">지원 (3D)</span>',
o: '<span style="font-size:9px;padding:2px 6px;border-radius:3px;background:rgba(6,182,212,.12);color:var(--color-accent);font-weight:700">지원 (3D)</span>',
},
{
label: '유류 DB',
k: '국내 주요 유종<br>맞춤 물성 파라미터',
p: '국내외 주요 유종<br>물성 DB 구축',
o: '<span style="color:var(--color-accent);font-weight:600">NOAA ADIOS2 연동<br>2,000종+ 광범위 지원</span>',
},
{
label: '오픈소스',
k: '<span style="font-size:9px;padding:2px 6px;border-radius:3px;background:rgba(59,130,246,.1);color:var(--color-info);font-weight:700">비공개</span>',
p: '<span style="font-size:9px;padding:2px 6px;border-radius:3px;background:rgba(59,130,246,.1);color:var(--color-info);font-weight:700">비공개</span>',
o: '<span style="font-size:9px;padding:2px 6px;border-radius:3px;background:rgba(6,182,212,.12);color:var(--color-accent);font-weight:700">완전 오픈소스</span>',
},
{
label: '예측 신뢰\n기간',
k: '조류: 무제한<br>해류·기상: <b style="color:var(--color-caution)">72h</b> 한계',
p: '최대 <b style="color:var(--color-caution)">72h</b><br>기상 입력 해상도 종속',
o: '최대 <b style="color:var(--color-caution)">72h~</b><br>입력 모델 기간 종속',
},
{
label: '앙상블',
k: '<span style="font-size:9px;padding:2px 6px;border-radius:3px;background:rgba(6,182,212,.12);color:var(--color-accent);font-weight:700">지원</span>',
p: '<span style="font-size:9px;padding:2px 6px;border-radius:3px;background:rgba(59,130,246,.12);color:var(--color-info);font-weight:700">지원</span>',
o: '<span style="font-size:9px;padding:2px 6px;border-radius:3px;background:rgba(6,182,212,.12);color:var(--color-accent);font-weight:700">지원</span>',
},
{
label: 'WING 활용\n역할',
k: '<span style="color:var(--color-accent);font-weight:600">한국 서해·남해<br>조류 정밀 예측</span><br><span style="color:var(--fg-disabled)">ESI 방제지도 연동</span>',
p: '<span style="color:var(--color-info);font-weight:600">매개변수 최적화<br>정확도 향상</span><br><span style="color:var(--fg-disabled)">뜰개 관측 동화</span>',
o: '<span style="color:var(--color-accent);font-weight:600">광역·3D 확산<br>다중 시나리오</span><br><span style="color:var(--fg-disabled)">오픈소스 모듈 확장</span>',
},
].map((row, i) => (
<tr
key={row.label}
style={{
borderBottom: '1px solid rgba(255,255,255,.04)',
background: i % 2 ? 'rgba(255,255,255,.01)' : 'transparent',
}}
>
<td
className="py-[7px] px-3 text-fg-default"
dangerouslySetInnerHTML={{
__html: sanitizeHtml(row.label.replace(/\n/g, '<br>')),
}}
/>
<td
style={{
padding: '7px 12px',
textAlign: 'center',
fontSize: 'var(--font-size-label-2)',
}}
dangerouslySetInnerHTML={{ __html: sanitizeHtml(row.k) }}
/>
<td
style={{
padding: '7px 12px',
textAlign: 'center',
fontSize: 'var(--font-size-label-2)',
}}
dangerouslySetInnerHTML={{ __html: sanitizeHtml(row.p) }}
/>
<td
style={{
padding: '7px 12px',
textAlign: 'center',
fontSize: 'var(--font-size-label-2)',
}}
dangerouslySetInnerHTML={{ __html: sanitizeHtml(row.o) }}
/>
</tr>
))}
</tbody>
</table>
</div>
{/* 앙상블 운용 설명 */}
<div
className="mt-3.5 px-3 py-2.5 rounded-lg"
style={{ background: 'rgba(6,182,212,.04)', border: '1px solid rgba(6,182,212,.15)' }}
>
<div className="text-label-2 text-fg-sub leading-[1.7]">
<b className="text-color-accent">3 </b> : WING
KOSPS·POSEIDON·OpenDrift를 ·. 3
<b> </b>, {' '}
<b className="text-color-accent"> </b>
.
</div>
</div>
</div>
</div>
);
}
/* ═══ 패널 7: KOSPS 상세 ═══ */
function KospsPanel() {
const [papersOpen, setPapersOpen] = useState(false);
const [intlPapersOpen, setIntlPapersOpen] = useState(false);
const [kospsPapersOpen, setKospsPapersOpen] = useState(false);
return (
<div>
<div className="rounded-[10px] p-[14px] mb-4 bg-bg-card border border-stroke">
<div className="flex items-center gap-3 mb-3">
<div className="w-[38px] h-[38px] rounded-[10px] flex items-center justify-center text-heading-3 bg-bg-elevated border border-stroke">
🔷
</div>
<div>
<div className="text-title-2 font-bold text-fg">
KOSPS (Korea Oil Spill Prediction System)
</div>
<div className="text-label-2 mt-0.5 text-fg-default">
(KORDI) ·
</div>
</div>
</div>
<div className="text-label-2 text-fg-sub leading-[1.8]">
···(2011) KOSPS는
<b className="text-color-accent"> </b>
. (·) (KMA ·HYCOM ·KORDI )
FTP <b className="text-color-accent"> </b>
.
</div>
<div
className="mt-2.5 px-3 py-2 rounded-md text-label-2"
style={{
background: 'rgba(6,182,212,.06)',
border: '1px solid rgba(6,182,212,.15)',
color: 'var(--fg-disabled)',
}}
>
참고문헌: 김혜진···, &quot;
&quot;, 17 4, pp.375-382, 2011.
</div>
</div>
{/* 논문·특허 근거 */}
<div className={`${card} ${cardBg}`}>
<div style={labelStyle('var(--fg-default)')}> · </div>
{/* 등록특허 라벨 */}
<div className="flex items-center gap-1.5 mb-2">
<span
style={{
...tag('var(--fg-sub)'),
fontWeight: 700,
fontSize: 'var(--font-size-label-1)',
}}
>
</span>
</div>
<div className="flex flex-col gap-1.5 mb-3.5">
{/* 특허 1 */}
<div className="rounded-lg p-3 bg-bg-base border border-stroke flex gap-3 items-start">
<div className="px-2.5 py-1.5 rounded-md text-center whitespace-nowrap bg-bg-elevated border border-stroke font-mono shrink-0">
<div className="text-label-2 text-fg-default"></div>
<div className="text-label-2 font-bold text-fg">10-1567431</div>
<div className="text-label-2 mt-0.5 text-fg-default">2015.11.03</div>
</div>
<div className="flex flex-col gap-1.5 text-label-2 min-w-0">
<div className="font-bold text-fg">
</div>
<div className="text-fg-sub">
발명자 : 이문진 · · · | 특허권자 : 한국해양과학기술원
</div>
<div className="flex flex-wrap gap-1">
{[
'ESI 방제정보지도',
'CHARRY 실시간 조류모델',
'취송류 경험식',
'Monte Carlo 입자추적',
'풍화 5단계',
].map((t) => (
<span
key={t}
className="px-2 py-0.5 rounded text-label-2 text-fg-sub bg-bg-elevated"
>
{t}
</span>
))}
</div>
<div className="text-fg-default">
R&amp;D: 3 ( 65%) HNS
( 35%) |
</div>
</div>
</div>
</div>
</div>
{/* CHARRY 모델 */}
<div className={`${card} ${cardBg}`}>
<div style={labelStyle('var(--fg-default)')}>
CHARRY ( )
</div>
<div className="text-label-2 mb-2.5 text-fg-sub leading-[1.8]">
<b>CHARRY</b>
(Modulated Tide) ,
.
</div>
<div className="grid grid-cols-2 gap-2.5">
<div className={codeBox}>
<span className="text-fg-default text-label-2">/* 변조조석 수식 */</span>
<br />
ζ(t) = A(t) cos[σt θ(t)]
<br />
A²(t) = Σ Yᵢ² + 2Σ YᵢYⱼ cos[(σσ)t(φφ)]
</div>
<div className="flex flex-col gap-1.5 text-label-2 text-fg-sub">
<div
className="px-2 py-1.5 rounded"
style={{ background: 'rgba(6,182,212,.05)', border: '1px solid rgba(6,182,212,.12)' }}
>
<span className="text-fg font-bold"> </span>
<br />
<span className="text-fg-sub"> f배 f배 </span>
</div>
<div
className="px-2 py-1.5 rounded"
style={{
background: 'rgba(6,182,212,.05)',
border: '1px solid rgba(6,182,212,.12)',
}}
>
<span className="text-fg font-bold"> </span>
<br />
<span className="text-fg-sub">- </span>
</div>
</div>
</div>
</div>
{/* 입력자료 체계 */}
<div className="grid grid-cols-2 gap-3 mb-4">
<div className={`${card} ${cardBg}`} style={{ margin: 0 }}>
<div style={labelStyle('var(--color-accent)')}> </div>
<div className="flex flex-col gap-1.5 text-label-2 text-fg-sub">
{[
{
icon: '🌬️',
label: '바람·기온',
detail: 'KMA UM · ~12km · 2회/일',
bg: 'rgba(6,182,212,.04)',
bd: 'rgba(6,182,212,.12)',
},
{
icon: '🌊',
label: '해류(표층)',
detail: 'HYCOM · ~9km · 1회/일',
bg: 'rgba(59,130,246,.04)',
bd: 'rgba(59,130,246,.12)',
},
{
icon: '🌀',
label: '조류',
detail: 'KORDI 조화분석 · 500m',
bg: 'rgba(6,182,212,.04)',
bd: 'rgba(6,182,212,.12)',
},
{
icon: '🌡️',
label: '표층수온(SST)',
detail: 'NOAA AVHRR · ~5.4km',
bg: 'rgba(6,182,212,.04)',
bd: 'rgba(6,182,212,.12)',
},
{
icon: '💨',
label: '취송류(풍성류)',
detail: 'KMA 바람 → 경험식 계산',
bg: 'rgba(6,182,212,.04)',
bd: 'rgba(6,182,212,.12)',
},
].map((d) => (
<div
key={d.label}
className="px-2.5 py-1.5 rounded-md flex justify-between items-center"
style={{ background: d.bg, border: `1px solid ${d.bd}` }}
>
<span className="font-medium">
{d.icon} {d.label}
</span>
<span className="text-label-2 text-fg-default">{d.detail}</span>
</div>
))}
</div>
</div>
<div className={`${card} ${cardBg}`} style={{ margin: 0 }}>
<div style={labelStyle('var(--color-info)')}> </div>
<div className="flex flex-col gap-1.5 text-label-2 text-fg-sub">
<div
className="px-2.5 py-1.5 rounded-md"
style={{ background: 'rgba(6,182,212,.04)', border: '1px solid rgba(6,182,212,.12)' }}
>
<div className="font-medium mb-0.5">📍 ·</div>
<div className="text-fg-default">(ENC) 500m </div>
</div>
<div
className="px-2.5 py-1.5 rounded-md"
style={{ background: 'rgba(6,182,212,.04)', border: '1px solid rgba(6,182,212,.12)' }}
>
<div className="font-medium mb-0.5">🗺 </div>
<div className="text-fg-default"> </div>
</div>
</div>
</div>
</div>
{/* 취송류 경험식 */}
<div className={`${card} ${cardBg}`}>
<div style={labelStyle('var(--color-info)')}>(Wind-Driven Current) </div>
<div className="grid grid-cols-2 gap-3">
<div>
<div className={`${codeBox} mb-2`}>
<span className="text-fg-default text-label-2">/* 취송류 유속 (이·강, 2000) */</span>
<br />
V_WDC = <span className="text-color-accent">0.029</span> × V_wind
</div>
<div className={codeBox}>
<span className="text-fg-default text-label-2">/* 취송류 유향 */</span>
<br />
θ_WDC = θ_wind + <span className="text-color-accent">18.6°</span>
</div>
</div>
<div className="flex flex-col gap-1.5 text-label-2 text-fg-sub">
<div
className="px-2 py-1.5 rounded"
style={{
background: 'rgba(6,182,212,.05)',
border: '1px solid rgba(59,130,246,.15)',
}}
>
<span className="text-color-accent font-bold">V_WDC</span> : (m/s)
2.9%
</div>
<div
className="px-2 py-1.5 rounded"
style={{ background: 'rgba(6,182,212,.05)', border: '1px solid rgba(6,182,212,.15)' }}
>
<span className="text-color-accent font-bold">18.6°</span> : Ekman
</div>
<div
className="px-2 py-1.5 rounded"
style={{ background: 'rgba(6,182,212,.05)', border: '1px solid rgba(6,182,212,.15)' }}
>
<b className="text-color-accent"></b> : ·(2000),
</div>
</div>
</div>
</div>
{/* 상시 운용 통신 체계 */}
<div className={`${card} ${cardBg}`}>
<div style={labelStyle('var(--fg-default)')}> </div>
<div className="flex items-center justify-center gap-0 py-3">
{[
{ label: 'NFRDI', sub: 'SST', color: 'var(--color-accent)' },
{ label: 'KMA', sub: 'Wind·기온', color: 'var(--color-accent)' },
{ label: 'HYCOM', sub: '해류', color: 'var(--color-accent)' },
{
label: 'DB 서버',
sub: '동적+정적자료',
color: 'var(--color-accent)',
strong: true,
},
{ label: 'KOSPS', sub: '확산예측', color: 'var(--color-accent)', strong: true },
{ label: '운용자', sub: '결과 수령', color: 'var(--color-accent)' },
].map((node, i) => (
<div key={node.label} className="flex items-center">
<div
className="px-3 py-2.5 rounded-lg text-center text-label-2"
style={{
background: `${node.color}14`,
border: node.strong ? `2px solid ${node.color}4D` : `1px solid ${node.color}33`,
}}
>
<div className="font-bold" style={{ color: node.color }}>
{node.label}
</div>
<div className="text-label-2 text-fg-default">{node.sub}</div>
</div>
{i < 5 && (
<div className="w-[30px] h-px" style={{ background: 'var(--stroke-light)' }} />
)}
</div>
))}
</div>
<div className="text-label-2 text-center mt-1 text-fg-default">
FTP DB
</div>
<div
className="mt-2.5 p-2 rounded-md"
style={{ background: 'rgba(234,179,8,.05)', border: '1px solid rgba(234,179,8,.15)' }}
>
<div className="text-label-2 font-bold mb-1 text-color-caution"> </div>
<div className="text-label-2 text-fg-sub leading-[1.6]">
조류: 무한
<br />
(HYCOM): <b>5</b>
<br />
·: <b className="text-color-caution">3</b>
<br /> <b className="text-color-caution">72h </b>
</div>
</div>
</div>
{/* 이문진 박사 특허 핵심 기술 */}
<div className="rounded-[10px] p-[14px] mb-4 bg-bg-card border border-stroke">
{/* 헤더 */}
<div className="flex items-center gap-2.5 mb-3.5">
<div className="w-[34px] h-[34px] rounded-lg flex items-center justify-center text-title-1 bg-bg-elevated border border-stroke">
📜
</div>
<div>
<div className="text-title-4 font-bold text-fg">
( 10-1567431)
</div>
<div className="text-label-2 mt-0.5 text-fg-default">
·
· 2015
</div>
</div>
</div>
{/* 특허 개요 */}
<div
className="rounded-lg p-3 mb-3.5 text-label-2"
style={{
background: 'rgba(6,182,212,.04)',
border: '1px solid rgba(6,182,212,.12)',
color: 'var(--fg-sub)',
lineHeight: '1.8',
}}
>
{' '}
<b className="text-color-accent">
(ESI, Environmental Sensitivity Index Map)
</b>
·· ,
. ··
·· ,{' '}
<b className="text-color-accent">CHARRY </b>
.
</div>
{/* 3단계 처리 프로세스 */}
<div className="text-label-2 font-bold mb-2.5"> 3 </div>
<div className="grid grid-cols-3 gap-2 mb-3.5">
<div
className="rounded-lg p-3"
style={{
background: 'var(--bg-base)',
border: '1px solid var(--stroke-default)',
}}
>
<div className="text-label-2 font-bold mb-1.5 text-color-info">
(S10)
</div>
<div className="text-label-2 text-fg-sub leading-[1.7]">
<b></b> : ··
<br /> <b></b> : NGSST ( , 5km )
<br /> <b></b> :
</div>
<div
className="mt-1 p-1 rounded text-label-2"
style={{ background: 'rgba(59,130,246,.06)', color: 'var(--fg-disabled)' }}
>
FTP ···DB구축까지
</div>
</div>
<div
className="rounded-lg p-3"
style={{
background: 'var(--bg-base)',
border: '1px solid rgba(6,182,212,.2)',
}}
>
<div className="text-label-2 font-bold mb-1.5 text-color-accent">
· (S20)
</div>
<div className="text-label-2 text-fg-sub leading-[1.7]">
<b className="text-color-accent">CHARRY </b>
<br />
2D DB
<br />
경험식 : V_WDC=0.029×V_wind, θ_WDC=θ_wind+18.6°
<br /> M2·S2·K1·O1 4
</div>
<div
className="mt-1 p-1 rounded text-label-2"
style={{ background: 'rgba(6,182,212,.06)', color: 'var(--fg-disabled)' }}
>
계산격자 : 15초( 463m) · 3,225,600
</div>
</div>
<div
className="rounded-lg p-3"
style={{
background: 'var(--bg-base)',
border: '1px solid var(--stroke-default)',
}}
>
<div className="text-label-2 font-bold mb-1.5 text-color-info">
(S30)
</div>
<div className="text-label-2 text-fg-sub leading-[1.7]">
<b>·</b>
<br />
(Monte Carlo)
<br />
fBm 난류확산 : σ²=Atᵐ (m2H)
<br /> 풍화모델 : 퍼짐····
</div>
<div
className="mt-1 p-1 rounded text-label-2"
style={{ background: 'rgba(59,130,246,.06)', color: 'var(--fg-disabled)' }}
>
ESI Map DB와
</div>
</div>
</div>
{/* 해수유동 방정식 & 풍화 5단계 */}
<div className="grid grid-cols-2 gap-2.5 mb-3">
<div
className="rounded-lg p-3"
style={{ background: 'var(--bg-base)', border: '1px solid rgba(59,130,246,.2)' }}
>
<div className="text-label-2 font-bold mb-2 text-color-info">
📐 2
</div>
<div className="text-label-2 mb-2 text-fg-sub leading-[1.7]">
(Polar Coordinate) ·
</div>
<div className="flex flex-col gap-1 text-label-2 text-fg-sub">
{[
{ label: '기조력', desc: '제1(Love Number 0.69) + 제2(α=0.9) 포함' },
{ label: '4대 분조', desc: 'M2(1.405×10⁻⁴/s) · S2 · K1 · O1' },
{ label: '수치기법', desc: '양해법 + 경사차분법 + 양방향 순차계산법' },
{ label: '조간대', desc: 'Flather & Heaps(1975) 처리기법' },
].map((r) => (
<div
key={r.label}
className="px-2 py-1 rounded"
style={{ background: 'rgba(59,130,246,.05)' }}
>
<span className="font-bold text-color-info">{r.label}</span> : {r.desc}
</div>
))}
</div>
</div>
<div
className="rounded-lg p-3"
style={{ background: 'var(--bg-base)', border: '1px solid rgba(59,130,246,.2)' }}
>
<div className="text-label-2 font-bold mb-2 text-color-info">
🔁 (Weathering) 5
</div>
<div className="flex flex-col gap-1 text-label-2 text-fg-sub">
<div
className="px-2 py-1 rounded"
style={{
background: 'rgba(6,182,212,.05)',
border: '1px solid rgba(6,182,212,.1)',
}}
>
<span className="font-bold text-color-info"> </span> : Fay(1969) + Mackay et
al.(1980) · 3
</div>
<div
className="px-2 py-1 rounded"
style={{
background: 'rgba(59,130,246,.05)',
border: '1px solid rgba(59,130,246,.1)',
}}
>
<span className="font-bold text-color-info"> </span> : Stiver &amp;
Mackay(1984) · (1~10, 25%)
</div>
<div
className="px-2 py-1 rounded"
style={{
background: 'rgba(6,182,212,.05)',
border: '1px solid rgba(6,182,212,.1)',
}}
>
<span className="font-bold text-color-info"> </span> : · ·
(15%)
</div>
<div
className="px-2 py-1 rounded"
style={{
background: 'rgba(234,179,8,.05)',
border: '1px solid rgba(234,179,8,.1)',
}}
>
<span className="font-bold text-color-caution"> </span> : Mackay et al.(1980)
+ Mooney(1951) · water-in-oil
</div>
<div
className="px-2 py-1 rounded"
style={{
background: 'rgba(6,182,212,.05)',
border: '1px solid rgba(6,182,212,.1)',
}}
>
<span className="font-bold text-color-info"> </span> : · ·
</div>
</div>
</div>
</div>
{/* Akima 수심 보간 & NGSST 수온 */}
<div className="grid grid-cols-2 gap-2.5 mb-3">
<div className="rounded-lg p-3" className="bg-bg-card border border-stroke">
<div className="text-label-2 font-bold mb-2 text-color-accent">
🗺 Akima
</div>
<div className="text-label-2 text-fg-sub leading-[1.7]">
(ENC) {' '}
<b className="text-color-accent">Akima(1978) 2 5</b>(Bivariate Quintic
Polynomial) . (TIN)
21 .
</div>
<div className="mt-1.5 p-1.5 rounded bg-bg-base font-mono text-label-1 leading-loose">
z(x,y) = Σ Σ qᵢⱼ xⁱ {' '}
<span className="text-label-2 text-fg-default">(i5, i+j5)</span>
</div>
</div>
<div className="rounded-lg p-3" className="bg-bg-card border border-stroke">
<div className="text-label-2 font-bold mb-2 text-color-accent">
🌡 NGSST
</div>
<div className="text-label-2 text-fg-sub leading-[1.7]">
Kawamura {' '}
<b className="text-color-accent">New Generation SST(NGSST)</b> FTP .
(AVHRR·MODIS) + (AMSR-E) .
</div>
<div className="flex flex-col gap-1 mt-1.5 text-label-2 text-fg-sub">
<div className="px-2 py-1 rounded" style={{ background: 'rgba(6,182,212,.05)' }}>
<span className="font-bold text-color-accent"></span> : 116~166°E, 13~63°N
()
</div>
<div className="px-2 py-1 rounded" style={{ background: 'rgba(6,182,212,.05)' }}>
<span className="font-bold text-color-accent"></span> : 3( 5km) · 1
</div>
<div className="px-2 py-1 rounded" style={{ background: 'rgba(6,182,212,.05)' }}>
<span className="font-bold text-color-accent"></span> : SST() = 0.15 × DN
3.0
</div>
</div>
</div>
</div>
{/* 특허 기여율 */}
<div
className="rounded-lg p-3"
style={{ background: 'rgba(234,179,8,.04)', border: '1px solid rgba(234,179,8,.2)' }}
>
<div className="text-label-2 font-bold mb-2 text-color-caution">
📋
</div>
<div className="grid grid-cols-2 gap-2 text-label-2 text-fg-sub">
<div
className="p-2 rounded-md"
style={{ background: 'rgba(6,182,212,.05)', border: '1px solid rgba(6,182,212,.12)' }}
>
<div className="font-bold mb-1 text-color-accent">🔷 ( 65%)</div>
<div className="text-fg-sub leading-[1.6]">
3
<br />
<span className="text-fg-default"> | 2013.01~2013.12</span>
</div>
</div>
<div
className="p-2 rounded-md"
style={{
background: 'rgba(6,182,212,.05)',
border: '1px solid rgba(6,182,212,.12)',
}}
>
<div className="font-bold mb-1 text-color-info">🔶 ( 35%)</div>
<div className="text-fg-sub leading-[1.6]">
(HNS)
<br />
<span className="text-fg-default"> | 2013.01~2013.12</span>
</div>
</div>
</div>
<div
className="mt-2 p-1.5 rounded text-label-2"
style={{
background: 'rgba(255,255,255,.02)',
border: '1px solid var(--stroke-default)',
color: 'var(--fg-disabled)',
}}
>
특허권자: 한국해양과학기술원 | 발명자: 이문진··· | 등록번호:
10-1567431 | 공고일: 2015.11.12
</div>
</div>
</div>
{/* KOSPS관련 유출유 확산예측 논문 */}
<div className="rounded-[10px] p-[14px] mb-4 bg-bg-card border border-stroke">
<div
className="flex items-center gap-2.5 cursor-pointer select-none"
onClick={() => setKospsPapersOpen(!kospsPapersOpen)}
>
<div
className="w-[30px] h-[30px] rounded-[7px] flex items-center justify-center text-title-3 shrink-0"
style={{ background: 'var(--bg-elevated)', border: '1px solid rgba(6,182,212,.25)' }}
>
📄
</div>
<div className="flex-1">
<div className="text-label-1 font-bold">
KOSPS관련 {' '}
<span
className="text-label-2 ml-1"
style={{
color: 'var(--fg-disabled)',
transition: 'transform .2s',
display: 'inline-block',
transform: kospsPapersOpen ? 'rotate(180deg)' : 'rotate(0)',
}}
>
</span>
</div>
<div className="text-label-2 mt-0.5 text-fg-default">
KOSPS · · 3D ·
·
</div>
</div>
</div>
{kospsPapersOpen && (
<div className="flex flex-col gap-2 mt-3.5">
{[
{
tags: [
{ label: '유출유 확산', color: '#06b6d4' },
{ label: '허베이스피리트 검증', color: 'var(--color-info)' },
{ label: 'Radarsat·GNOME', color: 'var(--color-accent)' },
],
year: '2010',
title: '허베이스피리트호 유출유 확산예측 검증 분석',
authors:
'이문진, 김선동, 김혜진, 오세웅 (한국해양연구원 해양시스템안전연구소) | 한국해양과학기술협의회 공동학술대회 | 2010.6 | pp.3154',
desc: '허베이 스피리트호 유출사고 당시 적용된 유출유 확산예측시스템의 결과를 2가지 검증방법으로 분석. (1) 사고당시 촬영된 Radarsat 인공위성영상에 나타난 유출유 분포와 확산예측시스템의 결과를 비교 분석, (2) 미국 NOAA의 유출유 확산예측시스템인 GNOME의 결과와 동일 입력조건 하에서 비교 검증. NAP 해양유출사고 대응지원시스템 구축 연구의 일부.',
},
{
tags: [
{ label: '3D 확산시스템', color: '#06b6d4' },
{ label: '3차원 모델', color: 'var(--color-accent)' },
{ label: 'Monte Carlo', color: 'var(--color-accent)' },
],
year: '2013',
title: '3차원 유출유 확산예측 시스템 연구',
authors:
'이문진, 김혜진, 강관근 (한국해양과학기술원) | 한국해양과학기술협의회 공동학술대회 | 2013.5 | pp.1718',
desc: '해수순환에 따른 유출유의 해수중 거동을 실제와 부합하게 재현하기 위해 3차원 유출유 확산예측모델 개발. Monte Carlo Simulation 방법을 적용한 수치 추적자(numerical tracer) 방법에 근거하며, 해수유동에 의한 이송(transport)·난류에 의한 확산(turbulent diffusion)·유출유의 침강 및 표면혼합층의 혼탁효과·풍화작용(weathering effect)을 통합 모의. 허베이 스피리트호 사고에 적용(2007.12.7 07:00~2008.1.6 07:00, 약 720시간) — 바람 및 해수유동 재현 결과에 근거하여 3차원 유출유의 수직 이동을 포함한 분포 결과 및 수층 이동 재현. 한국해양과학기술원 지원 〈3차원 유출유 확산예측 기반 해양유류오염 방제지원 기술 개발〉 연구의 일부.',
},
{
tags: [
{ label: '모델 적용', color: '#06b6d4' },
{ label: 'GS칼텍스·Captain Vangelis', color: 'var(--color-caution)' },
],
year: '2014',
title: '유출유 확산예측 모델의 해양사고 적용 및 개선방안 연구',
authors:
'이문진, 김혜진 (선박해양플랜트연구소/KRISO) | 한국해양과학기술협의회 공동학술대회 | 2014.5 | pp.2353',
desc: '유출유 확산예측 고도화의 일환으로 실제 해양사고에 유출유 확산예측 모델을 적용하고 개선방안 연구. 최근 발생한 GS 칼텍스 송유관 유출사고와 Captain Vangelis 유출사고에 적용하여, 사고로 발생된 유출유의 이동확산을 예측하고 현장 보고자료와 비교하여 유출유 확산 예측 모델의 정확도 및 문제점을 분석. 비교 분석을 토대로 기존 모델의 개선방안을 도출하고, 현실과 부합하는 현장상황 재현을 위한 신규 모델링 기술의 개발 방안 연구. 선박해양플랜트연구소 지원 〈3차원 유출유 확산예측 기반 해양유류오염 방제 지원기술 개발〉 연구의 일부.',
},
{
tags: [
{ label: '방제효과', color: '#06b6d4' },
{ label: '오일펜스·유회수기·유처리제', color: 'var(--color-accent)' },
],
year: '2014',
title: '유출유 확산예측 모델의 방제효과 모델링 기술 연구',
authors:
'이문진, 오상우, 정정열 (선박해양플랜트연구소/KRISO) | 한국해양과학기술협의회 공동학술대회 | 2014.5 | pp.2354',
desc: '유출유 확산예측 모델의 정확도 제고를 위하여 유출유 방제효과와 모델링 기술 연구. 유출유 방제효과는 오일펜스의 차단 포집 효과, 유처리제 분산효과, 유회수기 회수효과 등을 고려하여 각각의 효과를 개별 모델로 개발하고 유출유 확산예측 모델과 통합. 오일펜스의 차단 포집효과 모델링에서는 오일펜스 자체의 누유율을 적용하여 유출유의 포집량 및 누유량을 시뮬레이션하며, 유처리제 분산효과 모델링에서는 유처리제 및 유출유 특성을 고려하여 분산에 따른 확산면적 변화를 시뮬레이션. 유회수기 회수 효과 모델링에서는 유회수기의 특성에 따라 회수율을 적용하여 유출유 해상 분포량의 변화를 시뮬레이션. 선박해양플랜트연구소 지원 〈3차원 유출유 확산예측 기반 해양유류오염 방제 지원기술 개발〉 연구의 일부.',
},
].map((paper) => (
<div
key={paper.title}
className="rounded-[9px] p-3"
style={{
background: 'var(--bg-surface)',
border: '1px solid rgba(6,182,212,.18)',
}}
>
<div className="flex items-center justify-between mb-1.5">
<div className="flex items-center gap-1.5">
{paper.tags.map((t) => (
<span
key={t.label}
style={{
padding: '2px 7px',
background: `${t.color}1A`,
border: `1px solid ${t.color}38`,
borderRadius: '4px',
fontSize: 'var(--font-size-label-2)',
fontWeight: 700,
color: t.color,
}}
>
{t.label}
</span>
))}
</div>
<span className="text-label-2 text-fg-default">{paper.year}</span>
</div>
<div className="text-label-2 font-bold mb-1">{paper.title}</div>
<div className="text-label-2 mb-1.5 text-fg-default">{paper.authors}</div>
<div className="text-label-2 text-fg-sub leading-[1.7]">{paper.desc}</div>
</div>
))}
</div>
)}
</div>
{/* 국내 학술논문 */}
<div className={`${card} ${cardBg}`}>
<div
className="flex items-center gap-1.5 mb-2 cursor-pointer select-none"
onClick={() => setPapersOpen(!papersOpen)}
>
<span
style={{
...tag('var(--fg-sub)'),
fontWeight: 700,
fontSize: 'var(--font-size-label-1)',
}}
>
📄
</span>
<span
className="text-label-2 ml-1"
style={{
color: 'var(--fg-disabled)',
transition: 'transform .2s',
display: 'inline-block',
transform: papersOpen ? 'rotate(180deg)' : 'rotate(0)',
}}
>
</span>
</div>
{papersOpen && (
<div className="flex flex-col gap-1.5 mb-3.5 text-label-2 text-fg-sub">
{[
{
num: '①',
title: '유출유 확산 예측 모델의 상시 운용 체계 개발에 관한 연구',
authors: '김혜진 · 이문진 · 오세웅 · 강준묵',
journal: '해양환경안전학회지',
detail: '제17권 4호, pp.375-382 | 2011',
desc: 'KOSPS 상시 운용 체계 구축 · 정적/동적 자료 FTP 자동 연계 · 서해·남해 시범 운용 성능 평가',
color: 'var(--color-accent)',
},
{
num: '②',
title: '해양오염 방제지원시스템 구축을 위한 실시간 조류 예측 기술 개발',
authors: '이문진 · 강용균 외',
journal: '해양환경안전학회 춘계학술발표회',
detail: '| 2008',
desc: 'CHARRY 모델 인천·서해 적용 검증 · 창조/낙조 실시간 조류 재현 결과',
color: 'var(--color-accent)',
},
{
num: '③',
title: '표면 취송류 라그랑지안 측류 및 수치모델링',
authors: '이문진 · 강용균',
journal: '한국해양학회지',
detail: '| 2000',
desc: '취송류 경험식 V=0.029·Vw, θ=θw+18.6° 도출 · 국내 연안 현장 관측 기반',
color: 'var(--color-accent)',
},
{
num: '④',
title: '허베이스피리트호 유출유 확산예측 검증 분석',
authors: '이문진 · 김선동 · 김혜진 · 오세웅',
journal: '한국해양과학기술협의회 공동학술대회',
detail: 'pp.3154 | 2010',
desc: '허베이 스피리트호 유출사고 당시 확산예측시스템 결과 검증 · Radarsat 인공위성영상 비교 분석 · NOAA GNOME과 동일 입력조건 하 비교 검증',
color: 'var(--color-info)',
tags: ['허베이스피리트', 'Radarsat 위성영상', 'GNOME 비교검증'],
},
{
num: '⑤',
title: '3차원 유출유 확산예측 시스템 연구',
authors: '이문진 · 김혜진 · 강관근',
journal: '한국해양과학기술협의회 공동학술대회',
detail: 'pp.17-18 | 2013',
desc: 'Monte Carlo Simulation + 수치 추적자 방법 기반 3차원 유출유 확산예측모델 개발 · 이송·난류확산·침강·혼탁효과·풍화작용 통합 모의 · 허베이 스피리트호 720시간 적용 검증',
color: 'var(--color-accent)',
tags: ['3D 확산모델', 'Monte Carlo', '수치추적자', '720시간 검증'],
},
{
num: '⑥',
title: '유출유 확산예측 모델의 해양사고 적용 및 개선방안 연구',
authors: '이문진 · 김혜진',
journal: '한국해양과학기술협의회 공동학술대회',
detail: 'pp.2353 | 2014',
desc: 'GS 칼텍스 송유관 유출사고 · Captain Vangelis 유출사고에 모델 적용 · 현장 보고자료와 비교 분석 · 기존 모델 개선방안 도출 · 신규 모델링 기술 개발 방안',
color: 'var(--fg-default)',
tags: ['GS칼텍스', 'Captain Vangelis', '현장검증', '모델개선'],
},
{
num: '⑦',
title: '유출유 확산예측 모델의 방제효과 모델링 기술 연구',
authors: '이문진 · 오상우 · 정정열',
journal: '한국해양과학기술협의회 공동학술대회',
detail: 'pp.2354 | 2014',
desc: '오일펜스 차단 포집 효과(누유율) · 유처리제 분산효과(확산면적 변화) · 유회수기 회수효과(해상 분포량 변화) 개별 모델 개발 및 확산예측 모델과 통합',
color: 'var(--color-accent)',
tags: ['오일펜스', '유처리제', '유회수기', '방제효과 통합'],
},
{
num: '⑧',
title: '해양유류오염사고 위해도 평가에 관한 연구',
authors: '이문진 · 김혜진',
journal: '한국해양공학회지',
detail: '제23권 1호, pp.24-30 | 2009',
desc: '라그랑쥐 입자추적 + fBm 난류확산 모델 · CHARRY 실시간 조류예측 · 취송류 교적계수 반응함수 · 20년 과거자료 기반 100회 몬테카를로 통계 위험평가 · 가로림만 해안/어장 도달확률·도달시간 산정',
color: 'var(--color-accent)',
tags: ['Monte Carlo 100회', 'fBm 난류확산', '통계적 위험평가', '가로림만'],
},
{
num: '⑨',
title: '해양정보기반 방제지원시스템 프로토타입 구축에 관한 연구',
authors: '김혜진 · 이문진',
journal: '한국지리정보학회지',
detail: '제11권 4호, pp.182-192 | 2008',
desc: 'KOSPS 원형 시스템 · CHARRY 모델 기반 실시간 조류예측 + fBm 난류확산 통합 · GIS 기반 ESI 방제정보지도 통합 · 통계적 피해위험도(20년 200회) + 실시간 위험도 이원 체계',
color: 'var(--color-accent)',
tags: ['KOSPS 원형', 'GIS-ESI 통합', '의사결정지원', '인천-대산'],
},
{
num: '⑩',
title: '유출유의 초기 확산예측을 위한 고해상도 결합모형 개발',
authors: '손상영 · 이칠우 · 윤현덕 · 정태화',
journal: '한국해안·해양공학회논문집',
detail: '제29권 4호, pp.189-197 | 2017',
desc: 'Boussinesq 동수역학 모형 + MEDSLIK-II 유류 이송-확산-변형 모형 외적 결합 · Lagrangian 유막 표현 · 울산 진하해역 시뮬레이션 검증',
color: 'var(--color-accent)',
tags: ['Boussinesq', 'MEDSLIK-II', '고해상도 결합', '울산 진하'],
},
].map((paper) => (
<div
key={paper.num}
className="rounded-lg p-2.5"
style={{
background: 'var(--bg-base)',
border: '1px solid var(--stroke-default)',
display: 'grid',
gridTemplateColumns: '28px 1fr',
gap: '10px',
alignItems: 'start',
}}
>
<div
className="w-6 h-6 rounded-[5px] flex items-center justify-center text-label-2 shrink-0"
style={{ background: `${paper.color}1F` }}
>
{paper.num}
</div>
<div>
<div className="font-bold mb-0.5">{paper.title}</div>
<div className="text-fg-default leading-[1.6]">
{paper.authors} | <b style={{ color: paper.color }}>{paper.journal}</b>{' '}
{paper.detail}
</div>
<div className="mt-1 text-fg-sub">{paper.desc}</div>
{paper.tags && (
<div className="flex flex-wrap gap-1 mt-1">
{paper.tags.map((t) => (
<span key={t} style={tag(paper.color)}>
{t}
</span>
))}
</div>
)}
</div>
</div>
))}
</div>
)}
</div>
{/* 국외 핵심 논문 */}
<div className={`${card} ${cardBg}`}>
<div
className="flex items-center gap-1.5 mb-2 cursor-pointer select-none"
onClick={() => setIntlPapersOpen(!intlPapersOpen)}
>
<span
style={{
...tag('var(--fg-sub)'),
fontWeight: 700,
fontSize: 'var(--font-size-label-1)',
}}
>
🌐
</span>
<span
className="text-label-2 ml-1"
style={{
color: 'var(--fg-disabled)',
transition: 'transform .2s',
display: 'inline-block',
transform: intlPapersOpen ? 'rotate(180deg)' : 'rotate(0)',
}}
>
</span>
</div>
{intlPapersOpen && (
<div className="flex flex-col gap-1.5 text-label-2 text-fg-sub">
{[
{
num: '①',
title: 'OpenDrift v1.0: a generic framework for trajectory modelling',
authors: 'Dagestad et al.',
journal: 'Geoscientific Model Development',
detail: 'Vol.11, pp.1405-1420 | 2018',
desc: 'OpenDrift 프레임워크 전체 설계·구현·검증 · Python 모듈화 구조 · OpenOil 유출유 모듈 포함',
color: 'var(--color-accent)',
},
{
num: '②',
title:
'Observation-based evaluation of surface wave effects on oil spill trajectories',
authors: 'Röhrs et al.',
journal: 'J. Geophys. Res. Oceans',
detail: '| 2013',
desc: 'Stokes drift 파랑 기여 효과 · OpenOil 유출유 확산 현장 검증',
color: 'var(--color-accent)',
},
{
num: '③',
title: 'Numerical modelling and fate assessment of oil spills at sea',
authors: 'Nordam et al.',
journal: 'Marine Pollution Bulletin',
detail: '| 2019',
desc: '3D 유출유 수직 분포 모의 · 해저면 도달 경로 분석 · 풍화 통합 수치 검증',
color: 'var(--color-accent)',
},
{
num: '④',
title: 'The spreading of oil in the sea',
authors: 'Fay, J.A.',
journal: 'Oil on the Sea, Plenum Press',
detail: '| 1969',
desc: '유출유 퍼짐 3단계 이론(중력-관성력, 관성력-점성력, 표면장력-점성력) 원전. KOSPS 풍화모델 기반',
color: 'var(--color-accent)',
},
{
num: '⑤',
title: 'Evaporation and dissolution of oil spills',
authors: 'Stiver, W. & Mackay, D.',
journal: 'Environmental Science & Technology',
detail: '18(11) | 1984',
desc: '증발 해석 모델 · 증류 데이터 기반 증발비 산정 · KOSPS 증발 서브모듈 직접 적용',
color: 'var(--color-accent)',
},
{
num: '⑥',
title: 'A physical-chemical model of the dispersal of oil in water',
authors: 'Mackay, D. et al.',
journal: 'Environmental Science & Technology',
detail: '| 1980',
desc: '유상화(water-in-oil) 모델 · 소산·유상화 통합 수리 공식. KOSPS·OpenOil 양쪽 공통 적용',
color: 'var(--color-accent)',
},
{
num: '⑦',
title:
'Bivariate interpolation and smooth surface fitting based on local procedures',
authors: 'Akima, H.',
journal: 'Commun. ACM 17(1) | 1974 / ACM TOMS | 1978',
detail: '',
desc: '2차원 5차다항식 보간법(Bivariate Quintic). KOSPS 수심·수온 격자 보간 알고리즘 직접 적용',
color: 'var(--color-accent)',
},
].map((paper) => (
<div
key={paper.num}
className="rounded-lg p-2.5"
style={{
background: 'var(--bg-base)',
border: '1px solid var(--stroke-default)',
display: 'grid',
gridTemplateColumns: '28px 1fr',
gap: '10px',
alignItems: 'start',
}}
>
<div
className="w-6 h-6 rounded-[5px] flex items-center justify-center text-label-2 shrink-0"
style={{ background: `${paper.color}1F` }}
>
{paper.num}
</div>
<div>
<div className="font-bold mb-0.5">{paper.title}</div>
<div className="text-fg-default leading-[1.6]">
{paper.authors} | <b style={{ color: paper.color }}>{paper.journal}</b>{' '}
{paper.detail}
</div>
<div className="mt-1 text-fg-sub">{paper.desc}</div>
</div>
</div>
))}
</div>
)}
</div>
</div>
);
}
/* ═══ 패널 8: POSEIDON 상세 ═══ */
function PoseidonPanel() {
return (
<div>
<div className="rounded-[10px] p-[14px] mb-4 bg-bg-card border border-stroke">
<div className="flex items-center gap-3 mb-3">
<div className="w-[38px] h-[38px] rounded-[10px] flex items-center justify-center text-heading-3 bg-bg-elevated border border-stroke">
🔵
</div>
<div>
<div className="text-title-2 font-bold text-fg">
POSEIDON ( )
</div>
<div className="text-label-2 mt-0.5 text-fg-default">
· () · · MOHID
·
</div>
</div>
</div>
<div className="text-label-2 text-fg-sub leading-[1.8]">
· ( 10-1868791, 2018) POSEIDON은 {' '}
<b className="text-color-accent">(Drifter) </b> ,
{' '}
<b className="text-color-accent">GA·DE·HS·PSO </b>
. IST/MARETEC이 {' '}
<b className="text-color-accent">MOHID 3D </b> .
</div>
<div
className="mt-2.5 px-3 py-2 rounded-md text-label-2"
style={{
background: 'rgba(59,130,246,.06)',
border: '1px solid rgba(59,130,246,.15)',
color: 'var(--fg-disabled)',
}}
>
: ·, &quot;
&quot;, 10-1868791, 2018.
<br />
: ·· , &quot;2016 MOHID
&quot;, 39(5), pp.436-457, 2018.
</div>
</div>
{/* 등록특허 */}
<div className={`${card} ${cardBg}`}>
<div className="flex items-center gap-1.5 mb-2">
<span
style={{
...tag('var(--fg-sub)'),
fontWeight: 700,
fontSize: 'var(--font-size-label-1)',
}}
>
</span>
</div>
<div
className="rounded-lg p-3"
style={{
background: 'var(--bg-base)',
border: '1px solid var(--stroke-default)',
display: 'grid',
gridTemplateColumns: 'auto 1fr',
gap: '12px',
}}
>
<div
className="px-2.5 py-1.5 rounded-md text-center whitespace-nowrap"
style={{
background: 'var(--bg-elevated)',
border: '1px solid var(--stroke-default)',
fontFamily: 'var(--font-mono)',
}}
>
<div className="text-label-2 text-fg-default"></div>
<div className="text-label-2 font-bold text-fg">10-1868791</div>
<div className="text-label-2 mt-0.5 text-fg-default">2018 </div>
</div>
<div>
<div className="text-label-2 font-bold mb-1">
(POSEIDON)
</div>
<div className="text-label-2 text-fg-sub">
<span className="text-fg font-medium"></span> : · · |{' '}
<span className="text-fg font-medium"></span> : ()
</div>
<div className="flex flex-wrap gap-1 mt-1.5">
{[
'GA(유전알고리즘)',
'DE(차분진화)',
'HS(화음탐색)',
'PSO(입자군집최적화)',
'뜰개 자동 동화',
].map((t) => (
<span key={t} style={tag('var(--fg-sub)')}>
{t}
</span>
))}
</div>
</div>
</div>
</div>
<div className="grid grid-cols-2 gap-3 mb-4">
<div className={`${card} ${cardBg}`} style={{ margin: 0 }}>
<div style={labelStyle('var(--color-accent)')}>MOHID ( )</div>
<div className={`${bodyText} mb-2.5`}>
IST/MARETEC에서 1985 3D . 60
····· .
</div>
<div className="flex flex-col gap-1 text-label-2 text-fg-sub">
{[
{ label: '격자 구성', desc: '동아시아 9km + 한반도 3km (Nesting)' },
{ label: '수직 좌표', desc: 'σ + Z-level 혼합 (GVC)' },
{ label: '검증 결과', desc: 'SST RMSE 0.42~0.78°C, SLA RMSE 0.11~0.17m' },
{ label: '경계조건', desc: 'HYCOM 재분석 (MYOCEAN 대비 우수)' },
].map((t) => (
<div
key={t.label}
className="px-2 py-1 rounded"
style={{
background: 'rgba(6,182,212,.04)',
border: '1px solid rgba(6,182,212,.12)',
color: 'var(--fg-sub)',
}}
>
<span className="text-color-accent font-bold">{t.label}</span> : {t.desc}
</div>
))}
</div>
</div>
<div className={`${card} ${cardBg}`} style={{ margin: 0 }}>
<div style={labelStyle('var(--color-accent)')}> </div>
<div className={`${bodyText} mb-2.5`}>
1 2
.
</div>
<div className="flex flex-col gap-1 text-label-2 text-fg-sub">
{[
{ label: '최적화 알고리즘', desc: 'GA · DE · HS · PSO 중 선택' },
{ label: '입력 데이터', desc: '유속·풍속 벡터 + 제1모델 변화량' },
{ label: '관측 보강', desc: 'UAV·선박 탑재 센서 동시 활용 가능' },
{ label: '지원과제', desc: '행정안전부 KCG-01-2017-05 (해양경비지원기술)' },
].map((t) => (
<div
key={t.label}
className="px-2 py-1 rounded"
style={{
background: 'rgba(6,182,212,.04)',
border: '1px solid rgba(6,182,212,.12)',
color: 'var(--fg-sub)',
}}
>
<span style={{ color: 'var(--color-accent)', fontWeight: 700 }}>{t.label}</span> :{' '}
{t.desc}
</div>
))}
</div>
</div>
</div>
{/* 핵심 수식 */}
<div className={`${card} ${cardBg}`}>
<div style={labelStyle('var(--color-info)')}>POSEIDON </div>
<div className="grid grid-cols-2 gap-3">
<div>
<div className="text-label-2 font-medium mb-1.5 text-fg-default">
1 ()
</div>
<div className={codeBox}>
Model_x = Δt × current_u + Δt × c × wind_u
<br />
Model_y = Δt × current_v + Δt × c × wind_v
</div>
<div className="text-label-2 mt-1.5 text-fg-default">
c : 풍속 (: c=0.3 30% )
</div>
</div>
<div>
<div className="text-label-2 font-medium mb-1.5 text-fg-default">
2 ( )
</div>
<div className={codeBox}>
Revised_x = a1·current_u + a2·current_v
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+ a3·wind_u +
a4·wind_v
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+ a5·Model_x +
a6·Model_y + a7
</div>
<div className="text-label-2 mt-1.5 text-fg-default">
a1~a7 : GA·DE·PSO로
</div>
</div>
</div>
</div>
{/* 상시 운용 통신 체계 */}
<div className={`${card} ${cardBg}`}>
<div style={labelStyle('var(--fg-default)')}>🔄 POSEIDON_V2 </div>
{/* 외부 입력 자료 */}
<div className="text-label-2 font-bold mb-1.5 mt-1 text-fg-default"> </div>
<div className="flex items-center justify-center gap-0 mb-3">
{[
{
label: 'HYCOM',
sub: '해류·수온·염분',
detail: 'YYYYMMDD.nc',
color: 'var(--color-info)',
},
{
label: 'GDAPS(UM)',
sub: '바람·기온·기압',
detail: 'g512_v070_erea_*.grib2',
color: 'var(--color-accent)',
},
{
label: 'DAIN(.enc)',
sub: '격자·수심·해안선·조석',
detail: '정적 바이너리 데이터',
color: 'var(--color-accent)',
},
{
label: '뜰개(Drifter)',
sub: '관측 표류 경로',
detail: 'GA/DE/PSO 동화용',
color: 'var(--color-accent)',
},
].map((node, i) => (
<div key={node.label} className="flex items-center">
<div
className="px-2.5 py-2 rounded-lg text-center text-label-2"
style={{
background: `${node.color}14`,
border: `1px solid ${node.color}33`,
}}
>
<div className="font-bold text-label-2" style={{ color: node.color }}>
{node.label}
</div>
<div className="text-fg-sub">{node.sub}</div>
<div
className="mt-0.5"
style={{ color: 'var(--fg-disabled)', fontSize: 'var(--font-size-caption)' }}
>
{node.detail}
</div>
</div>
{i < 3 && (
<div className="w-[20px] h-px" style={{ background: 'var(--stroke-light)' }} />
)}
</div>
))}
</div>
{/* 구분선 */}
<div className="border-t border-stroke my-3" />
{/* 중앙 화살표 */}
<div className="text-center text-label-2 mb-2 text-fg-default">
DATA PREP /
</div>
{/* 4대 도메인 실행 모듈 */}
<div className="text-label-2 font-bold mb-1.5 text-fg-default">
POSEIDON 4 (EA012 KO108 )
</div>
<div className="grid grid-cols-4 gap-2 mb-3">
{[
{
label: 'HYDR',
script: 'RUN-HYDR.sh',
engine: 'MOHID 3D',
desc: '해류·수온·염분·수위',
color: 'var(--color-accent)',
icon: '🌊',
},
{
label: 'WAVE',
script: 'RUN-WAVE.sh',
engine: 'SWAN',
desc: '파랑 (wave_x, wave_y)',
color: 'var(--color-info)',
icon: '🌬️',
},
{
label: 'TIDE',
script: 'RUN-TIDE.sh',
engine: 'MOHID-TIDE',
desc: '조석 (M2·S2·K1·O1)',
color: 'var(--color-accent)',
icon: '🌀',
},
{
label: 'OILS',
script: 'RUN-OILS.sh',
engine: 'OILS-RUN',
desc: '유출유 입자추적·확산',
color: 'var(--color-accent)',
icon: '🛢️',
strong: true,
},
].map((m) => (
<div
key={m.label}
className="rounded-lg p-2.5 text-center"
style={{
background: `${m.color}0A`,
border: m.strong ? `2px solid ${m.color}4D` : `1px solid ${m.color}25`,
}}
>
<div className="text-title-2 mb-1">{m.icon}</div>
<div className="text-label-1 font-bold" style={{ color: m.color }}>
{m.label}
</div>
<div className="text-caption mt-0.5 text-fg-sub">{m.engine}</div>
<div
className="text-caption mt-0.5 px-1 py-0.5 rounded"
style={{ background: `${m.color}14`, color: 'var(--fg-disabled)' }}
>
{m.script}
</div>
<div className="text-caption mt-1 text-fg-sub">{m.desc}</div>
</div>
))}
</div>
{/* 화살표 + 최적화 */}
<div className="text-center text-label-2 mb-2 text-fg-default">
HYDR + WAVE + TIDE OILS GA/DE/PSO
</div>
{/* 출력·활용 */}
<div className="flex items-center justify-center gap-0">
{[
{ label: 'DASV 결과', sub: 'HDF5·NetCDF', color: 'var(--color-accent)' },
{ label: 'SAR/위성', sub: '원격탐사 검증', color: 'var(--color-info)' },
{
label: '방제 의사결정',
sub: '자원배치·전략',
color: 'var(--color-accent)',
strong: true,
},
].map((node, i) => (
<div key={node.label} className="flex items-center">
<div
className="px-3 py-2 rounded-lg text-center text-label-2"
style={{
background: `${node.color}14`,
border: node.strong ? `2px solid ${node.color}4D` : `1px solid ${node.color}33`,
}}
>
<div className="font-bold" style={{ color: node.color }}>
{node.label}
</div>
<div className="text-label-2 text-fg-default">{node.sub}</div>
</div>
{i < 2 && (
<div className="w-[30px] h-px" style={{ background: 'var(--stroke-light)' }} />
)}
</div>
))}
</div>
<div
className="text-label-2 text-center mt-2.5 px-3 py-1.5 rounded-md"
style={{
background: 'rgba(59,130,246,.04)',
border: '1px solid rgba(59,130,246,.12)',
color: 'var(--fg-disabled)',
}}
>
HYCOM(.nc) + GDAPS(.grib2) (EA012/KO108) MOHID() + SWAN() +
OILS HDF5
</div>
<div
className="mt-2.5 p-2 rounded-md"
style={{ background: 'rgba(234,179,8,.05)', border: '1px solid rgba(234,179,8,.15)' }}
>
<div className="text-label-2 font-bold mb-1 text-color-caution"> </div>
<div className="text-label-2 text-fg-sub leading-[1.6]">
조류: 무한
<br />
(HYCOM): <b>5</b>
<br />
·: <b className="text-color-caution">3</b>
<br /> <b className="text-color-caution">72h </b>
</div>
</div>
</div>
{/* POSEIDON관련 유출유 확산예측 논문 */}
<div className="rounded-[10px] p-[14px] mb-4 bg-bg-card border border-stroke">
<div className="flex items-center gap-2.5 mb-3.5">
<div
className="w-[30px] h-[30px] rounded-[7px] flex items-center justify-center text-title-3 shrink-0"
style={{ background: 'var(--bg-elevated)', border: '1px solid rgba(59,130,246,.25)' }}
>
📄
</div>
<div>
<div className="text-label-1 font-bold">POSEIDON관련 </div>
<div className="text-label-2 mt-0.5 text-fg-default">
· · · · MOHID
·
</div>
</div>
</div>
<div className="flex flex-col gap-2">
{[
{
tags: [
{ label: '시스템 소개', color: '#3b82f6' },
{ label: '활용사례', color: 'var(--color-accent)' },
],
year: '2019',
title: '해양오염방제 의사결정지원 시스템(포세이돈) 소개 및 활용사례',
authors:
'김충기, 윤종휘, 천정윤, 김도연 | 한국해양환경·에너지학회 학술대회논문집 | 2019.5 | pp.14',
desc: 'POSEIDON 시스템의 전체 아키텍처 및 주요 기능 소개. 유출유 확산예측·방제자원 연계·의사결정지원 모듈 구성 설명. 실제 해양오염 대응 사례 적용 결과 발표.',
},
{
tags: [
{ label: '방제전략', color: '#3b82f6' },
{ label: '정책수립', color: 'var(--color-accent)' },
],
year: '2020',
title: '포세이돈 시스템을 활용한 최적 해양오염 방제전략 및 정책수립',
authors:
'윤종휘, 김충기, 천정윤, 김형만, 방기영, 홍성수 | 한국해양환경·에너지학회 학술대회논문집 | 2020.7 | pp.1452',
desc: 'POSEIDON 시스템을 활용한 방제자원 최적 배치 전략 수립 방법론 연구. 유출유 확산 시나리오별 방제효율 비교 분석 및 정책 의사결정 지원 프레임워크 제안.',
},
{
tags: [
{ label: '원격탐사', color: '#3b82f6' },
{ label: '위성 연동', color: '#06b6d4' },
],
year: '2022',
title: '원격탐사 기반의 유출유 확산예측 및 해양오염 방제 지원',
authors:
'김도연, 김충기, 양찬수 | 한국해양환경·에너지학회 학술대회논문집 | 2022.11 | pp.79',
desc: '위성 원격탐사(SAR·광학) 기반 유출유 탐지 결과를 POSEIDON 확산예측 모델과 연동하는 기술 연구. 실시간 위성 영상으로 초기 유출 범위를 파악하고 예측 정확도 향상.',
},
{
tags: [
{ label: 'MOHID 검증', color: '#3b82f6' },
{ label: '해양순환모델', color: '#06b6d4' },
],
year: '2018',
title: '한반도 인근 해역 MOHID 지역 해양순환 모델 검증',
authors: '이재호, 임병준, 김도연 외 | 한국지구과학회지 제39권 5호, pp.436-457 | 2018',
desc: 'POSEIDON 기반 MOHID 모델 동아시아 해역 2016년 검증. 수온·염분·해류 정확도 평가 — SST RMSE 0.42~0.78°C, SLA RMSE 0.11~0.17m.',
},
].map((paper) => (
<div
key={paper.title}
className="rounded-[9px] p-3"
style={{
background: 'var(--bg-surface)',
border: '1px solid rgba(59,130,246,.18)',
}}
>
<div className="flex items-center justify-between mb-1.5">
<div className="flex items-center gap-1.5">
{paper.tags.map((t) => (
<span
key={t.label}
style={{
padding: '2px 7px',
background: `${t.color}1A`,
border: `1px solid ${t.color}38`,
borderRadius: '4px',
fontSize: 'var(--font-size-label-2)',
fontWeight: 700,
color: t.color,
}}
>
{t.label}
</span>
))}
</div>
<span className="text-label-2 text-fg-default">{paper.year}</span>
</div>
<div className="text-label-2 font-bold mb-1">{paper.title}</div>
<div className="text-label-2 mb-1.5 text-fg-default">{paper.authors}</div>
<div className="text-label-2 text-fg-sub leading-[1.7]">{paper.desc}</div>
</div>
))}
</div>
</div>
</div>
);
}
/* ═══ 패널 9: OpenDrift 상세 ═══ */
function OpenDriftPanel() {
return (
<div>
<div className="rounded-[10px] p-[14px] mb-4 bg-bg-card border border-stroke">
<div className="flex items-center gap-3 mb-3">
<div className="w-[38px] h-[38px] rounded-[10px] flex items-center justify-center text-heading-3 bg-bg-elevated border border-stroke">
🟢
</div>
<div>
<div className="text-title-2 font-bold text-fg">
OpenDrift ( )
</div>
<div className="text-label-2 mt-0.5 text-fg-default">
MET Norway · OpenOil · Python · IMO/IPIECA
</div>
</div>
</div>
<div className="text-label-2 text-fg-sub leading-[1.8]">
Dagestad et al.(2018) OpenDrift는 ·SAR··
{' '}
<b className="text-color-info">Python </b>.
NEMO·ROMS·HYCOM ,{' '}
<b className="text-color-accent">OpenOil </b> .
</div>
<div className="flex gap-2 flex-wrap mt-2.5">
<span
className="px-3.5 py-1.5 rounded-md text-label-2 font-medium"
style={{
border: '1px solid rgba(6,182,212,.3)',
background: 'rgba(6,182,212,.08)',
color: 'var(--color-accent)',
}}
>
🔗 (opendrift.github.io)
</span>
<span
className="px-3.5 py-1.5 rounded-md text-label-2"
style={{
border: '1px solid rgba(6,182,212,.2)',
background: 'rgba(6,182,212,.06)',
color: 'var(--color-accent)',
}}
>
Dagestad et al., Geosci. Model Dev. 11, 2018
</span>
</div>
</div>
<div className="grid grid-cols-3 gap-3 mb-4">
{[
{
title: '🧩 모듈 구성',
color: 'var(--color-accent)',
items: [
{ html: '<b style="color:var(--color-accent)">OpenOil</b> : 유출유 확산·풍화' },
{ html: '<b style="color:var(--color-accent)">OceanDrift</b> : 표류물 추적' },
{ html: '<b style="color:var(--color-accent)">ShipDrift</b> : 선박 표류' },
{ html: '<b style="color:var(--color-accent)">SedimentDrift</b> : 부유퇴적물' },
],
},
{
title: '🌊 지원 해양모델',
color: 'var(--color-accent)',
items: [
{ text: 'NEMO (전지구·지역)' },
{ text: 'ROMS (지역 고해상도)' },
{ text: 'HYCOM (전지구)' },
{ text: 'Copernicus CMEMS' },
],
},
{
title: '✅ 검증·인증',
color: 'var(--color-info)',
items: [
{ text: 'IMO / IPIECA 기준 충족' },
{ text: 'ITOPF 뜰개 검증 완료' },
{ text: 'NOAA Oil Library 탑재' },
{ text: 'Geosci. Model Dev. 게재' },
],
},
].map((s) => (
<div key={s.title} className={`${card} ${cardBg}`} style={{ margin: 0 }}>
<div style={labelStyle(s.color)}>{s.title}</div>
<div className="flex flex-col gap-1 text-label-2 text-fg-sub">
{s.items.map((item, idx) =>
'html' in item ? (
<div
key={idx}
className="px-2 py-1 rounded"
style={{
background: `${s.color}0A`,
border: `1px solid ${s.color}1F`,
color: 'var(--fg-sub)',
}}
dangerouslySetInnerHTML={{ __html: sanitizeHtml(item.html) }}
/>
) : (
<div
key={idx}
className="px-2 py-1 rounded"
style={{
background: `${s.color}0A`,
border: `1px solid ${s.color}1F`,
color: 'var(--fg-sub)',
}}
>
{item.text}
</div>
),
)}
</div>
</div>
))}
</div>
{/* OpenOil 풍화 프로세스 */}
<div className={`${card} ${cardBg}`}>
<div style={labelStyle('var(--fg-default)')}>
🔁 OpenOil (IKI )
</div>
<div className="grid grid-cols-4 gap-2">
{[
{
icon: '☁️',
title: '증발',
color: 'var(--fg-default)',
desc: '성분별 증기압·분자량 기반 다성분 증발',
},
{
icon: '🌊',
title: '유화',
color: 'var(--color-accent)',
desc: 'Mackay 유화 방정식·함수율 추적',
},
{
icon: '💧',
title: '수중 분산',
color: 'var(--color-info)',
desc: '파랑 에너지 기반 수직 혼합·재부상',
},
{
icon: '🦠',
title: '생분해',
color: 'var(--color-accent)',
desc: '수온 의존 미생물 분해율 모의',
},
].map((w) => (
<div
key={w.title}
className="p-2.5 rounded-lg text-center"
style={{ background: `${w.color}0A`, border: `1px solid ${w.color}25` }}
>
<div className="text-title-2 mb-1">{w.icon}</div>
<div className="text-label-2 font-medium" style={{ color: w.color }}>
{w.title}
</div>
<div className="text-label-2 mt-1 text-fg-default leading-normal">{w.desc}</div>
</div>
))}
</div>
</div>
{/* 상시 운용 통신 체계 */}
<div className={`${card} ${cardBg}`}>
<div style={labelStyle('var(--fg-default)')}> </div>
<div className="flex items-center justify-center gap-0 py-3">
{[
{ label: 'NEMO/ROMS', sub: '해류 강제력', color: 'var(--color-accent)' },
{ label: 'HYCOM', sub: '전지구 해류', color: 'var(--color-info)' },
{ label: 'ECMWF/GFS', sub: 'Wind·파랑', color: 'var(--color-info)' },
{ label: 'NOAA Oil Lib', sub: '유종 물성 DB', color: 'var(--color-info)' },
{
label: 'OpenDrift',
sub: 'Python 모듈 구동',
color: 'var(--color-info)',
strong: true,
},
{ label: 'OpenOil', sub: '유출유 풍화·확산', color: 'var(--color-info)', strong: true },
{ label: '결과 표출', sub: 'NetCDF·시각화', color: 'var(--color-accent)' },
].map((node, i) => (
<div key={node.label} className="flex items-center">
<div
className="px-3 py-2.5 rounded-lg text-center text-label-2"
style={{
background: `${node.color}14`,
border: node.strong ? `2px solid ${node.color}4D` : `1px solid ${node.color}33`,
}}
>
<div className="font-bold" style={{ color: node.color }}>
{node.label}
</div>
<div className="text-label-2 text-fg-default">{node.sub}</div>
</div>
{i < 6 && (
<div className="w-[24px] h-px" style={{ background: 'var(--stroke-light)' }} />
)}
</div>
))}
</div>
<div className="text-label-2 text-center mt-1 text-fg-default">
(NEMO·ROMS·HYCOM) + (ECMWF·GFS) NOAA Oil Library
OpenDrift/OpenOil NetCDF ·
</div>
</div>
{/* 관련 논문 카드 섹션 */}
<div className="rounded-[10px] p-[14px] mb-4 bg-bg-card border border-stroke">
<div className="flex items-center gap-2.5 mb-3.5">
<div
className="w-8 h-8 rounded-lg flex items-center justify-center text-title-2 shrink-0"
style={{ background: 'var(--bg-elevated)', border: '1px solid rgba(59,130,246,.25)' }}
>
📄
</div>
<div>
<div className="text-label-1 font-bold">
OpenDrift / OpenOil
</div>
<div className="text-label-2 mt-0.5 text-fg-default">
3 WING
</div>
</div>
</div>
<div className="flex flex-col gap-2.5">
{/* 논문 1: Dang et al. 2024 */}
<div
className="rounded-[10px] p-3.5"
style={{
background: 'var(--bg-surface)',
border: '1px solid var(--stroke-default)',
}}
>
<div className="flex items-start justify-between gap-2.5 mb-2">
<div className="flex items-center gap-1.5 flex-wrap">
{[
{ label: '국제저널', color: '#3b82f6' },
{ label: 'OpenOil 적용', color: 'var(--color-info)' },
{ label: '허베이 스피리트', color: 'var(--color-info)' },
].map((t) => (
<span
key={t.label}
className="whitespace-nowrap"
style={{
padding: '2px 8px',
background: `${t.color}1F`,
border: `1px solid ${t.color}40`,
borderRadius: '4px',
fontSize: 'var(--font-size-label-2)',
fontWeight: 700,
color: t.color,
}}
>
{t.label}
</span>
))}
</div>
<span className="text-label-2 whitespace-nowrap text-fg-default">2024</span>
</div>
<div className="text-label-2 font-bold mb-1 leading-normal">
Numerical Model Test of Spilled Oil Transport Near the Korean Coasts Using Various
Input Parametric Models
</div>
<div className="text-label-2 mb-2 text-fg-default">
Hai Van Dang, Suchan Joo, Junhyeok Lim, Jinhwan Hur, Sungwon Shin | Hanyang University
ERICA | Journal of Ocean Engineering and Technology, 2024
</div>
<div className="grid grid-cols-2 gap-2 mb-2">
<div className="text-label-2 text-fg-sub leading-[1.7]">
<b> </b>
<br />
OpenOil - (Met-Ocean)
. (2007) 6 .
</div>
<div className="text-label-2 text-fg-sub leading-[1.7]">
<b> </b>
<br />
CMEMS + ECMWF . ( &lt;60m )
. Envisat ASAR .
</div>
</div>
<div className="mb-2">
<div className="text-label-2 mb-1 text-fg-sub">
<b> 6 </b>
</div>
<div className="grid grid-cols-3 gap-1 text-label-2 text-fg-sub">
<div
className="px-2 py-1 rounded"
style={{
background: 'rgba(59,130,246,.05)',
border: '1px solid rgba(59,130,246,.12)',
color: 'var(--fg-sub)',
}}
>
<b className="text-color-info"></b> HYCOM / CMEMS
</div>
<div
className="px-2 py-1 rounded"
style={{
background: 'rgba(6,182,212,.05)',
border: '1px solid rgba(6,182,212,.12)',
color: 'var(--fg-sub)',
}}
>
<b className="text-color-info"></b> CMEMS / ECMWF-ERA5
</div>
<div
className="px-2 py-1 rounded"
style={{
background: 'rgba(6,182,212,.05)',
border: '1px solid rgba(6,182,212,.12)',
color: 'var(--fg-sub)',
}}
>
<b className="text-color-info"></b> NCEP-GFS / ECMWF
</div>
</div>
</div>
<div
className="p-2 rounded-md text-label-2"
style={{
background: 'rgba(59,130,246,.04)',
border: '1px solid rgba(59,130,246,.1)',
color: 'var(--fg-sub)',
lineHeight: '1.7',
}}
>
<b className="text-color-info">WING </b> : OpenOil이
. 6 Met-Ocean WING의 OpenDrift
(CMEMS+ECMWF) .
</div>
</div>
{/* 논문 2: 류청로 외 1998 */}
<div
className="rounded-[10px] p-3.5"
style={{
background: 'var(--bg-surface)',
border: '1px solid rgba(6,182,212,.2)',
}}
>
<div className="flex items-start justify-between gap-2.5 mb-2">
<div className="flex items-center gap-1.5 flex-wrap">
{[
{ label: '국내저널', color: '#06b6d4' },
{ label: '라그랑지안 실증', color: 'var(--color-accent)' },
{ label: '동남해역 검증', color: 'var(--color-caution)' },
].map((t) => (
<span
key={t.label}
className="whitespace-nowrap"
style={{
padding: '2px 8px',
background: `${t.color}1F`,
border: `1px solid ${t.color}40`,
borderRadius: '4px',
fontSize: 'var(--font-size-label-2)',
fontWeight: 700,
color: t.color,
}}
>
{t.label}
</span>
))}
</div>
<span className="text-label-2 whitespace-nowrap text-fg-default">1998</span>
</div>
<div className="text-label-2 font-bold mb-1 leading-normal">
(Oil Spill Behavior Forecasting Model in
South-eastern Coastal Area of Korea)
</div>
<div className="text-label-2 mb-2 text-fg-default">
, , , | | Vol.1
No.2, pp.5259, 1998
</div>
<div className="grid grid-cols-2 gap-2 mb-2">
<div className="text-label-2 text-fg-sub leading-[1.7]">
<b> </b>
<br />
(-) OILSPILLFM .
(1995) .
</div>
<div className="text-label-2 text-fg-sub leading-[1.7]">
<b> </b>
<br />
+ 2D (ADI법). Vw = 0.03×V10. Random
Walk. 1 .
</div>
</div>
<div className="grid grid-cols-3 gap-1 text-label-2 mb-2 text-fg-sub">
<div
className="px-2 py-1 rounded"
style={{
background: 'rgba(6,182,212,.05)',
border: '1px solid rgba(6,182,212,.12)',
color: 'var(--fg-sub)',
}}
>
<b className="text-color-accent"></b> Navier-Stokes 2D ·
</div>
<div
className="px-2 py-1 rounded"
style={{
background: 'rgba(6,182,212,.05)',
border: '1px solid rgba(6,182,212,.12)',
color: 'var(--fg-sub)',
}}
>
<b className="text-color-accent"></b> DB
</div>
<div
className="px-2 py-1 rounded"
style={{
background: 'rgba(6,182,212,.05)',
border: '1px solid rgba(6,182,212,.12)',
color: 'var(--fg-sub)',
}}
>
<b className="text-color-info"></b> ··· 5
</div>
</div>
<div
className="p-2 rounded-md text-label-2"
style={{
background: 'rgba(6,182,212,.04)',
border: '1px solid rgba(6,182,212,.1)',
color: 'var(--fg-sub)',
lineHeight: '1.7',
}}
>
<b className="text-color-accent">WING </b> : OpenDrift·KOSPS의
. ·
OpenDrift .
</div>
</div>
{/* 논문 3: 정태성·조형진 2008 */}
<div
className="rounded-[10px] p-3.5"
style={{
background: 'var(--bg-surface)',
border: '1px solid var(--stroke-default)',
}}
>
<div className="flex items-start justify-between gap-2.5 mb-2">
<div className="flex items-center gap-1.5 flex-wrap">
{[
{ label: '국내학술대회', color: 'var(--color-accent)' },
{ label: '태안 허베이스피리트', color: 'var(--color-info)' },
{ label: '서해 수치모의', color: 'var(--color-accent)' },
].map((t) => (
<span
key={t.label}
className="whitespace-nowrap"
style={{
padding: '2px 8px',
background: `${t.color}1F`,
border: `1px solid ${t.color}40`,
borderRadius: '4px',
fontSize: 'var(--font-size-label-2)',
fontWeight: 700,
color: t.color,
}}
>
{t.label}
</span>
))}
</div>
<span className="text-label-2 whitespace-nowrap text-fg-default">2008</span>
</div>
<div className="text-label-2 font-bold mb-1 leading-normal">
(Analysis of Oil Spill Dispersion in Taean
Coastal Zone)
</div>
<div className="text-label-2 mb-2 text-fg-default">
, | | · 17
pp.6063, 2008
</div>
<div className="grid grid-cols-2 gap-2 mb-2">
<div className="text-label-2 text-fg-sub leading-[1.7]">
<b> </b>
<br />
2007 12 15 ·
2D . · .
</div>
<div className="text-label-2 text-fg-sub leading-[1.7]">
<b> </b>
<br />
α = 2% (3% , 2.5% ).
θ = 20° . , .
</div>
</div>
<div className="grid grid-cols-3 gap-1 text-label-2 mb-2 text-fg-sub">
<div
className="px-2 py-1 rounded"
style={{
background: 'rgba(6,182,212,.05)',
border: '1px solid rgba(6,182,212,.12)',
color: 'var(--fg-sub)',
}}
>
<b className="text-color-info"></b> ~( )
</div>
<div
className="px-2 py-1 rounded"
style={{
background: 'rgba(59,130,246,.05)',
border: '1px solid rgba(59,130,246,.12)',
color: 'var(--fg-sub)',
}}
>
<b className="text-color-info"></b> , 150m
</div>
<div
className="px-2 py-1 rounded"
style={{
background: 'rgba(6,182,212,.05)',
border: '1px solid rgba(6,182,212,.12)',
color: 'var(--fg-sub)',
}}
>
<b className="text-color-info"></b> 50/9 · 5
</div>
</div>
{/* 취송류 파라미터 비교 */}
<div
className="p-2.5 rounded-[7px] mb-2"
style={{ background: 'var(--bg-base)', border: '1px solid var(--stroke-default)' }}
>
<div className="text-label-2 font-bold mb-1.5 text-fg-sub">
( )
</div>
<div className="grid grid-cols-4 gap-1 text-label-2 text-fg-sub">
<div
className="px-2 py-1 rounded text-center"
style={{
background: 'rgba(59,130,246,.06)',
border: '1px solid rgba(59,130,246,.12)',
}}
>
<div className="font-bold text-color-info">α = 3%</div>
<div className="text-fg-default"> </div>
</div>
<div
className="px-2 py-1 rounded text-center"
style={{
background: 'rgba(234,179,8,.06)',
border: '1px solid rgba(234,179,8,.12)',
}}
>
<div className="font-bold text-color-caution">α = 2.5%</div>
<div className="text-fg-default"> </div>
</div>
<div
className="px-2 py-1 rounded text-center"
style={{
background: 'rgba(6,182,212,.08)',
border: '1px solid rgba(6,182,212,.2)',
}}
>
<div className="font-bold text-color-info">α = 2% </div>
<div className="text-fg-default"> </div>
</div>
<div
className="px-2 py-1 rounded text-center"
style={{
background: 'rgba(6,182,212,.06)',
border: '1px solid rgba(6,182,212,.12)',
}}
>
<div className="font-bold text-color-accent">θ = 20° </div>
<div className="text-fg-default"> </div>
</div>
</div>
</div>
<div
className="p-2 rounded-md text-label-2"
style={{
background: 'rgba(6,182,212,.04)',
border: '1px solid rgba(6,182,212,.1)',
color: 'var(--fg-sub)',
lineHeight: '1.7',
}}
>
<b className="text-color-accent">WING </b> : ( )
α = 2%·θ = 20° OpenDrift wind drift factor
. POSEIDON( 10-1868791) .
</div>
</div>
</div>
</div>
</div>
);
}
/* ═══ 패널 1: 라그랑지안 모델 ═══ */
function LagrangianPanel() {
return (
<div>
<div className="rounded-[10px] p-[14px] mb-4 bg-bg-card border border-stroke">
<div style={labelStyle('var(--fg-default)')}>
🧭 (Lagrangian Particle Tracking)
</div>
<div className="text-label-2 text-fg-sub leading-[1.8]">
<b className="text-color-accent"> (Virtual Particle)</b>
, .
KOSPS·POSEIDON·OpenDrift 3 .
</div>
</div>
<div className="grid grid-cols-2 gap-3 mb-4">
<div className={`${card} ${cardBg}`} style={{ margin: 0 }}>
<div style={labelStyle('var(--color-accent)')}> </div>
<div className={`${codeBox} mb-2.5`}>
dx/dt = U_c + α·U_w + U_stokes + U&apos;
<br />
dy/dt = V_c + α·V_w + V_stokes + V&apos;
</div>
<div className="flex flex-col gap-1 text-label-2 text-fg-sub">
<div className="flex gap-2">
<span style={{ color: 'var(--color-accent)', fontWeight: 700, width: '60px' }}>
U_c, V_c
</span>
<span className="text-fg-sub"> · (m/s)</span>
</div>
<div className="flex gap-2">
<span style={{ color: 'var(--color-info)', fontWeight: 700, width: '60px' }}>
α·U_w
</span>
<span className="text-fg-sub"> (α 0.03)</span>
</div>
<div className="flex gap-2">
<span style={{ color: 'var(--color-accent)', fontWeight: 700, width: '60px' }}>
U_stokes
</span>
<span className="text-fg-sub"> ( )</span>
</div>
<div className="flex gap-2">
<span style={{ color: 'var(--color-accent)', fontWeight: 700, width: '60px' }}>
U&apos;
</span>
<span className="text-fg-sub"> ()</span>
</div>
</div>
</div>
<div className={`${card} ${cardBg}`} style={{ margin: 0 }}>
<div style={labelStyle('var(--color-info)')}> ()</div>
<div className={`${codeBox} mb-2.5`}>
U&apos; = R · (2K_h / Δt)
<br />
V&apos; = R · (2K_h / Δt)
</div>
<div className="flex flex-col gap-1 text-label-2 text-fg-sub">
<div className="flex gap-2">
<span style={{ color: 'var(--color-accent)', fontWeight: 700, width: '60px' }}>
R
</span>
<span className="text-fg-sub">[-1, 1] </span>
</div>
<div className="flex gap-2">
<span style={{ color: 'var(--color-info)', fontWeight: 700, width: '60px' }}>
K_h
</span>
<span className="text-fg-sub"> (m²/s)</span>
</div>
<div className="flex gap-2">
<span style={{ color: 'var(--fg-sub)', fontWeight: 700, width: '60px' }}>Δt</span>
<span className="text-fg-sub"> ( 1)</span>
</div>
</div>
</div>
</div>
{/* Fay(1971) 중력-점성 체제 */}
<div className={`${card} ${cardBg}`}>
<div style={labelStyle('var(--color-info)')}>
🛢 Fay(1971) -
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<div className={`${codeBox} mb-2`}>
<span className="text-fg-default text-label-2">/* 중력-관성 체제 (초기) */</span>
<br />
R(t) = <span className="text-color-accent">K</span> · (
<span className="text-color-accent">ΔρgV²</span> /{' '}
<span className="text-color-info">ρw</span>)<sup>¼</sup> · t<sup>½</sup>
</div>
<div className={codeBox}>
<span className="text-fg-default text-label-2">/* 중력-점성 체제 (후기) */</span>
<br />
R(t) = <span className="text-color-accent">K</span> · (
<span className="text-color-accent">ΔρgV²</span> /{' '}
<span className="text-color-accent">νw</span>)<sup></sup> · t<sup>¾</sup>
</div>
</div>
<div className="flex flex-col gap-1.5 text-label-2 text-fg-sub">
<div
className="px-2.5 py-1.5 rounded-md"
style={{
background: 'rgba(6,182,212,.05)',
border: '1px solid rgba(59,130,246,.15)',
}}
>
<span className="text-color-accent font-bold">Δρ</span> : - (kg/m³)
</div>
<div
className="px-2.5 py-1.5 rounded-md"
style={{ background: 'rgba(6,182,212,.05)', border: '1px solid rgba(6,182,212,.15)' }}
>
<span className="text-color-accent font-bold">g</span> : 9.81 m/s²
</div>
<div
className="px-2.5 py-1.5 rounded-md"
style={{
background: 'rgba(59,130,246,.05)',
border: '1px solid rgba(59,130,246,.15)',
}}
>
<span style={{ color: 'var(--color-info)', fontWeight: 700 }}>V</span> :
(m³)
</div>
<div
className="px-2.5 py-1.5 rounded-md"
style={{
background: 'rgba(6,182,212,.05)',
border: '1px solid rgba(6,182,212,.15)',
}}
>
<span style={{ color: 'var(--color-accent)', fontWeight: 700 }}>νw</span> :
(m²/s)
</div>
</div>
</div>
</div>
</div>
);
}
/* ═══ 패널 2: 풍화 프로세스 ═══ */
function WeatheringPanel() {
return (
<div>
<div className="rounded-[10px] p-[14px] mb-4 bg-bg-card border border-stroke">
<div style={labelStyle('var(--fg-default)')}> (Weathering) </div>
<div className="text-label-2 text-fg-sub leading-[1.8]">
{' '}
<b className="text-color-accent">·· </b>
. WING
.
</div>
</div>
<div className="grid grid-cols-3 gap-3 mb-4">
{[
{
title: '증발(Evaporation)',
color: 'var(--fg-default)',
desc: '유류의 경질 성분이 대기로 증발하는 과정. 유출 초기 수시간 내에 가장 활발하게 발생합니다.',
formula: 'F_e(t) = ∑ x_i·(1 - e^(-k_ei·t))',
note: 'x_i: 성분별 몰분율 / k_ei: 증발 속도상수',
},
{
title: '유화(Emulsification)',
color: 'var(--fg-default)',
desc: '파랑에 의해 해수가 유류에 혼입되어 점도가 크게 증가하는 현상. 방제 작업을 어렵게 만듭니다.',
formula: 'dW_c/dt = K_em·U_w²·(1-W_c/W_max)',
note: 'W_c: 함수율 / K_em: 유화 속도상수',
},
{
title: '자연분산(Dispersion)',
color: 'var(--fg-default)',
desc: '파랑 에너지에 의해 유류가 수중으로 분산되어 유막 두께가 감소하는 과정입니다.',
formula: 'D_r = 0.11·(U_w+U_c)²·(1+U_w)⁻¹',
note: 'Mackay 경험식 기반 분산율 계산',
},
].map((w) => (
<div key={w.title} className={`${card} ${cardBg}`} style={{ margin: 0 }}>
<div style={labelStyle(w.color)}>{w.title}</div>
<div className={`${bodyText} mb-2`}>{w.desc}</div>
<div className={codeBox}>{w.formula}</div>
<div className="mt-2 text-label-2 text-fg-default">{w.note}</div>
</div>
))}
</div>
{/* 타임라인 */}
<div className={`${card} ${cardBg}`}>
<div style={labelStyle('var(--fg-default)')}> ( )</div>
<div className="flex gap-0 items-stretch">
{[
{
time: '0 ~ 6시간',
title: '초기 확산',
color: 'var(--fg-default)',
desc: '증발 20~30%\n유막 급속 확대\n중질유분 잔류',
nextColor: 'var(--color-accent)',
},
{
time: '6 ~ 24시간',
title: '유화 진행',
color: 'var(--color-accent)',
desc: '유화율 50~70%\n점도 급증\n체적 1.5~4배',
nextColor: 'var(--color-info)',
},
{
time: '24 ~ 72시간',
title: '안정화',
color: 'var(--color-info)',
desc: '수중 분산 증가\n자연분해 시작\n해안 표착 위험',
nextColor: 'var(--fg-disabled)',
},
{
time: '72시간 이후',
title: '타르볼 생성',
color: 'var(--fg-disabled)',
desc: '타르볼 형성\n생물분해 우세\n장기 잔류',
},
].map((s, i) => (
<div key={s.time} className="flex-1 flex items-stretch">
<div
className="flex-1 p-2.5 text-center"
style={{
background: `${s.color}0F`,
border: `1px solid ${s.color}33`,
borderRadius: i === 0 ? '8px 0 0 8px' : i === 3 ? '0 8px 8px 0' : '0',
}}
>
<div className="text-label-2 font-bold mb-1" style={{ color: s.color }}>
{s.time}
</div>
<div className="text-label-2 font-medium mb-1">{s.title}</div>
<div className="text-label-2 whitespace-pre-line text-fg-default leading-normal">
{s.desc}
</div>
</div>
{s.nextColor && (
<div
style={{
width: '2px',
background: 'var(--stroke-default)',
}}
/>
)}
</div>
))}
</div>
</div>
</div>
);
}
/* ═══ 패널 3: 해양환경 입력 ═══ */
function OceanInputPanel() {
return (
<div>
<div className="rounded-[10px] p-[14px] mb-4 bg-bg-card border border-stroke">
<div style={labelStyle('var(--fg-default)')}> </div>
<div className="text-label-2 text-fg-sub leading-[1.8]">
{' '}
<b className="text-color-accent"> · </b> . WING
.
</div>
</div>
<div className="grid grid-cols-2 gap-3">
<div className={`${card} ${cardBg}`} style={{ margin: 0 }}>
<div style={labelStyle('var(--color-info)')}> </div>
<div className="flex flex-col gap-1.5 text-label-2 text-fg-sub">
{[
{ label: 'KMA RDAPS', desc: '동아시아 12km 해상도 · 24h 예보' },
{ label: 'ECMWF ERA5', desc: '전지구 0.25° 재분석 데이터' },
{ label: 'AWS 자동기상관측', desc: '실시간 10분 단위 풍향·풍속' },
].map((t) => (
<div
key={t.label}
className="rounded-md"
style={{
padding: '7px 10px',
background: 'rgba(6,182,212,.04)',
border: '1px solid rgba(6,182,212,.12)',
}}
>
<div className="font-medium mb-0.5">{t.label}</div>
<div className="text-fg-default">{t.desc}</div>
</div>
))}
</div>
</div>
<div className={`${card} ${cardBg}`} style={{ margin: 0 }}>
<div style={labelStyle('var(--color-accent)')}> </div>
<div className="flex flex-col gap-1.5 text-label-2 text-fg-sub">
{[
{ label: 'NIFS ROMS', desc: '한국 근해 1km 해류·수온 예보' },
{ label: 'HYCOM', desc: '전지구 1/12° 해양 순환 모델' },
{ label: 'TPXO9 조화분석', desc: '15개 분조 기반 조류 예측' },
].map((t) => (
<div
key={t.label}
className="rounded-md"
style={{
padding: '7px 10px',
background: 'rgba(6,182,212,.04)',
border: '1px solid rgba(6,182,212,.12)',
}}
>
<div className="font-medium mb-0.5">{t.label}</div>
<div className="text-fg-default">{t.desc}</div>
</div>
))}
</div>
</div>
</div>
</div>
);
}
/* ═══ 패널 4: 모델 검증 ═══ */
function VerificationPanel() {
const [papersOpen, setPapersOpen] = useState(false);
return (
<div>
<div className="rounded-[10px] p-[14px] mb-4 bg-bg-card border border-stroke">
<div style={labelStyle('var(--fg-default)')}> </div>
<div className="text-label-2 text-fg-sub leading-[1.8]">
WING {' '}
<b className="text-color-info"> </b>
.
</div>
</div>
<div className={`${card} ${cardBg}`}>
<div className="flex items-center justify-between mb-2">
<div style={labelStyle('var(--fg-default)')}>2007 </div>
<span
className="text-label-2 px-2 py-0.5 rounded"
style={{
background: 'var(--bg-elevated)',
color: 'var(--color-accent)',
fontWeight: 500,
}}
>
</span>
</div>
<div className="grid grid-cols-4 gap-2">
{[
{ value: '78.4%', label: '해안 표착 예측 일치율', color: 'var(--color-accent)' },
{ value: '±12%', label: '유막 면적 오차', color: 'var(--color-accent)' },
{ value: '±8h', label: '해안 도달 시간 오차', color: 'var(--color-accent)' },
{ value: '3종', label: '비교 검증 모델 수', color: 'var(--color-accent)' },
].map((s) => (
<div key={s.label} className="text-center p-2 rounded-md bg-bg-base">
<div
className="font-bold"
style={{
fontSize: 'var(--font-size-title-3)',
color: s.color,
fontFamily: 'var(--font-mono)',
}}
>
{s.value}
</div>
<div className="text-label-2 text-fg-default">{s.label}</div>
</div>
))}
</div>
</div>
{/* 2014년 우이산 충돌 사고 재현 */}
<div className={`${card} ${cardBg}`}>
<div className="flex items-center justify-between mb-2">
<div style={labelStyle('var(--fg-default)')}>2014 </div>
<span
className="text-label-2 px-2 py-0.5 rounded"
style={{
background: 'var(--bg-elevated)',
color: 'var(--color-accent)',
fontWeight: 500,
}}
>
</span>
</div>
<div className="text-label-2 text-fg-sub leading-[1.6]">
KOSPS , SAR {' '}
<b className="text-color-accent"> 82.1%</b> .
.
</div>
</div>
{/* 관련 논문 */}
<div className={`${card} ${cardBg}`}>
<div
className="flex items-center gap-1.5 mb-2 cursor-pointer select-none"
onClick={() => setPapersOpen(!papersOpen)}
>
<span
style={{
...tag('var(--fg-sub)'),
fontWeight: 700,
fontSize: 'var(--font-size-label-1)',
}}
>
📄
</span>
<span
className="text-label-2 ml-1"
style={{
color: 'var(--fg-disabled)',
transition: 'transform .2s',
display: 'inline-block',
transform: papersOpen ? 'rotate(180deg)' : 'rotate(0)',
}}
>
</span>
</div>
{papersOpen && (
<div className="flex flex-col gap-1.5 text-label-2 text-fg-sub">
{[
{
num: '①',
title: '허베이스피리트호 유출유 확산예측 검증 분석',
authors: '이문진 · 김선동 · 김혜진 · 오세웅',
journal: '한국해양과학기술협의회 공동학술대회',
detail: 'pp.3154 | 2010',
desc: 'Radarsat 인공위성영상 비교 분석 · NOAA GNOME과 동일 입력조건 하 비교 검증',
color: 'var(--color-info)',
system: 'KOSPS',
},
{
num: '②',
title: '3차원 유출유 확산예측 시스템 연구',
authors: '이문진 · 김혜진 · 강관근',
journal: '한국해양과학기술협의회 공동학술대회',
detail: 'pp.17-18 | 2013',
desc: 'Monte Carlo Simulation 기반 3D 확산모델 · 허베이 스피리트호 720시간 적용 검증',
color: 'var(--color-accent)',
system: 'KOSPS',
},
{
num: '③',
title: '유출유 확산예측 모델의 해양사고 적용 및 개선방안 연구',
authors: '이문진 · 김혜진',
journal: '한국해양과학기술협의회 공동학술대회',
detail: 'pp.2353 | 2014',
desc: 'GS칼텍스 송유관 · Captain Vangelis 유출사고 적용 검증 · 현장 보고자료 비교 분석',
color: 'var(--fg-default)',
system: 'KOSPS',
},
{
num: '④',
title: '해양유류오염사고 위해도 평가에 관한 연구',
authors: '이문진 · 김혜진',
journal: '한국해양공학회지',
detail: '제23권 1호, pp.24-30 | 2009',
desc: '20년 과거자료 기반 100회 몬테카를로 통계 위험평가 · 가로림만 해안/어장 도달확률 산정',
color: 'var(--color-accent)',
system: 'KOSPS',
},
{
num: '⑤',
title: '한반도 인근 해역 MOHID 지역 해양순환 모델 검증',
authors: '이재호 · 임병준 · 김도연 외',
journal: '한국지구과학회지',
detail: '제39권 5호, pp.436-457 | 2018',
desc: 'POSEIDON 기반 MOHID 모델 동아시아 해역 2016년 검증 · SST RMSE 0.42~0.78°C',
color: 'var(--color-info)',
system: 'POSEIDON',
},
{
num: '⑥',
title: '원격탐사 기반의 유출유 확산예측 및 해양오염 방제 지원',
authors: '김도연 · 김충기 · 양찬수',
journal: '한국해양환경·에너지학회 학술대회논문집',
detail: 'pp.79 | 2022',
desc: '위성 원격탐사(SAR·광학) 기반 유출유 탐지 · POSEIDON 확산예측 모델 연동 검증',
color: 'var(--color-info)',
system: 'POSEIDON',
},
{
num: '⑦',
title: 'OpenDrift v1.0: a generic framework for trajectory modelling',
authors: 'Dagestad et al.',
journal: 'Geoscientific Model Development',
detail: 'Vol.11, pp.1405-1420 | 2018',
desc: 'OpenDrift 프레임워크 설계·구현·검증 · OpenOil 유출유 모듈 다중 사례 검증',
color: 'var(--color-accent)',
system: 'OpenDrift',
},
{
num: '⑧',
title: 'Observation-based evaluation of surface wave effects on currents',
authors: 'Röhrs et al.',
journal: 'J. Geophys. Res. Oceans',
detail: '| 2013',
desc: 'Stokes drift 파랑 기여 효과 · OpenOil 유출유 확산 현장 관측 검증',
color: 'var(--color-accent)',
system: 'OpenDrift',
},
{
num: '⑨',
title: 'Numerical modelling and fate assessment of oil spills at sea',
authors: 'Nordam et al.',
journal: 'Marine Pollution Bulletin',
detail: '| 2019',
desc: '3D 유출유 수직 분포 모의 · 해저면 도달 경로 분석 · 풍화 통합 수치 검증',
color: 'var(--color-accent)',
system: 'OpenDrift',
},
{
num: '⑩',
title: '유출유의 초기 확산예측을 위한 고해상도 결합모형 개발',
authors: '손상영 · 이칠우 · 윤현덕 · 정태화',
journal: '한국해안·해양공학회논문집',
detail: '제29권 4호, pp.189-197 | 2017',
desc: 'Boussinesq + MEDSLIK-II 결합 모형 · 울산 진하해역 시뮬레이션 검증',
color: 'var(--color-accent)',
system: '결합모형',
},
].map((paper) => (
<div
key={paper.num}
className="rounded-lg p-2.5"
style={{
background: 'var(--bg-base)',
border: '1px solid var(--stroke-default)',
display: 'grid',
gridTemplateColumns: '28px 1fr',
gap: '10px',
alignItems: 'start',
}}
>
<div
className="w-6 h-6 rounded-[5px] flex items-center justify-center text-label-2 shrink-0"
style={{ background: `${paper.color}1F` }}
>
{paper.num}
</div>
<div>
<div className="flex items-center gap-1.5 mb-0.5">
<div className="font-bold">{paper.title}</div>
<span
style={{
...tag(paper.color),
fontSize: 'var(--font-size-caption)',
padding: '1px 5px',
}}
>
{paper.system}
</span>
</div>
<div className="text-fg-default leading-[1.6]">
{paper.authors} | <b style={{ color: paper.color }}>{paper.journal}</b>{' '}
{paper.detail}
</div>
<div className="mt-1 text-fg-sub">{paper.desc}</div>
</div>
</div>
))}
</div>
)}
</div>
</div>
);
}
/* ═══ 패널 5: 앙상블 ═══ */
function EnsemblePanel() {
return (
<div>
<div className="rounded-[10px] p-[14px] mb-4 bg-bg-card border border-stroke">
<div style={labelStyle('var(--fg-default)')}> (Ensemble Prediction)</div>
<div className="text-label-2 text-fg-sub leading-[1.8]">
WING 3 (KOSPS·POSEIDON·OpenDrift) {' '}
<b className="text-color-accent"> </b> .
.
</div>
</div>
<div className="grid grid-cols-2 gap-3">
<div className={`${card} ${cardBg}`} style={{ margin: 0 }}>
<div style={labelStyle('var(--color-accent)')}> </div>
<div className={`${codeBox} mb-2`}>
P_ens(x,t) = w·P_KOSPS + w·P_POSEIDON + w·P_OpenDrift
</div>
<div className={bodyText}>
. 기본값: 1/3
.
</div>
</div>
<div className={`${card} ${cardBg}`} style={{ margin: 0 }}>
<div style={labelStyle('var(--color-accent)')}> (Worst Case)</div>
<div className={bodyText}>
3 {' '}
<b className="text-color-accent"> </b>
. .
</div>
</div>
</div>
</div>
);
}
/* ═══ 패널 6: 발전 방향 ═══ */
function RoadmapPanel() {
return (
<div>
<div className="grid grid-cols-2 gap-3 mb-4">
<div className={`${card} ${cardBg}`} className="m-0">
<div style={labelStyle('var(--color-info)')}> </div>
<div className="flex flex-col gap-2 text-label-2 text-fg-sub">
<div
className="rounded-md"
style={{
padding: '8px 10px',
background: 'rgba(59,130,246,.04)',
border: '1px solid rgba(59,130,246,.12)',
color: 'var(--fg-sub)',
}}
>
2D <b>3D </b>
</div>
<div
className="rounded-md"
style={{
padding: '8px 10px',
background: 'rgba(59,130,246,.04)',
border: '1px solid rgba(59,130,246,.12)',
color: 'var(--fg-sub)',
}}
>
· <b> </b>
</div>
<div
className="rounded-md"
style={{
padding: '8px 10px',
background: 'rgba(59,130,246,.04)',
border: '1px solid rgba(59,130,246,.12)',
color: 'var(--fg-sub)',
}}
>
<b>· </b>
</div>
</div>
</div>
<div className={`${card} ${cardBg}`} className="m-0">
<div style={labelStyle('var(--color-info)')}> </div>
<div className="flex flex-col gap-2 text-label-2 text-fg-sub">
{[
{ title: 'ROMS 3D 해양모델 결합', desc: '광역→상세역 Nesting 200m급 고해상도 구현' },
{ title: 'AI/ML 서로게이트 모델', desc: '수치모델 학습 기반 수초 내 근사 예측' },
{ title: '위성 SAR 실시간 동화', desc: '관측 유막 데이터 모델 보정 자동화' },
].map((r) => (
<div
key={r.title}
className="rounded-md"
style={{
padding: '8px 10px',
background: 'rgba(6,182,212,.04)',
border: '1px solid rgba(6,182,212,.12)',
}}
>
<div className="font-medium mb-0.5">{r.title}</div>
<div className="text-label-2 text-fg-default">{r.desc}</div>
</div>
))}
</div>
</div>
</div>
{/* 로드맵 */}
<div className={`${card} ${cardBg}`}>
<div style={labelStyle('var(--fg-default)')}>WING </div>
<div className="flex gap-0 items-stretch">
{[
{
phase: '✅ Phase 1 (완료)',
title: '2D 앙상블',
color: 'var(--color-accent)',
desc: 'KOSPS + POSEIDON\n+ OpenDrift 3종\n표층 확산 앙상블',
nextColor: 'var(--color-accent)',
},
{
phase: '🔧 Phase 2 (진행중)',
title: 'ROMS 결합',
color: 'var(--color-accent)',
desc: '3D 수직 구조\n고해상도 연안\n조류 정밀 반영',
nextColor: 'var(--color-accent)',
},
{
phase: '🔬 Phase 3 (계획)',
title: 'AI 융합',
color: 'var(--color-accent)',
desc: 'ML 서로게이트\n위성 SAR 동화\n실시간 보정',
nextColor: 'var(--color-info)',
},
{
phase: '🎯 Phase 4 (목표)',
title: '자동 대응',
color: 'var(--color-accent)',
desc: '사고감지→예측\n→방제자원 배치\n전 과정 자동화',
},
].map((s, i) => (
<div key={s.phase} className="flex-1 flex items-stretch">
<div
className="flex-1 p-2.5 text-center"
style={{
background: `${s.color}0F`,
border: `1px solid ${s.color}33`,
borderRadius: i === 0 ? '8px 0 0 8px' : i === 3 ? '0 8px 8px 0' : '0',
}}
>
<div className="text-label-2 font-bold mb-1" style={{ color: s.color }}>
{s.phase}
</div>
<div className="text-label-2 font-medium mb-1">{s.title}</div>
<div className="text-label-2 whitespace-pre-line text-fg-default leading-normal">
{s.desc}
</div>
</div>
{s.nextColor && (
<div
style={{
width: '2px',
background: 'var(--stroke-default)',
}}
/>
)}
</div>
))}
</div>
</div>
</div>
);
}