guohanghui commited on
Commit
17e9aab
·
verified ·
1 Parent(s): 26833c6

Update pysph/mcp_output/mcp_plugin/mcp_service.py

Browse files
pysph/mcp_output/mcp_plugin/mcp_service.py CHANGED
@@ -1,5 +1,6 @@
1
  import os
2
  import sys
 
3
 
4
  # Add the local source directory to sys.path
5
  source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source")
@@ -7,69 +8,875 @@ if source_path not in sys.path:
7
  sys.path.insert(0, source_path)
8
 
9
  from fastmcp import FastMCP
 
10
 
11
  # Import core modules from the local source
12
- from pysph.solver.application import Application
13
- from pysph.sph.scheme import Scheme
 
14
  from pysph.sph.integrator import Integrator
15
 
16
  # Create the MCP service application
17
  mcp = FastMCP("pysph_service")
18
 
19
- @mcp.tool(name="run_simulation", description="Run a SPH simulation using the Application class.")
20
- def run_simulation(config_file: str) -> dict:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  """
22
- Run a SPH simulation using the provided configuration file.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  Parameters:
25
- - config_file (str): Path to the configuration file for the simulation.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
 
 
 
 
 
 
 
 
 
27
  Returns:
28
- - dict: A dictionary containing success, result, or error information.
29
  """
30
  try:
31
- app = Application(config_file=config_file)
32
- app.run()
33
- return {"success": True, "result": "Simulation completed successfully."}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  except Exception as e:
35
  return {"success": False, "error": str(e)}
36
 
37
- @mcp.tool(name="create_scheme", description="Create a SPH scheme using the Scheme class.")
38
- def create_scheme(scheme_name: str, parameters: dict) -> dict:
 
 
39
  """
40
- Create a SPH scheme with the given name and parameters.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
 
 
 
 
 
 
 
 
42
  Parameters:
43
- - scheme_name (str): Name of the scheme to create.
44
- - parameters (dict): Parameters for the scheme.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
 
 
 
 
 
 
 
 
 
 
46
  Returns:
47
- - dict: A dictionary containing success, result, or error information.
48
  """
49
  try:
50
- scheme = Scheme(name=scheme_name, **parameters)
51
- return {"success": True, "result": f"Scheme '{scheme_name}' created successfully."}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  except Exception as e:
53
  return {"success": False, "error": str(e)}
54
 
55
- @mcp.tool(name="initialize_integrator", description="Initialize an integrator using the Integrator class.")
56
- def initialize_integrator(integrator_type: str, options: dict) -> dict:
 
 
 
 
 
57
  """
58
- Initialize an integrator with the specified type and options.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
 
 
 
 
 
 
60
  Parameters:
61
- - integrator_type (str): Type of the integrator to initialize.
62
- - options (dict): Options for the integrator.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  Returns:
65
- - dict: A dictionary containing success, result, or error information.
66
  """
67
  try:
68
- integrator = Integrator(type=integrator_type, **options)
69
- return {"success": True, "result": f"Integrator '{integrator_type}' initialized successfully."}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  except Exception as e:
71
  return {"success": False, "error": str(e)}
72
 
 
73
  def create_app() -> FastMCP:
