guohanghui commited on
Commit
fc17224
·
verified ·
1 Parent(s): b6cc76c

Update pyPDAF/mcp_output/mcp_plugin/mcp_service.py

Browse files
pyPDAF/mcp_output/mcp_plugin/mcp_service.py CHANGED
@@ -8,23 +8,30 @@ This module provides two MCP tools:
8
 
9
  import sys
10
  import os
11
- # Add source path to import pyPDAF
12
- sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../source/src'))
13
-
14
  import numpy as np
15
  import netCDF4 as nc
16
  from fastmcp import FastMCP
17
  from typing import Dict, Any, Tuple
18
  import glob
19
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  try:
21
  import pyPDAF
22
- import pyPDAF.PDAF as PDAF
23
- import pyPDAF.PDAFomi as PDAFomi
24
  PYPDAF_AVAILABLE = True
25
- except ImportError:
26
  PYPDAF_AVAILABLE = False
27
- print("Warning: pyPDAF not available, run_enoi_pipeline will not work")
28
 
29
  # Initialize FastMCP server
30
  mcp = FastMCP("pyPDAF MCP Server")
@@ -178,10 +185,7 @@ def _gaspari_cohn(r: float, c: float) -> float:
178
  # MCP Tool 1: Generate Demo Ocean Data
179
  # ============================================================================
180
 
