guohanghui commited on
Commit
36ea9e6
·
verified ·
1 Parent(s): ba04674

Update PySDM/mcp_output/mcp_plugin/mcp_service.py

Browse files
PySDM/mcp_output/mcp_plugin/mcp_service.py CHANGED
@@ -1,5 +1,7 @@
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,68 +9,448 @@ 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 source directory
12
- from PySDM import particulator
13
- from PySDM.dynamics import condensation
14
- from PySDM.physics import constants
15
 
16
  # Create the FastMCP service application
17
  mcp = FastMCP("pysdm_service")
18
 
19
- @mcp.tool(name="simulate_particles", description="Simulate particle dynamics using PySDM")
20
- def simulate_particles(particle_count: int, time_step: float) -> dict:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  """
22
- Simulate particle dynamics using PySDM.
23
 
24
  Parameters:
25
- - particle_count (int): Number of particles to simulate.
26
- - time_step (float): Time step for the simulation.
27
 
28
  Returns:
29
- - dict: A dictionary containing success, result, and error fields.
30
  """
31
  try:
32
- # Example simulation logic
33
- result = particulator.simulate(particle_count, time_step)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  return {"success": True, "result": result, "error": None}
35
  except Exception as e:
36
  return {"success": False, "result": None, "error": str(e)}
37
 
 
 
 
38
  @mcp.tool(name="calculate_condensation", description="Calculate condensation rates")
39
- def calculate_condensation(temperature: float, pressure: float) -> dict:
40
  """
41
- Calculate condensation rates.
42
 
43
  Parameters:
44
  - temperature (float): Temperature in Kelvin.
45
  - pressure (float): Pressure in Pascals.
 
46
 
47
  Returns:
48
- - dict: A dictionary containing success, result, and error fields.
49
  """
50
  try:
51
- # Example condensation calculation
52
- rate = condensation.calculate_rate(temperature, pressure)
53
- return {"success": True, "result": rate, "error": None}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  except Exception as e:
55
  return {"success": False, "result": None, "error": str(e)}
56
 
57
- @mcp.tool(name="get_physical_constants", description="Retrieve physical constants")
58
- def get_physical_constants() -> dict:
 
 
 
59
  """
60
- Retrieve physical constants.
 
 
 
 
61
 
62
  Returns:
63
- - dict: A dictionary containing success, result, and error fields.
64
  """
65
  try:
66
- # Example retrieval of constants
67
- constants_data = constants.get_constants()
68
- return {"success": True, "result": constants_data, "error": None}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  except Exception as e:
70
  return {"success": False, "result": None, "error": str(e)}