74
  """
75
  Create and return the FastMCP instance for the service.
 
1
  import os
2
  import sys
3
+ from typing import List, Optional, Dict, Any
4
 
5
  # Add the local source directory to sys.path
6
  source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source")
 
8
  sys.path.insert(0, source_path)
9
 
10
  from fastmcp import FastMCP
11
+ import numpy as np
12
 
13
  # Import core modules from the local source
14
+ from pysph.tools import geometry
15
+ from pysph.base import kernels
16
+ from pysph.base.utils import get_particle_array
17
  from pysph.sph.integrator import Integrator
18
 
19
  # Create the MCP service application
20
  mcp = FastMCP("pysph_service")
21
 
22
+ # =====================================================
23
+ # Geometry Tools - 几何工具
24
+ # =====================================================
25
+
26
+ @mcp.tool(name="create_2d_circle", description="Create a 2D circular particle distribution.")
27
+ def create_2d_circle(dx: float = 0.01, radius: float = 1.0,
28
+ center_x: float = 0.0, center_y: float = 0.0) -> dict:
29
+ """
30
+ Create a 2D circular particle distribution.
31
+
32
+ Parameters:
33
+ - dx (float): Particle spacing (default: 0.01)
34
+ - radius (float): Radius of the circle (default: 1.0)
35
+ - center_x (float): X coordinate of center (default: 0.0)
36
+ - center_y (float): Y coordinate of center (default: 0.0)
37
+
38
+ Returns:
39
+ - dict: Contains x, y coordinates and particle count.
40
+ """
41
+ try:
42
+ center = np.array([center_x, center_y])
43
+ x, y = geometry.get_2d_circle(dx=dx, r=radius, center=center)
44
+ return {
45
+ "success": True,
46
+ "num_particles": len(x),
47
+ "x": x.tolist(),
48
+ "y": y.tolist()
49
+ }
50
+ except Exception as e:
51
+ return {"success": False, "error": str(e)}
52
+
53
+
54
+ @mcp.tool(name="create_2d_block", description="Create a 2D rectangular block of particles.")
55
+ def create_2d_block(dx: float = 0.01, length: float = 1.0, height: float = 1.0,
56
+ center_x: float = 0.0, center_y: float = 0.0) -> dict:
57
+ """
58
+ Create a 2D rectangular block of particles.
59
+
60
+ Parameters:
61
+ - dx (float): Particle spacing (default: 0.01)
62
+ - length (float): Length of the block in x direction (default: 1.0)
63
+ - height (float): Height of the block in y direction (default: 1.0)
64
+ - center_x (float): X coordinate of center (default: 0.0)
65
+ - center_y (float): Y coordinate of center (default: 0.0)
66
+
67
+ Returns:
68
+ - dict: Contains x, y coordinates and particle count.
69
+ """
70
+ try:
71
+ center = np.array([center_x, center_y])
72
+ x, y = geometry.get_2d_block(dx=dx, length=length, height=height, center=center)
73
+ return {
74
+ "success": True,
75
+ "num_particles": len(x),
76
+ "x": x.tolist(),
77
+ "y": y.tolist()
78
+ }
79
+ except Exception as e:
80
+ return {"success": False, "error": str(e)}
81
+
82
+
83
+ @mcp.tool(name="create_3d_sphere", description="Create a 3D spherical particle distribution.")
84
+ def create_3d_sphere(dx: float = 0.05, radius: float = 0.5,
85
+ center_x: float = 0.0, center_y: float = 0.0,
86
+ center_z: float = 0.0) -> dict:
87
+ """
88
+ Create a 3D spherical particle distribution.
89
+
90
+ Parameters:
91
+ - dx (float): Particle spacing (default: 0.05)
92
+ - radius (float): Radius of the sphere (default: 0.5)
93
+ - center_x, center_y, center_z: Coordinates of center
94
+
95
+ Returns:
96
+ - dict: Contains x, y, z coordinates and particle count.
97
+ """
98
+ try:
99
+ center = np.array([center_x, center_y, center_z])
100
+ x, y, z = geometry.get_3d_sphere(dx=dx, r=radius, center=center)
101
+ return {
102
+ "success": True,
103
+ "num_particles": len(x),
104
+ "x": x.tolist(),
105
+ "y": y.tolist(),
106
+ "z": z.tolist()
107
+ }
108
+ except Exception as e:
109
+ return {"success": False, "error": str(e)}
110
+
111
+
112
+ @mcp.tool(name="create_3d_block", description="Create a 3D rectangular block of particles.")
113
+ def create_3d_block(dx: float = 0.05, length: float = 1.0, height: float = 1.0,
114
+ depth: float = 1.0, center_x: float = 0.0,
115
+ center_y: float = 0.0, center_z: float = 0.0) -> dict:
116
+ """
117
+ Create a 3D rectangular block of particles.
118
+
119
+ Parameters:
120
+ - dx (float): Particle spacing (default: 0.05)
121
+ - length (float): Length in x direction (default: 1.0)
122
+ - height (float): Height in y direction (default: 1.0)
123
+ - depth (float): Depth in z direction (default: 1.0)
124
+ - center_x, center_y, center_z: Coordinates of center
125
+
126
+ Returns:
127
+ - dict: Contains x, y, z coordinates and particle count.
128
+ """
129
+ try:
130
+ center = np.array([center_x, center_y, center_z])
131
+ x, y, z = geometry.get_3d_block(dx=dx, length=length, height=height,
132
+ depth=depth, center=center)
133
+ return {
134
+ "success": True,
135
+ "num_particles": len(x),
136
+ "x": x.tolist(),
137
+ "y": y.tolist(),
138
+ "z": z.tolist()
139
+ }
140
+ except Exception as e:
141
+ return {"success": False, "error": str(e)}
142
+
143
+
144
+ @mcp.tool(name="translate_particles", description="Translate particle positions in 3D space.")
145
+ def translate_particles(x: List[float], y: List[float], z: List[float],
146
+ x_translate: float = 0.0, y_translate: float = 0.0,
147
+ z_translate: float = 0.0) -> dict:
148
+ """
149
+ Translate particle positions by specified amounts.
150
+
151
+ Parameters:
152
+ - x, y, z: Lists of particle coordinates
153
+ - x_translate, y_translate, z_translate: Translation amounts
154
+
155
+ Returns:
156
+ - dict: Contains translated x, y, z coordinates.
157
  """
158
+ try:
159
+ x_new, y_new, z_new = geometry.translate(
160
+ np.array(x), np.array(y), np.array(z),
161
+ x_translate, y_translate, z_translate
162
+ )
163
+ return {
164
+ "success": True,
165
+ "num_particles": len(x_new),
166
+ "x": x_new.tolist(),
167
+ "y": y_new.tolist(),
168
+ "z": z_new.tolist()
169
+ }
170
+ except Exception as e:
171
+ return {"success": False, "error": str(e)}
172
+
173
+
174
+ @mcp.tool(name="rotate_particles", description="Rotate particle positions around an axis.")
175
+ def rotate_particles(x: List[float], y: List[float], z: List[float],
176
+ axis_x: float = 0.0, axis_y: float = 0.0, axis_z: float = 1.0,
177
+ angle: float = 90.0) -> dict:
178
+ """
179
+ Rotate particle positions around a specified axis.
180
+
181
+ Parameters:
182
+ - x, y, z: Lists of particle coordinates
183
+ - axis_x, axis_y, axis_z: Components of rotation axis (default: z-axis)
184
+ - angle: Rotation angle in degrees (default: 90.0)
185
+
186
+ Returns:
187
+ - dict: Contains rotated x, y, z coordinates.
188
+ """
189
+ try:
190
+ axis = np.array([axis_x, axis_y, axis_z])
191
+ x_new, y_new, z_new = geometry.rotate(
192
+ np.array(x), np.array(y), np.array(z), axis, angle
193
+ )
194
+ return {
195
+ "success": True,
196
+ "num_particles": len(x_new),
197
+ "x": x_new.tolist(),
198
+ "y": y_new.tolist(),
199
+ "z": z_new.tolist()
200
+ }
201
+ except Exception as e:
202
+ return {"success": False, "error": str(e)}
203
+
204
+
205
+ @mcp.tool(name="extrude_2d_to_3d", description="Extrude a 2D geometry into 3D along the z-axis.")
206
+ def extrude_2d_to_3d(x: List[float], y: List[float], dx: float = 0.01,
207
+ extrude_dist: float = 1.0, z_center: float = 0.0) -> dict:
208
+ """
209
+ Extrude a 2D geometry into 3D along the z-axis.
210
+
211
+ Parameters:
212
+ - x, y: Lists of 2D particle coordinates
213
+ - dx (float): Particle spacing for extrusion (default: 0.01)
214
+ - extrude_dist (float): Total extrusion distance (default: 1.0)
215
+ - z_center (float): Center z coordinate for extrusion (default: 0.0)
216
+
217
+ Returns:
218
+ - dict: Contains 3D x, y, z coordinates.
219
+ """
220
+ try:
221
+ x_new, y_new, z_new = geometry.extrude(
222
+ np.array(x), np.array(y), dx, extrude_dist, z_center
223
+ )
224
+ return {
225
+ "success": True,
226
+ "num_particles": len(x_new),
227
+ "x": x_new.tolist(),
228
+ "y": y_new.tolist(),
229
+ "z": z_new.tolist()
230
+ }
231
+ except Exception as e:
232
+ return {"success": False, "error": str(e)}
233
+
234
+
235
+ @mcp.tool(name="create_2d_wall", description="Create a 2D wall (line of particles) parallel to x-axis.")
236
+ def create_2d_wall(dx: float = 0.01, center_x: float = 0.0, center_y: float = 0.0,
237
+ length: float = 1.0, num_layers: int = 1, up: bool = True) -> dict:
238
+ """
239
+ Create a 2D wall (line of particles) parallel to x-axis.
240
+
241
+ Parameters:
242
+ - dx (float): Particle spacing (default: 0.01)
243
+ - center_x, center_y: Center coordinates of the wall
244
+ - length (float): Length of the wall (default: 1.0)
245
+ - num_layers (int): Number of particle layers (default: 1)
246
+ - up (bool): If True, layers extend upward; if False, downward (default: True)
247
+
248
+ Returns:
249
+ - dict: Contains x, y coordinates and particle count.
250
+ """
251
+ try:
252
+ center = np.array([center_x, center_y])
253
+ x, y = geometry.get_2d_wall(dx=dx, center=center, length=length,
254
+ num_layers=num_layers, up=up)
255
+ return {
256
+ "success": True,
257
+ "num_particles": len(x),
258
+ "x": x.tolist(),
259
+ "y": y.tolist()
260
+ }
261
+ except Exception as e:
262
+ return {"success": False, "error": str(e)}
263
 
264
+
265
+ @mcp.tool(name="create_naca_airfoil", description="Create a NACA 4-digit series airfoil geometry.")
266
+ def create_naca_airfoil(dx: float = 0.01, airfoil: str = "0012",
267
+ chord: float = 1.0) -> dict:
268
+ """
269
+ Create a NACA 4-digit series airfoil geometry.
270
+
271
+ The airfoil string format: 'MPXX' where:
272
+ - M: Maximum camber (% of chord)
273
+ - P: Position of maximum camber (tenths of chord)
274
+ - XX: Maximum thickness (% of chord)
275
+
276
  Parameters:
277
+ - dx (float): Particle spacing (default: 0.01)
278
+ - airfoil (str): NACA 4-digit designation (default: "0012")
279
+ - chord (float): Chord length (default: 1.0)
280
+
281
+ Returns:
282
+ - dict: Contains x, y coordinates and particle count.
283
+ """
284
+ try:
285
+ x, y = geometry.get_4digit_naca_airfoil(dx=dx, airfoil=airfoil, c=chord)
286
+ return {
287
+ "success": True,
288
+ "num_particles": len(x),
289
+ "x": x.tolist(),
290
+ "y": y.tolist(),
291
+ "airfoil_type": f"NACA {airfoil}",
292
+ "chord": chord
293
+ }
294
+ except Exception as e:
295
+ return {"success": False, "error": str(e)}
296
+
297
+
298
+ # =====================================================
299
+ # Kernel Tools - 核函数工具
300
+ # =====================================================
301
+
302
+ @mcp.tool(name="list_available_kernels", description="List all available SPH kernel functions.")
303
+ def list_available_kernels() -> dict:
304
+ """
305
+ List all available SPH kernel functions in PySPH.
306
+
307
+ Returns:
308
+ - dict: Contains list of kernel names with descriptions.
309
+ """
310
+ try:
311
+ available_kernels = {
312
+ "CubicSpline": {
313
+ "dimensions": [1, 2, 3],
314
+ "radius_scale": 2.0,
315
+ "description": "Classical cubic spline kernel (Monaghan 1992)"
316
+ },
317
+ "WendlandQuintic": {
318
+ "dimensions": [2, 3],
319
+ "radius_scale": 2.0,
320
+ "description": "Wendland quintic C2 kernel, good stability"
321
+ },
322
+ "WendlandQuinticC2_1D": {
323
+ "dimensions": [1],
324
+ "radius_scale": 2.0,
325
+ "description": "Wendland quintic C2 kernel for 1D"
326
+ },
327
+ "WendlandQuinticC4": {
328
+ "dimensions": [2, 3],
329
+ "radius_scale": 2.0,
330
+ "description": "Wendland quintic C4 kernel"
331
+ },
332
+ "WendlandQuinticC4_1D": {
333
+ "dimensions": [1],
334
+ "radius_scale": 2.0,
335
+ "description": "Wendland quintic C4 kernel for 1D"
336
+ },
337
+ "WendlandQuinticC6": {
338
+ "dimensions": [2, 3],
339
+ "radius_scale": 2.0,
340
+ "description": "Wendland quintic C6 kernel"
341
+ },
342
+ "Gaussian": {
343
+ "dimensions": [1, 2, 3],
344
+ "radius_scale": 3.0,
345
+ "description": "Gaussian kernel with infinite support (truncated)"
346
+ },
347
+ "QuinticSpline": {
348
+ "dimensions": [1, 2, 3],
349
+ "radius_scale": 3.0,
350
+ "description": "Quintic spline kernel"
351
+ },
352
+ "SuperGaussian": {
353
+ "dimensions": [1, 2, 3],
354
+ "radius_scale": 3.0,
355
+ "description": "Super Gaussian kernel"
356
+ }
357
+ }
358
+ return {
359
+ "success": True,
360
+ "kernels": available_kernels,
361
+ "total_count": len(available_kernels)
362
+ }
363
+ except Exception as e:
364
+ return {"success": False, "error": str(e)}
365
+
366
 
367
+ @mcp.tool(name="create_kernel", description="Create an SPH kernel function instance.")
368
+ def create_kernel(kernel_type: str = "CubicSpline", dim: int = 2) -> dict:
369
+ """
370
+ Create an SPH kernel function instance.
371
+
372
+ Parameters:
373
+ - kernel_type (str): Type of kernel (default: "CubicSpline")
374
+ - dim (int): Dimension of the problem (default: 2)
375
+
376
  Returns:
377
+ - dict: Contains kernel information and properties.
378
  """
379
  try:
380
+ kernel_map = {
381
+ "CubicSpline": kernels.CubicSpline,
382
+ "WendlandQuintic": kernels.WendlandQuintic,
383
+ "Gaussian": kernels.Gaussian,
384
+ "QuinticSpline": kernels.QuinticSpline,
385
+ }
386
+
387
+ if kernel_type not in kernel_map:
388
+ return {
389
+ "success": False,
390
+ "error": f"Unknown kernel type: {kernel_type}. Available: {list(kernel_map.keys())}"
391
+ }
392
+
393
+ kernel = kernel_map[kernel_type](dim=dim)
394
+
395
+ return {
396
+ "success": True,
397
+ "kernel_type": kernel_type,
398
+ "dimension": dim,
399
+ "radius_scale": kernel.radius_scale,
400
+ "deltap": kernel.get_deltap() if hasattr(kernel, 'get_deltap') else None
401
+ }
402
  except Exception as e:
403
  return {"success": False, "error": str(e)}
404
 
405
+
406
+ @mcp.tool(name="evaluate_kernel", description="Evaluate a kernel function at a given distance.")
407
+ def evaluate_kernel(kernel_type: str = "CubicSpline", dim: int = 2,
408
+ rij: float = 0.5, h: float = 0.1) -> dict:
409
  """