181
- @mcp.tool(
182
- name="generate_demo_ocean_data",
183
- description="Generate synthetic ocean data for EnOI testing. Creates restart.nc (background state), ensemble members, and temperature observations at surface."
184
- )
185
  def generate_demo_ocean_data(
186
  nx: int = 20,
187
  ny: int = 20,
@@ -310,10 +314,7 @@ def generate_demo_ocean_data(
310
  # MCP Tool 2: Run EnOI Pipeline with Localization
311
  # ============================================================================
312
 
313
- @mcp.tool(
314
- name="run_enoi_pipeline",
315
- description="Run Ensemble Optimal Interpolation (EnOI) data assimilation pipeline with optional Gaspari-Cohn localization. Computes analysis state from background, ensemble, and observations."
316
- )
317
  def run_enoi_pipeline(
318
  restart_path: str = "restart.nc",
319
  ensemble_dir: str = "ensemble_data",
@@ -417,7 +418,7 @@ def run_enoi_pipeline(
417
  # ========================================================================
418
 
419
  # Initialize PDAFomi with 1 observation type
420
- PDAFomi.init(1)
421
 
422
  # Setup localization if requested
423
  if localization_radius > 0:
@@ -425,10 +426,10 @@ def run_enoi_pipeline(
425
  radius_deg = localization_radius / 111.0
426
 
427
  # Initialize local analysis
428
- PDAFomi.init_local()
429
 
430
  # Set localization
431
- PDAF.set_localfilter(1) # Enable local filter
432
 
433
  # Observation class to handle PDAFomi callbacks
434
  class ObsHandler:
@@ -438,9 +439,9 @@ def run_enoi_pipeline(
438
  def init_dim_obs_pdafomi(self, step, dim_obs):
439
  """Initialize observation dimension"""
440
  # Set OMI parameters
441
- PDAFomi.set_doassim(self.i_obs, 1) # Assimilate this obs type
442
- PDAFomi.set_disttype(self.i_obs, 0) # Cartesian distance
443
- PDAFomi.set_ncoord(self.i_obs, 2) # 2D coordinates
444
 
445
  # Create coordinate array for observations
446
  ocoord_p = np.zeros((2, n_obs), order='F')
@@ -455,12 +456,12 @@ def run_enoi_pipeline(
455
  id_obs_p[0, :] = obs_indices + 1 # Fortran 1-indexing
456
 
457
  # Gather observation information
458
- PDAFomi.set_id_obs_p(self.i_obs, 1, n_obs, id_obs_p)
459
- PDAFomi.set_ivar_obs_p(self.i_obs, n_obs, ivar_obs)
460
- PDAFomi.set_ocoord_p(self.i_obs, 2, n_obs, ocoord_p)
461
 
462
  # Set observation values
463
- PDAFomi.set_obs_p(self.i_obs, n_obs, obs_temp)
464
 
465
  return n_obs
466
 
@@ -581,7 +582,7 @@ def run_enoi_pipeline(
581
  rms_innovation = float(np.sqrt(np.mean(innovation**2)))
582
 
583
  # Finalize PDAF
584
- PDAF.deallocate()
585
 
586
  return {
587
  "status": "success",
 
8
 
9
  import sys
10
  import os
 
 
 
11
  import numpy as np
12
  import netCDF4 as nc
13
  from fastmcp import FastMCP
14
  from typing import Dict, Any, Tuple
15
  import glob
16
 
17
+ # Try to add source path to import pyPDAF
18
+ _current_dir = os.path.dirname(os.path.abspath(__file__))
19
+ _source_paths = [
20
+ os.path.join(_current_dir, '../../source/src'), # Local development
21
+ os.path.join(_current_dir, '../../../source/src'), # Alternative path
22
+ '/app/pyPDAF', # HuggingFace Spaces path
23
+ ]
24
+
25
+ for _path in _source_paths:
26
+ if os.path.exists(_path) and _path not in sys.path:
27
+ sys.path.insert(0, _path)
28
+
29
  try:
30
  import pyPDAF
 
 
31
  PYPDAF_AVAILABLE = True
32
+ except ImportError as e:
33
  PYPDAF_AVAILABLE = False
34
+ print(f"Warning: pyPDAF not available: {e}")
35
 
36
  # Initialize FastMCP server
37
  mcp = FastMCP("pyPDAF MCP Server")
 
185
  # MCP Tool 1: Generate Demo Ocean Data
186
  # ============================================================================
187
 
188
+ @mcp.tool(name="generate_demo_ocean_data", description="Generate synthetic ocean data for EnOI testing. Creates restart.nc (background state), ensemble members, and temperature observations at surface.")
 
 
 
189
  def generate_demo_ocean_data(
190
  nx: int = 20,
191
  ny: int = 20,
 
314
  # MCP Tool 2: Run EnOI Pipeline with Localization
315
  # ============================================================================
316
 
317
+ @mcp.tool(name="run_enoi_pipeline", description="Run Ensemble Optimal Interpolation (EnOI) data assimilation pipeline with optional Gaspari-Cohn localization. Computes analysis state from background, ensemble, and observations.")
 
 
 
318
  def run_enoi_pipeline(
319
  restart_path: str = "restart.nc",
320
  ensemble_dir: str = "ensemble_data",
 
418
  # ========================================================================
419
 
420
  # Initialize PDAFomi with 1 observation type
421
+ pyPDAF.PDAFomi.init(1)
422
 
423
  # Setup localization if requested
424
  if localization_radius > 0:
 
426
  radius_deg = localization_radius / 111.0
427
 
428
  # Initialize local analysis
429
+ pyPDAF.PDAFomi.init_local()
430
 
431
  # Set localization
432
+ pyPDAF.PDAF.set_localfilter(1) # Enable local filter
433
 
434
  # Observation class to handle PDAFomi callbacks
435
  class ObsHandler:
 
439
  def init_dim_obs_pdafomi(self, step, dim_obs):
440
  """Initialize observation dimension"""
441
  # Set OMI parameters
442
+ pyPDAF.PDAFomi.set_doassim(self.i_obs, 1) # Assimilate this obs type
443
+ pyPDAF.PDAFomi.set_disttype(self.i_obs, 0) # Cartesian distance
444
+ pyPDAF.PDAFomi.set_ncoord(self.i_obs, 2) # 2D coordinates
445
 
446
  # Create coordinate array for observations
447
  ocoord_p = np.zeros((2, n_obs), order='F')
 
456
  id_obs_p[0, :] = obs_indices + 1 # Fortran 1-indexing
457
 
458
  # Gather observation information
459
+ pyPDAF.PDAFomi.set_id_obs_p(self.i_obs, 1, n_obs, id_obs_p)
460
+ pyPDAF.PDAFomi.set_ivar_obs_p(self.i_obs, n_obs, ivar_obs)
461
+ pyPDAF.PDAFomi.set_ocoord_p(self.i_obs, 2, n_obs, ocoord_p)
462
 
463
  # Set observation values
464
+ pyPDAF.PDAFomi.set_obs_p(self.i_obs, n_obs, obs_temp)
465
 
466
  return n_obs
467
 
 
582
  rms_innovation = float(np.sqrt(np.mean(innovation**2)))
583
 
584
  # Finalize PDAF
585
+ pyPDAF.PDAF.deallocate()
586
 
587
  return {
588
  "status": "success",