71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  def create_app() -> FastMCP:
73
  """
74
  Create and return the FastMCP application instance.
 
1
  import os
2
  import sys
3
+ from typing import List, Optional, Dict, Any
4
+ import math
5
 
6
  # Add the local source directory to sys.path
7
  source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source")
 
9
  sys.path.insert(0, source_path)
10
 
11
  from fastmcp import FastMCP
12
+ import numpy as np
13
+ from scipy import constants as sci
 
 
 
14
 
15
  # Create the FastMCP service application
16
  mcp = FastMCP("pysdm_service")
17
 
18
+
19
+ # ===================== Physical Constants =====================
20
+
21
+ # Define key physical constants (based on PySDM's constants_defaults.py)
22
+ PHYSICAL_CONSTANTS = {
23
+ "R_str": sci.R, # Universal gas constant (J/K/mol)
24
+ "N_A": sci.N_A, # Avogadro constant (1/mol)
25
+ "T0": sci.zero_Celsius, # 0°C in Kelvin (273.15 K)
26
+ "PI": sci.pi,
27
+ "Md": 28.966e-3, # Dry air molar mass (kg/mol)
28
+ "Mv": 18.015e-3, # Water vapour molar mass (kg/mol)
29
+ "eps": 18.015 / 28.966, # Mv/Md ratio
30
+ "g_std": sci.g, # Standard gravity (m/s²)
31
+ "c_pd": 1005.0, # Specific heat of dry air at constant pressure (J/kg/K)
32
+ "c_pv": 1850.0, # Specific heat of water vapour at constant pressure (J/kg/K)
33
+ "rho_w": 1000.0, # Density of liquid water (kg/m³)
34
+ "l_tri": 2.5e6, # Latent heat of vaporization at triple point (J/kg)
35
+ "MAC": 1.0, # Mass accommodation coefficient
36
+ "HAC": 1.0, # Thermal accommodation coefficient
37
+ }
38
+
39
+ # Flatau-Walko-Cotton saturation vapour pressure coefficients
40
+ FWC_COEFFS = {
41
+ "C0": 6.115836990e2, # Pa
42
+ "C1": 0.444606896e2, # Pa/K
43
+ "C2": 0.143177157e1, # Pa/K²
44
+ "C3": 0.264224321e-1, # Pa/K³
45
+ "C4": 0.299291081e-3, # Pa/K⁴
46
+ "C5": 0.203154182e-5, # Pa/K⁵
47
+ "C6": 0.702620698e-8, # Pa/K⁶
48
+ "C7": 0.379534310e-11, # Pa/K⁷
49
+ "C8": -0.321582393e-13, # Pa/K⁸
50
+ }
51
+
52
+ # Isotope constants (VSMOW standard)
53
+ ISOTOPE_CONSTANTS = {
54
+ "VSMOW_R_2H": 155.76e-6, # Heavy-to-light isotope abundance ratio for deuterium
55
+ "VSMOW_R_3H": 1.85e-17, # For tritium
56
+ "VSMOW_R_18O": 2005.20e-6, # For oxygen-18
57
+ "VSMOW_R_17O": 379.9e-6, # For oxygen-17
58
+ "M_1H": 1.00782503224e-3, # Hydrogen atomic weight (kg/mol)
59
+ "M_2H": 2.01410177812e-3, # Deuterium atomic weight (kg/mol)
60
+ "M_16O": 15.99491461957e-3, # Oxygen-16 atomic weight (kg/mol)
61
+ "M_18O": 17.99915961287e-3, # Oxygen-18 atomic weight (kg/mol)
62
+ }
63
+
64
+
65
+ @mcp.tool(name="get_physical_constants", description="Retrieve physical constants")
66
+ def get_physical_constants() -> dict:
67
+ """
68
+ Retrieve physical constants used in PySDM simulations.
69
+
70
+ Returns:
71
+ - dict: Dictionary containing physical constants with their values and units.
72
+ """
73
+ try:
74
+ result = {
75
+ "fundamental_constants": {
76
+ "R_str": {"value": PHYSICAL_CONSTANTS["R_str"], "unit": "J/(K·mol)", "description": "Universal gas constant"},
77
+ "N_A": {"value": PHYSICAL_CONSTANTS["N_A"], "unit": "1/mol", "description": "Avogadro constant"},
78
+ "g_std": {"value": PHYSICAL_CONSTANTS["g_std"], "unit": "m/s²", "description": "Standard gravity"},
79
+ "PI": {"value": PHYSICAL_CONSTANTS["PI"], "unit": "dimensionless", "description": "Pi"},
80
+ },
81
+ "thermodynamic_constants": {
82
+ "T0": {"value": PHYSICAL_CONSTANTS["T0"], "unit": "K", "description": "Zero Celsius in Kelvin"},
83
+ "c_pd": {"value": PHYSICAL_CONSTANTS["c_pd"], "unit": "J/(kg·K)", "description": "Specific heat of dry air"},
84
+ "c_pv": {"value": PHYSICAL_CONSTANTS["c_pv"], "unit": "J/(kg·K)", "description": "Specific heat of water vapour"},
85
+ "l_tri": {"value": PHYSICAL_CONSTANTS["l_tri"], "unit": "J/kg", "description": "Latent heat of vaporization"},
86
+ },
87
+ "molecular_constants": {
88
+ "Md": {"value": PHYSICAL_CONSTANTS["Md"], "unit": "kg/mol", "description": "Dry air molar mass"},
89
+ "Mv": {"value": PHYSICAL_CONSTANTS["Mv"], "unit": "kg/mol", "description": "Water vapour molar mass"},
90
+ "eps": {"value": PHYSICAL_CONSTANTS["eps"], "unit": "dimensionless", "description": "Mv/Md ratio"},
91
+ "rho_w": {"value": PHYSICAL_CONSTANTS["rho_w"], "unit": "kg/m³", "description": "Density of liquid water"},
92
+ },
93
+ "accommodation_coefficients": {
94
+ "MAC": {"value": PHYSICAL_CONSTANTS["MAC"], "unit": "dimensionless", "description": "Mass accommodation coefficient"},
95
+ "HAC": {"value": PHYSICAL_CONSTANTS["HAC"], "unit": "dimensionless", "description": "Thermal accommodation coefficient"},
96
+ }
97
+ }
98
+ return {"success": True, "result": result, "error": None}
99
+ except Exception as e:
100
+ return {"success": False, "result": None, "error": str(e)}
101
+
102
+
103
+ # ===================== Saturation Vapour Pressure =====================
104
+
105
+ def _pvs_flatau_walko_cotton(T_celsius: float) -> float:
106
+ """Calculate saturation vapour pressure using Flatau-Walko-Cotton polynomial."""
107
+ C = FWC_COEFFS
108
+ return (C["C0"] + T_celsius * (C["C1"] + T_celsius * (C["C2"] + T_celsius * (
109
+ C["C3"] + T_celsius * (C["C4"] + T_celsius * (C["C5"] + T_celsius * (
110
+ C["C6"] + T_celsius * (C["C7"] + T_celsius * C["C8"]))))))))
111
+
112
+
113
+ def _pvs_august_roche_magnus(T_celsius: float) -> float:
114
+ """Calculate saturation vapour pressure using August-Roche-Magnus formula."""
115
+ # Coefficients from Alduchov & Eskridge 1996
116
+ C1 = 610.94 # Pa
117
+ C2 = 17.625
118
+ C3 = 243.04 # °C
119
+ return C1 * math.exp(C2 * T_celsius / (T_celsius + C3))
120
+
121
+
122
+ @mcp.tool(name="calculate_saturation_vapour_pressure", description="Calculate saturation vapour pressure over water")
123
+ def calculate_saturation_vapour_pressure(temperature_kelvin: float, method: str = "flatau_walko_cotton") -> dict:
124
  """