410
+ Evaluate a kernel function at a given distance.
411
+
412
+ Parameters:
413
+ - kernel_type (str): Type of kernel (default: "CubicSpline")
414
+ - dim (int): Dimension of the problem (default: 2)
415
+ - rij (float): Distance between particles (default: 0.5)
416
+ - h (float): Smoothing length (default: 0.1)
417
+
418
+ Returns:
419
+ - dict: Contains kernel value and gradient magnitude.
420
+ """
421
+ try:
422
+ kernel_map = {
423
+ "CubicSpline": kernels.CubicSpline,
424
+ "WendlandQuintic": kernels.WendlandQuintic,
425
+ "Gaussian": kernels.Gaussian,
426
+ "QuinticSpline": kernels.QuinticSpline,
427
+ }
428
+
429
+ if kernel_type not in kernel_map:
430
+ return {
431
+ "success": False,
432
+ "error": f"Unknown kernel type: {kernel_type}"
433
+ }
434
+
435
+ kernel = kernel_map[kernel_type](dim=dim)
436
+
437
+ # Evaluate kernel value
438
+ w = kernel.kernel(rij=rij, h=h)
439
+
440
+ # Evaluate kernel gradient (dwdq)
441
+ dwdq = kernel.dwdq(rij=rij, h=h)
442
+
443
+ # Calculate q = rij/h
444
+ q = rij / h
445
+
446
+ return {
447
+ "success": True,
448
+ "kernel_type": kernel_type,
449
+ "dimension": dim,
450
+ "rij": rij,
451
+ "h": h,
452
+ "q": q,
453
+ "kernel_value": w,
454
+ "kernel_gradient": dwdq
455
+ }
456
+ except Exception as e:
457
+ return {"success": False, "error": str(e)}
458
+
459
+
460
+ # =====================================================
461
+ # Particle Array Tools - 粒子数组工具
462
+ # =====================================================
463
 
464
+ @mcp.tool(name="create_particle_array", description="Create a PySPH particle array with specified properties.")
465
+ def create_particle_array(name: str = "fluid", x: List[float] = None,
466
+ y: List[float] = None, z: List[float] = None,
467
+ h: float = 0.1, rho: float = 1000.0,
468
+ m: float = None) -> dict:
469
+ """
470
+ Create a PySPH particle array with specified properties.
471
+
472
  Parameters:
473
+ - name (str): Name of the particle array (default: "fluid")
474
+ - x, y, z: Lists of particle coordinates (z optional for 2D)
475
+ - h (float): Smoothing length (default: 0.1)
476
+ - rho (float): Density (default: 1000.0)
477
+ - m (float): Particle mass (if None, calculated from rho and h)
478
+
479
+ Returns:
480
+ - dict: Contains particle array information.
481
+ """
482
+ try:
483
+ if x is None:
484
+ x = [0.0]
485
+ if y is None:
486
+ y = [0.0]
487
+ if z is None:
488
+ z = [0.0] * len(x)
489
+
490
+ x = np.array(x)
491
+ y = np.array(y)
492
+ z = np.array(z)
493
+
494
+ # Calculate mass if not provided (2D approximation)
495
+ if m is None:
496
+ dx = h / 1.3 # approximate dx from h
497
+ m = rho * dx * dx # 2D mass
498
+
499
+ pa = get_particle_array(
500
+ name=name,
501
+ x=x, y=y, z=z,
502
+ h=np.ones_like(x) * h,
503
+ rho=np.ones_like(x) * rho,
504
+ m=np.ones_like(x) * m
505
+ )
506
+
507
+ return {
508
+ "success": True,
509
+ "name": name,
510
+ "num_particles": len(x),
511
+ "properties": list(pa.properties.keys()),
512
+ "h": h,
513
+ "rho": rho,
514
+ "m": m
515
+ }
516
+ except Exception as e:
517
+ return {"success": False, "error": str(e)}
518
+
519
+
520
+ @mcp.tool(name="get_default_properties", description="Get the default properties for SPH particle arrays.")
521
+ def get_default_properties() -> dict:
522
+ """
523
+ Get the default properties for SPH particle arrays.
524
+
525
+ Returns:
526
+ - dict: Contains list of default properties with descriptions.
527
+ """
528
+ try:
529
+ from pysph.base.utils import DEFAULT_PROPS
530
+
531
+ property_descriptions = {
532
+ 'x': 'X-coordinate position',
533
+ 'y': 'Y-coordinate position',
534
+ 'z': 'Z-coordinate position',
535
+ 'u': 'X-component of velocity',
536
+ 'v': 'Y-component of velocity',
537
+ 'w': 'Z-component of velocity',
538
+ 'm': 'Particle mass',
539
+ 'h': 'Smoothing length',
540
+ 'rho': 'Density',
541
+ 'p': 'Pressure',
542
+ 'au': 'X-component of acceleration',
543
+ 'av': 'Y-component of acceleration',
544
+ 'aw': 'Z-component of acceleration',
545
+ 'gid': 'Global particle ID',
546
+ 'pid': 'Processor ID (for parallel)',
547
+ 'tag': 'Particle tag (local/remote/ghost)'
548
+ }
549
+
550
+ return {
551
+ "success": True,
552
+ "default_properties": list(DEFAULT_PROPS),
553
+ "descriptions": property_descriptions
554
+ }
555
+ except Exception as e:
556
+ return {"success": False, "error": str(e)}
557
+
558
+
559
+ # =====================================================
560
+ # SPH Equations Tools - SPH方程工具
561
+ # =====================================================
562
+
563
+ @mcp.tool(name="list_basic_equations", description="List available basic SPH equations.")
564
+ def list_basic_equations() -> dict:
565
+ """
566
+ List available basic SPH equations in PySPH.
567
+
568
+ Returns:
569
+ - dict: Contains list of equation names with descriptions.
570
+ """
571
+ try:
572
+ equations = {
573
+ "SummationDensity": {
574
+ "description": "Compute density using SPH summation: ρ_a = Σ m_b W_ab",
575
+ "required_properties": ["rho", "m"]
576
+ },
577
+ "ContinuityEquation": {
578
+ "description": "Compute density rate: dρ/dt = Σ m_b v_ab · ∇W_ab",
579
+ "required_properties": ["arho", "m"]
580
+ },
581
+ "BodyForce": {
582
+ "description": "Add constant body force (e.g., gravity)",
583
+ "parameters": ["fx", "fy", "fz"],
584
+ "required_properties": ["au", "av", "aw"]
585
+ },
586
+ "IsothermalEOS": {
587
+ "description": "Isothermal equation of state: p = p0 + c0²(ρ - ρ0)",
588
+ "parameters": ["rho0", "c0", "p0"],
589
+ "required_properties": ["p", "rho"]
590
+ },
591
+ "MonaghanArtificialViscosity": {
592
+ "description": "Classical Monaghan artificial viscosity for shock handling",
593
+ "parameters": ["alpha", "beta"],
594
+ "required_properties": ["au", "av", "aw", "rho", "cs"]
595
+ },
596
+ "VelocityGradient2D": {
597
+ "description": "Compute velocity gradient tensor in 2D",
598
+ "required_properties": ["v00", "v01", "v10", "v11"]
599
+ },
600
+ "VelocityGradient3D": {
601
+ "description": "Compute velocity gradient tensor in 3D",
602
+ "required_properties": ["v00", "v01", "v02", "v10", "v11", "v12", "v20", "v21", "v22"]
603
+ }
604
+ }
605
+
606
+ return {
607
+ "success": True,
608
+ "equations": equations,
609
+ "total_count": len(equations)
610
+ }
611
+ except Exception as e:
612
+ return {"success": False, "error": str(e)}
613
+
614
+
615
+ # =====================================================
616
+ # Integrator Tools - 积分器工具
617
+ # =====================================================
618
+
619
+ @mcp.tool(name="list_integrators", description="List available integrator types in PySPH.")
620
+ def list_integrators() -> dict:
621
+ """
622
+ List available integrator types in PySPH.
623
+
624
+ Returns:
625
+ - dict: Contains list of integrator types with descriptions.
626
+ """
627
+ try:
628
+ integrators = {
629
+ "EulerIntegrator": {
630
+ "description": "Simple first-order Euler integration",
631
+ "accuracy": "First order",
632
+ "stages": 1
633
+ },
634
+ "PECIntegrator": {
635
+ "description": "Predict-Evaluate-Correct integrator",
636
+ "accuracy": "Second order",
637
+ "stages": 2
638
+ },
639
+ "EPECIntegrator": {
640
+ "description": "Evaluate-Predict-Evaluate-Correct integrator",
641
+ "accuracy": "Second order",
642
+ "stages": 3
643
+ },
644
+ "TVDRK3Integrator": {
645
+ "description": "Total Variation Diminishing Runge-Kutta 3rd order",
646
+ "accuracy": "Third order",
647
+ "stages": 3
648
+ },
649
+ "LeapFrogIntegrator": {
650
+ "description": "Leap-frog (Verlet) integration scheme",
651
+ "accuracy": "Second order",
652
+ "stages": 2
653
+ }
654
+ }
655
+
656
+ return {
657
+ "success": True,
658
+ "integrators": integrators,
659
+ "total_count": len(integrators)
660
+ }
661
+ except Exception as e:
662
+ return {"success": False, "error": str(e)}
663
 
664
+
665
+ # =====================================================
666
+ # Solver Tools - 求解器工具
667
+ # =====================================================
668
+
669
+ @mcp.tool(name="get_solver_parameters", description="Get description of common solver parameters.")
670
+ def get_solver_parameters() -> dict:
671
+ """
672
+ Get description of common solver parameters in PySPH.
673
+
674
  Returns:
675
+ - dict: Contains solver parameter descriptions and typical values.
676
  """
677
  try:
678
+ parameters = {
679
+ "dim": {
680
+ "description": "Dimension of the problem",
681
+ "type": "int",
682
+ "values": [1, 2, 3]
683
+ },
684
+ "dt": {
685
+ "description": "Initial/suggested time step",
686
+ "type": "float",
687
+ "typical_range": [1e-5, 1e-2]
688
+ },
689
+ "tf": {
690
+ "description": "Final simulation time",
691
+ "type": "float"
692
+ },
693
+ "adaptive_timestep": {
694
+ "description": "Enable adaptive time stepping",
695
+ "type": "bool",
696
+ "default": False
697
+ },
698
+ "cfl": {
699
+ "description": "CFL number for adaptive time stepping",
700
+ "type": "float",
701
+ "typical_range": [0.1, 0.5],
702
+ "default": 0.3
703
+ },
704
+ "n_damp": {
705
+ "description": "Number of initial damping timesteps",
706
+ "type": "int",
707
+ "default": 0
708
+ },
709
+ "pfreq": {
710
+ "description": "Output printing frequency (iterations)",
711
+ "type": "int",
712
+ "default": 100
713
+ },
714
+ "fixed_h": {
715
+ "description": "Use constant smoothing length",
716
+ "type": "bool",
717
+ "default": False
718
+ }
719
+ }
720
+
721
+ return {
722
+ "success": True,
723
+ "parameters": parameters
724
+ }
725
  except Exception as e:
726
  return {"success": False, "error": str(e)}
727
 
728
+
729
+ # =====================================================
730
+ # Mathematical Utilities - 数学工具
731
+ # =====================================================
732
+
733
+ @mcp.tool(name="calculate_distance", description="Calculate Euclidean distance between two points.")
734
+ def calculate_distance(point1: List[float], point2: List[float] = None) -> dict:
735
  """
736
+ Calculate Euclidean distance between two points.
737
+
738
+ Parameters:
739
+ - point1: First point coordinates [x, y] or [x, y, z]
740
+ - point2: Second point coordinates (default: origin)
741
+
742
+ Returns:
743
+ - dict: Contains the calculated distance.
744
+ """
745
+ try:
746
+ p1 = np.array(point1)
747
+ if point2 is None:
748
+ p2 = np.zeros_like(p1)
749
+ else:
750
+ p2 = np.array(point2)
751
+
752
+ dist = geometry.distance(p1, p2) if len(p1) == 3 else geometry.distance_2d(p1[:2], p2[:2])
753
+
754
+ return {
755
+ "success": True,
756
+ "point1": point1,
757
+ "point2": point2 if point2 else [0.0] * len(point1),
758
+ "distance": float(dist)
759
+ }
760
+ except Exception as e:
761
+ return {"success": False, "error": str(e)}
762
+
763
 
764
+ @mcp.tool(name="calculate_triangle_area", description="Calculate area of a triangle given three 3D points.")
765
+ def calculate_triangle_area(point1: List[float], point2: List[float],
766
+ point3: List[float]) -> dict:
767
+ """
768
+ Calculate area of a triangle given three 3D points.
769
+
770
  Parameters:
771
+ - point1, point2, point3: Three vertices of the triangle [x, y, z]
772
+
773
+ Returns:
774
+ - dict: Contains the calculated area.
775
+ """
776
+ try:
777
+ points = np.array([point1, point2, point3])
778
+ area = geometry.evaluate_area_of_triangle(points)
779
+
780
+ return {
781
+ "success": True,
782
+ "vertices": [point1, point2, point3],
783
+ "area": float(area)
784
+ }
785
+ except Exception as e:
786
+ return {"success": False, "error": str(e)}
787
+
788
+
789
+ # =====================================================
790
+ # Information Tools - 信息工具
791
+ # =====================================================
792
 
793
+ @mcp.tool(name="get_pysph_info", description="Get PySPH library information and capabilities.")
794
+ def get_pysph_info() -> dict:
795
+ """
796
+ Get PySPH library information and capabilities.
797
+
798
+ Returns:
799
+ - dict: Contains library information and main features.
800
+ """
801
+ try:
802
+ return {
803
+ "success": True,
804
+ "name": "PySPH",
805
+ "description": "A framework for Smoothed Particle Hydrodynamics in Python",
806
+ "main_modules": {
807
+ "pysph.base": "Core data structures (ParticleArray, NNPS, Kernels)",
808
+ "pysph.sph": "SPH equations, schemes, and integrators",
809
+ "pysph.solver": "Solver and application framework",
810
+ "pysph.tools": "Geometry, interpolation, and post-processing tools",
811
+ "pysph.parallel": "Parallel computing support with MPI"
812
+ },
813
+ "features": [
814
+ "Multiple SPH formulations (WCSPH, ISPH, IISPH, etc.)",
815
+ "Flexible equation specification",
816
+ "Automatic code generation for performance",
817
+ "GPU acceleration support (OpenCL)",
818
+ "MPI-based parallel computing",
819
+ "Various boundary condition implementations"
820
+ ],
821
+ "supported_applications": [
822
+ "Free-surface flows",
823
+ "Dam break simulations",
824
+ "Wave dynamics",
825
+ "Solid mechanics",
826
+ "Fluid-structure interaction",
827
+ "Gas dynamics"
828
+ ]
829
+ }
830
+ except Exception as e:
831
+ return {"success": False, "error": str(e)}
832
+
833
+
834
+ @mcp.tool(name="get_nnps_info", description="Get information about Nearest Neighbor Particle Search (NNPS) methods.")
835
+ def get_nnps_info() -> dict:
836
+ """
837
+ Get information about available NNPS (Nearest Neighbor Particle Search) methods.
838
+
839
  Returns:
840
+ - dict: Contains NNPS algorithm descriptions.
841
  """
842
  try:
843
+ nnps_methods = {
844
+ "LinkedListNNPS": {
845
+ "description": "Cell linked list method for neighbor search",
846
+ "complexity": "O(N)",
847
+ "best_for": "Uniform particle distributions"
848
+ },
849
+ "BoxSortNNPS": {
850
+ "description": "Box sorting based neighbor search",
851
+ "complexity": "O(N log N)",
852
+ "best_for": "General purpose"
853
+ },
854
+ "SpatialHashNNPS": {
855
+ "description": "Spatial hashing based neighbor search",
856
+ "complexity": "O(N)",
857
+ "best_for": "Sparse distributions"
858
+ },
859
+ "OctreeNNPS": {
860
+ "description": "Octree-based neighbor search",
861
+ "complexity": "O(N log N)",
862
+ "best_for": "Non-uniform distributions"
863
+ },
864
+ "StratifiedHashNNPS": {
865
+ "description": "Stratified hashing for variable-h particles",
866
+ "complexity": "O(N)",
867
+ "best_for": "Variable smoothing length"
868
+ }
869
+ }
870
+
871
+ return {
872
+ "success": True,
873
+ "nnps_methods": nnps_methods,
874
+ "total_count": len(nnps_methods)
875
+ }
876
  except Exception as e:
877
  return {"success": False, "error": str(e)}
878
 
879
+
880
  def create_app() -> FastMCP:
881
  """
882
  Create and return the FastMCP instance for the service.