125
+ Calculate saturation vapour pressure over liquid water.
126
 
127
  Parameters:
128
+ - temperature_kelvin (float): Temperature in Kelvin.
129
+ - method (str): Method to use - 'flatau_walko_cotton' or 'august_roche_magnus'.
130
 
131
  Returns:
132
+ - dict: Saturation vapour pressure in Pascals and hectopascals.
133
  """
134
  try:
135
+ T_celsius = temperature_kelvin - PHYSICAL_CONSTANTS["T0"]
136
+
137
+ if method == "flatau_walko_cotton":
138
+ pvs = _pvs_flatau_walko_cotton(T_celsius)
139
+ elif method == "august_roche_magnus":
140
+ pvs = _pvs_august_roche_magnus(T_celsius)
141
+ else:
142
+ return {"success": False, "result": None, "error": f"Unknown method: {method}. Use 'flatau_walko_cotton' or 'august_roche_magnus'."}
143
+
144
+ result = {
145
+ "temperature_K": temperature_kelvin,
146
+ "temperature_C": T_celsius,
147
+ "saturation_vapour_pressure_Pa": pvs,
148
+ "saturation_vapour_pressure_hPa": pvs / 100,
149
+ "method": method
150
+ }
151
  return {"success": True, "result": result, "error": None}
152
  except Exception as e:
153
  return {"success": False, "result": None, "error": str(e)}
154
 
155
+
156
+ # ===================== Condensation Calculations =====================
157
+
158
  @mcp.tool(name="calculate_condensation", description="Calculate condensation rates")
159
+ def calculate_condensation(temperature: float, pressure: float, relative_humidity: float = 1.0) -> dict:
160
  """
161
+ Calculate condensation-related parameters.
162
 
163
  Parameters:
164
  - temperature (float): Temperature in Kelvin.
165
  - pressure (float): Pressure in Pascals.
166
+ - relative_humidity (float): Relative humidity (0-1 or as fraction >1 for supersaturation).
167
 
168
  Returns:
169
+ - dict: Condensation parameters including supersaturation and vapour pressure.
170
  """
171
  try:
172
+ T_celsius = temperature - PHYSICAL_CONSTANTS["T0"]
173
+ pvs = _pvs_flatau_walko_cotton(T_celsius)
174
+
175
+ # Actual vapour pressure
176
+ pv = relative_humidity * pvs
177
+
178
+ # Supersaturation
179
+ supersaturation = relative_humidity - 1.0
180
+
181
+ # Water vapour mixing ratio
182
+ eps = PHYSICAL_CONSTANTS["eps"]
183
+ mixing_ratio = eps * pv / (pressure - pv)
184
+
185
+ # Specific humidity
186
+ specific_humidity = mixing_ratio / (1 + mixing_ratio)
187
+
188
+ result = {
189
+ "temperature_K": temperature,
190
+ "pressure_Pa": pressure,
191
+ "saturation_vapour_pressure_Pa": pvs,
192
+ "actual_vapour_pressure_Pa": pv,
193
+ "relative_humidity": relative_humidity,
194
+ "supersaturation": supersaturation,
195
+ "supersaturation_percent": supersaturation * 100,
196
+ "water_vapour_mixing_ratio": mixing_ratio,
197
+ "specific_humidity": specific_humidity
198
+ }
199
+ return {"success": True, "result": result, "error": None}
200
  except Exception as e:
201
  return {"success": False, "result": None, "error": str(e)}
202
 
203
+
204
+ # ===================== Particle Dynamics =====================
205
+
206
+ @mcp.tool(name="simulate_particles", description="Simulate particle dynamics using PySDM")
207
+ def simulate_particles(particle_count: int, time_step: float) -> dict:
208
  """
209
+ Get information about particle simulation parameters in PySDM.
210
+
211
+ Parameters:
212
+ - particle_count (int): Number of super-droplets to simulate.
213
+ - time_step (float): Time step for the simulation in seconds.
214
 
215
  Returns:
216
+ - dict: Simulation configuration and recommendations.
217
  """
218
  try:
219
+ # PySDM condensation solver defaults
220
+ defaults = {
221
+ "rtol_x": 1e-6, # Relative tolerance for particle size
222
+ "rtol_thd": 1e-6, # Relative tolerance for thermodynamic variables
223
+ "dt_cond_range": (1e-4, 1.0), # Condensation timestep range (s)
224
+ "max_iters": 16, # Maximum iterations for solver
225
+ }
226
+
227
+ result = {
228
+ "configuration": {
229
+ "n_sd": particle_count,
230
+ "dt": time_step,
231
+ "dt_unit": "seconds"
232
+ },
233
+ "solver_defaults": defaults,
234
+ "available_dynamics": [
235
+ "Condensation",
236
+ "Collision/Coalescence",
237
+ "Displacement",
238
+ "Freezing",
239
+ "AqueousChemistry",
240
+ "IsotopicFractionation",
241
+ "VapourDepositionOnIce"
242
+ ],
243
+ "recommendations": {
244
+ "adaptive_timestep": "Recommended for condensation",
245
+ "suggested_n_sd": "100-10000 for typical cloud simulations"
246
+ }
247
+ }
248
+ return {"success": True, "result": result, "error": None}
249
  except Exception as e:
250
  return {"success": False, "result": None, "error": str(e)}
251
 
252
+
253
+ # ===================== Trivia/Utility Functions =====================
254
+
255
+ @mcp.tool(name="calculate_droplet_volume", description="Calculate droplet volume from radius")
256
+ def calculate_droplet_volume(radius_um: float) -> dict:
257
+ """
258
+ Calculate droplet volume and related properties.
259
+
260
+ Parameters:
261
+ - radius_um (float): Droplet radius in micrometers.
262
+
263
+ Returns:
264
+ - dict: Volume, surface area, and mass of the droplet.
265
+ """
266
+ try:
267
+ radius_m = radius_um * 1e-6
268
+ PI = PHYSICAL_CONSTANTS["PI"]
269
+ rho_w = PHYSICAL_CONSTANTS["rho_w"]
270
+
271
+ volume = (4/3) * PI * radius_m**3
272
+ surface_area = 4 * PI * radius_m**2
273
+ mass = rho_w * volume
274
+
275
+ result = {
276
+ "radius_um": radius_um,
277
+ "radius_m": radius_m,
278
+ "volume_m3": volume,
279
+ "volume_um3": volume * 1e18,
280
+ "surface_area_m2": surface_area,
281
+ "surface_area_um2": surface_area * 1e12,
282
+ "mass_kg": mass,
283
+ "mass_ng": mass * 1e12
284
+ }
285
+ return {"success": True, "result": result, "error": None}
286
+ except Exception as e:
287
+ return {"success": False, "result": None, "error": str(e)}
288
+
289
+
290
+ @mcp.tool(name="calculate_radius_from_volume", description="Calculate droplet radius from volume")
291
+ def calculate_radius_from_volume(volume_um3: float) -> dict:
292
+ """
293
+ Calculate droplet radius from volume.
294
+
295
+ Parameters:
296
+ - volume_um3 (float): Droplet volume in cubic micrometers.
297
+
298
+ Returns:
299
+ - dict: Radius in various units.
300
+ """
301
+ try:
302
+ volume_m3 = volume_um3 * 1e-18
303
+ PI = PHYSICAL_CONSTANTS["PI"]
304
+
305
+ radius_m = (volume_m3 * 3 / (4 * PI)) ** (1/3)
306
+ radius_um = radius_m * 1e6
307
+
308
+ result = {
309
+ "volume_um3": volume_um3,
310
+ "volume_m3": volume_m3,
311
+ "radius_m": radius_m,
312
+ "radius_um": radius_um,
313
+ "diameter_um": 2 * radius_um
314
+ }
315
+ return {"success": True, "result": result, "error": None}
316
+ except Exception as e:
317
+ return {"success": False, "result": None, "error": str(e)}
318
+
319
+
320
+ # ===================== Kappa-Köhler Hygroscopicity =====================
321
+
322
+ @mcp.tool(name="calculate_kappa_koehler", description="Calculate critical supersaturation using kappa-Köhler theory")
323
+ def calculate_kappa_koehler(dry_radius_um: float, kappa: float, temperature_kelvin: float = 293.15) -> dict:
324
+ """
325
+ Calculate critical supersaturation and radius using kappa-Köhler theory.
326
+
327
+ Parameters:
328
+ - dry_radius_um (float): Dry aerosol radius in micrometers.
329
+ - kappa (float): Hygroscopicity parameter (kappa).
330
+ - temperature_kelvin (float): Temperature in Kelvin (default 293.15 K = 20°C).
331
+
332
+ Returns:
333
+ - dict: Critical supersaturation and activation radius.
334
+ """
335
+ try:
336
+ # Physical constants
337
+ sigma = 0.072 # Surface tension of water (N/m)
338
+ Mv = PHYSICAL_CONSTANTS["Mv"]
339
+ rho_w = PHYSICAL_CONSTANTS["rho_w"]
340
+ R = PHYSICAL_CONSTANTS["R_str"]
341
+ T = temperature_kelvin
342
+
343
+ dry_radius_m = dry_radius_um * 1e-6
344
+
345
+ # Kelvin parameter A
346
+ A = 2 * sigma * Mv / (rho_w * R * T)
347
+
348
+ # Critical supersaturation (approximation from leading terms)
349
+ # S_c ≈ (4 A³ / 27 κ D_dry³)^0.5
350
+ S_c = math.sqrt(4 * A**3 / (27 * kappa * dry_radius_m**3))
351
+
352
+ # Critical radius
353
+ r_c = math.sqrt(3 * kappa * dry_radius_m**3 / A)
354
+
355
+ result = {
356
+ "dry_radius_um": dry_radius_um,
357
+ "kappa": kappa,
358
+ "temperature_K": temperature_kelvin,
359
+ "kelvin_parameter_A": A,
360
+ "critical_supersaturation": S_c,
361
+ "critical_supersaturation_percent": S_c * 100,
362
+ "critical_radius_um": r_c * 1e6,
363
+ "activation_diameter_um": 2 * r_c * 1e6
364
+ }
365
+ return {"success": True, "result": result, "error": None}
366
+ except Exception as e:
367
+ return {"success": False, "result": None, "error": str(e)}
368
+
369
+
370
+ # ===================== Isotope Tools =====================
371
+
372
+ @mcp.tool(name="get_isotope_constants", description="Get water isotope constants")
373
+ def get_isotope_constants() -> dict:
374
+ """
375
+ Get water isotope constants (VSMOW standard).
376
+
377
+ Returns:
378
+ - dict: Isotope abundance ratios and atomic masses.
379
+ """
380
+ try:
381
+ result = {
382
+ "VSMOW_ratios": {
383
+ "R_2H": {"value": ISOTOPE_CONSTANTS["VSMOW_R_2H"], "description": "Deuterium abundance ratio"},
384
+ "R_3H": {"value": ISOTOPE_CONSTANTS["VSMOW_R_3H"], "description": "Tritium abundance ratio"},
385
+ "R_18O": {"value": ISOTOPE_CONSTANTS["VSMOW_R_18O"], "description": "Oxygen-18 abundance ratio"},
386
+ "R_17O": {"value": ISOTOPE_CONSTANTS["VSMOW_R_17O"], "description": "Oxygen-17 abundance ratio"},
387
+ },
388
+ "atomic_masses_kg_per_mol": {
389
+ "M_1H": ISOTOPE_CONSTANTS["M_1H"],
390
+ "M_2H": ISOTOPE_CONSTANTS["M_2H"],
391
+ "M_16O": ISOTOPE_CONSTANTS["M_16O"],
392
+ "M_18O": ISOTOPE_CONSTANTS["M_18O"],
393
+ },
394
+ "description": "VSMOW (Vienna Standard Mean Ocean Water) is the international standard for water isotope ratios"
395
+ }
396
+ return {"success": True, "result": result, "error": None}
397
+ except Exception as e:
398
+ return {"success": False, "result": None, "error": str(e)}
399
+
400
+
401
+ # ===================== Available Formulae =====================
402
+
403
+ @mcp.tool(name="list_available_formulae", description="List available physics formulae in PySDM")
404
+ def list_available_formulae() -> dict:
405
+ """
406
+ List available physics formulae options in PySDM.
407
+
408
+ Returns:
409
+ - dict: Categories of formulae with available options.
410
+ """
411
+ try:
412
+ result = {
413
+ "saturation_vapour_pressure": [
414
+ "FlatauWalkoCotton",
415
+ "AugustRocheMagnus",
416
+ "Lowe1977",
417
+ "MurphyKoop2005",
418
+ "Wexler1976",
419
+ "Bolton1980"
420
+ ],
421
+ "hygroscopicity": [
422
+ "KappaKoehler",
423
+ "KappaKoehlerLeadingTerms"
424
+ ],
425
+ "latent_heat_vapourisation": [
426
+ "Kirchhoff",
427
+ "Constant"
428
+ ],
429
+ "drop_growth": [
430
+ "Mason1971",
431
+ "FuchsSutugin"
432
+ ],
433
+ "surface_tension": [
434
+ "Constant",
435
+ "CompressedFilm"
436
+ ],
437
+ "terminal_velocity": [
438
+ "GunnKinzer1949",
439
+ "PowerSeries",
440
+ "RogersYau"
441
+ ],
442
+ "freezing_temperature_spectrum": [
443
+ "Null",
444
+ "Bigg1953",
445
+ "Niemand_et_al_2012"
446
+ ],
447
+ "description": "These are configurable physics options in PySDM.Formulae"
448
+ }
449
+ return {"success": True, "result": result, "error": None}
450
+ except Exception as e:
451
+ return {"success": False, "result": None, "error": str(e)}
452
+
453
+
454
  def create_app() -> FastMCP:
455
  """
456
  Create and return the FastMCP application instance.