rowan-mcp 2.0.0__tar.gz → 2.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of rowan-mcp might be problematic. Click here for more details.

Files changed (74) hide show
  1. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/.gitignore +2 -0
  2. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/PKG-INFO +3 -18
  3. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/README.md +2 -17
  4. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/pyproject.toml +11 -1
  5. rowan_mcp-2.0.1/rowan_mcp/functions/admet.py +89 -0
  6. rowan_mcp-2.0.1/rowan_mcp/functions/bde.py +106 -0
  7. rowan_mcp-2.0.1/rowan_mcp/functions/calculation_retrieve.py +89 -0
  8. rowan_mcp-2.0.1/rowan_mcp/functions/conformers.py +77 -0
  9. rowan_mcp-2.0.1/rowan_mcp/functions/descriptors.py +89 -0
  10. rowan_mcp-2.0.1/rowan_mcp/functions/docking.py +290 -0
  11. rowan_mcp-2.0.1/rowan_mcp/functions/docking_enhanced.py +174 -0
  12. rowan_mcp-2.0.1/rowan_mcp/functions/electronic_properties.py +202 -0
  13. rowan_mcp-2.0.1/rowan_mcp/functions/folder_management.py +130 -0
  14. rowan_mcp-2.0.1/rowan_mcp/functions/fukui.py +216 -0
  15. rowan_mcp-2.0.1/rowan_mcp/functions/hydrogen_bond_basicity.py +87 -0
  16. rowan_mcp-2.0.1/rowan_mcp/functions/irc.py +125 -0
  17. rowan_mcp-2.0.1/rowan_mcp/functions/macropka.py +120 -0
  18. rowan_mcp-2.0.1/rowan_mcp/functions/molecular_converter.py +423 -0
  19. rowan_mcp-2.0.1/rowan_mcp/functions/molecular_dynamics.py +191 -0
  20. rowan_mcp-2.0.1/rowan_mcp/functions/molecule_lookup.py +57 -0
  21. rowan_mcp-2.0.1/rowan_mcp/functions/multistage_opt.py +168 -0
  22. rowan_mcp-2.0.1/rowan_mcp/functions/pdb_handler.py +200 -0
  23. rowan_mcp-2.0.1/rowan_mcp/functions/pka.py +81 -0
  24. rowan_mcp-2.0.1/rowan_mcp/functions/redox_potential.py +349 -0
  25. rowan_mcp-2.0.1/rowan_mcp/functions/scan.py +536 -0
  26. rowan_mcp-2.0.1/rowan_mcp/functions/scan_analyzer.py +347 -0
  27. rowan_mcp-2.0.1/rowan_mcp/functions/solubility.py +277 -0
  28. rowan_mcp-2.0.1/rowan_mcp/functions/spin_states.py +747 -0
  29. rowan_mcp-2.0.1/rowan_mcp/functions/system_management.py +361 -0
  30. rowan_mcp-2.0.1/rowan_mcp/functions/tautomers.py +88 -0
  31. rowan_mcp-2.0.1/rowan_mcp/functions/workflow_management.py +422 -0
  32. rowan_mcp-2.0.1/uv.lock +1655 -0
  33. rowan_mcp-2.0.0/ROWAN_MCP_TEST_QUERIES.md +0 -123
  34. rowan_mcp-2.0.0/ROWAN_MCP_TOOLS.md +0 -440
  35. rowan_mcp-2.0.0/rowan-dxt.dxt +0 -0
  36. rowan_mcp-2.0.0/uv.lock +0 -1890
  37. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/__init__.py +0 -0
  38. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/__main__.py +0 -0
  39. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/functions_v2/BENCHMARK.md +0 -0
  40. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/functions_v2/molecule_lookup.py +0 -0
  41. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/functions_v2/protein_management.py +0 -0
  42. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/functions_v2/submit_basic_calculation_workflow.py +0 -0
  43. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/functions_v2/submit_conformer_search_workflow.py +0 -0
  44. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/functions_v2/submit_descriptors_workflow.py +0 -0
  45. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/functions_v2/submit_docking_workflow.py +0 -0
  46. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/functions_v2/submit_fukui_workflow.py +0 -0
  47. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/functions_v2/submit_irc_workflow.py +0 -0
  48. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/functions_v2/submit_macropka_workflow.py +0 -0
  49. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/functions_v2/submit_pka_workflow.py +0 -0
  50. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/functions_v2/submit_protein_cofolding_workflow.py +0 -0
  51. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/functions_v2/submit_redox_potential_workflow.py +0 -0
  52. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/functions_v2/submit_scan_workflow.py +0 -0
  53. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/functions_v2/submit_solubility_workflow.py +0 -0
  54. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/functions_v2/submit_tautomer_search_workflow.py +0 -0
  55. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/functions_v2/workflow_management_v2.py +0 -0
  56. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/server.py +0 -0
  57. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/tests/basic_calculation_from_json.py +0 -0
  58. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/tests/basic_calculation_with_constraint.py +0 -0
  59. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/tests/basic_calculation_with_solvent.py +0 -0
  60. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/tests/bde.py +0 -0
  61. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/tests/benchmark_queries.md +0 -0
  62. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/tests/cofolding_screen.py +0 -0
  63. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/tests/conformer_dependent_redox.py +0 -0
  64. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/tests/conformers.py +0 -0
  65. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/tests/data.json +0 -0
  66. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/tests/docking_screen.py +0 -0
  67. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/tests/irc.py +0 -0
  68. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/tests/macropka.py +0 -0
  69. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/tests/multistage_opt.py +0 -0
  70. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/tests/optimization.py +0 -0
  71. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/tests/phenol_pka.py +0 -0
  72. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/tests/pka.py +0 -0
  73. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/tests/protein_cofolding.py +0 -0
  74. {rowan_mcp-2.0.0 → rowan_mcp-2.0.1}/rowan_mcp/tests/scan.py +0 -0
@@ -17,3 +17,5 @@ CLAUDE.md
17
17
  # Personal compatibility notes
18
18
  GEMINI_MCP_COMPATIBILITY.md
19
19
  MCP_CHATGPT_GEMINI_COMPATIBILITY_ANALYSIS.md
20
+ ROWAN_MCP_TEST_QUERIES.md
21
+ ROWAN_MCP_TOOLS.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rowan-mcp
3
- Version: 2.0.0
3
+ Version: 2.0.1
4
4
  Summary: Model Context Protocol server for Rowan computational chemistry platform
5
5
  Project-URL: Homepage, https://github.com/k-yenko/rowan-mcp
6
6
  Author-email: Katherine Yenko <katherineayenko@gmail.com>
@@ -130,7 +130,7 @@ Ask the LLM to:
130
130
 
131
131
  ## **System Requirements**
132
132
 
133
- - **Python 3.10+** (Python 3.11+ recommended)
133
+ - **Python 3.11+**
134
134
  - **Package manager**: [uv](https://docs.astral.sh/uv/) (recommended) or pip
135
135
  - **Rowan API key** (free at [labs.rowansci.com](https://labs.rowansci.com))
136
136
  - **MCP-compatible client** (Claude Desktop, etc.)
@@ -173,7 +173,7 @@ uv run python -m rowan_mcp
173
173
 
174
174
  ## **Requirements**
175
175
 
176
- - Python 3.10+
176
+ - Python 3.11+
177
177
  - Rowan API key
178
178
  - MCP-compatible AI assistant (Claude Desktop, etc.)
179
179
 
@@ -184,21 +184,6 @@ uv run python -m rowan_mcp
184
184
 
185
185
  ---
186
186
 
187
- ## **Todo**
188
-
189
- - [X] Remove unnecessary AI spaghetti formatting
190
- - [X] Remove no longer necessary API config lines
191
- - [ ] Some complex conformer searches hang on "running"
192
- - [X] Edit MCP one-liner context
193
- - [ ] Transition state finding and IRC
194
- - [X] `rowan_scan` - Potential energy surfaces
195
- - [ ] `rowan_docking` - Protein-ligand docking
196
- - [X] add in h-bond, BDE and macroscopic pka, logD, BBB
197
- - [ ] Folder listing API bug (returns 500 error) - Rowan side?
198
- - [ ] Multistage optimization sometimes shows unexpected imaginary frequencies
199
- - [X] Some calculations show as finished in logs but not in Rowan UI
200
- - [ ] Can you hook up Rowan's visual capabilites?
201
-
202
187
  ## **Citation**
203
188
 
204
189
  If you use this MCP tool in your research, please cite the underlying Rowan platform:
@@ -97,7 +97,7 @@ Ask the LLM to:
97
97
 
98
98
  ## **System Requirements**
99
99
 
100
- - **Python 3.10+** (Python 3.11+ recommended)
100
+ - **Python 3.11+**
101
101
  - **Package manager**: [uv](https://docs.astral.sh/uv/) (recommended) or pip
102
102
  - **Rowan API key** (free at [labs.rowansci.com](https://labs.rowansci.com))
103
103
  - **MCP-compatible client** (Claude Desktop, etc.)
@@ -140,7 +140,7 @@ uv run python -m rowan_mcp
140
140
 
141
141
  ## **Requirements**
142
142
 
143
- - Python 3.10+
143
+ - Python 3.11+
144
144
  - Rowan API key
145
145
  - MCP-compatible AI assistant (Claude Desktop, etc.)
146
146
 
@@ -151,21 +151,6 @@ uv run python -m rowan_mcp
151
151
 
152
152
  ---
153
153
 
154
- ## **Todo**
155
-
156
- - [X] Remove unnecessary AI spaghetti formatting
157
- - [X] Remove no longer necessary API config lines
158
- - [ ] Some complex conformer searches hang on "running"
159
- - [X] Edit MCP one-liner context
160
- - [ ] Transition state finding and IRC
161
- - [X] `rowan_scan` - Potential energy surfaces
162
- - [ ] `rowan_docking` - Protein-ligand docking
163
- - [X] add in h-bond, BDE and macroscopic pka, logD, BBB
164
- - [ ] Folder listing API bug (returns 500 error) - Rowan side?
165
- - [ ] Multistage optimization sometimes shows unexpected imaginary frequencies
166
- - [X] Some calculations show as finished in logs but not in Rowan UI
167
- - [ ] Can you hook up Rowan's visual capabilites?
168
-
169
154
  ## **Citation**
170
155
 
171
156
  If you use this MCP tool in your research, please cite the underlying Rowan platform:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "rowan-mcp"
7
- version = "2.0.0"
7
+ version = "2.0.1"
8
8
  description = "Model Context Protocol server for Rowan computational chemistry platform"
9
9
  authors = [
10
10
  {name = "Katherine Yenko", email = "katherineayenko@gmail.com"}
@@ -50,6 +50,16 @@ rowan-mcp = "rowan_mcp.server:main"
50
50
  [tool.hatch.build.targets.wheel]
51
51
  packages = ["rowan_mcp"]
52
52
 
53
+ [tool.hatch.build.targets.sdist]
54
+ exclude = [
55
+ "rowan-dxt/",
56
+ "dxt/",
57
+ "*.log",
58
+ "test_*.py",
59
+ ".venv/",
60
+ "*.dxt"
61
+ ]
62
+
53
63
  [tool.black]
54
64
  line-length = 88
55
65
  target-version = ['py38']
@@ -0,0 +1,89 @@
1
+ """
2
+ Rowan ADMET properties function for drug discovery.
3
+ """
4
+
5
+ import os
6
+ import logging
7
+ import time
8
+ import traceback
9
+ from typing import Optional
10
+
11
+ try:
12
+ import rowan
13
+ except ImportError:
14
+ rowan = None
15
+
16
+ # Setup logging
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ def log_rowan_api_call(workflow_type: str, **kwargs):
21
+ """Log Rowan API calls with detailed parameters."""
22
+
23
+ try:
24
+ start_time = time.time()
25
+ result = rowan.compute(workflow_type=workflow_type, **kwargs)
26
+ api_time = time.time() - start_time
27
+
28
+ return result
29
+
30
+ except Exception as e:
31
+ api_time = time.time() - start_time
32
+
33
+ raise e
34
+
35
+
36
+ def rowan_admet(
37
+ name: str,
38
+ molecule: str,
39
+ folder_uuid: Optional[str] = None,
40
+ blocking: bool = True,
41
+ ping_interval: int = 5
42
+ ) -> str:
43
+ """Predict ADME-Tox properties for drug discovery.
44
+
45
+ ADMET (Absorption, Distribution, Metabolism, Excretion, Toxicity) properties are crucial
46
+ for drug development. This workflow predicts drug-like properties including:
47
+ - Bioavailability and permeability
48
+ - Metabolic stability and clearance
49
+ - Toxicity indicators and safety profiles
50
+ - Drug-likeness metrics
51
+
52
+ Use this for: Drug discovery, pharmaceutical development, toxicity screening
53
+
54
+ Args:
55
+ name: Name for the calculation
56
+ molecule: Molecule SMILES string
57
+ folder_uuid: Optional folder UUID for organization
58
+ blocking: Whether to wait for completion (default: True)
59
+ ping_interval: Check status interval in seconds (default: 5)
60
+
61
+ Returns:
62
+ ADMET prediction results
63
+ """
64
+ result = log_rowan_api_call(
65
+ workflow_type="admet",
66
+ name=name,
67
+ molecule=molecule,
68
+ folder_uuid=folder_uuid,
69
+ blocking=blocking,
70
+ ping_interval=ping_interval
71
+ )
72
+ return str(result)
73
+
74
+
75
+ def test_rowan_admet():
76
+ """Test the rowan_admet function."""
77
+ try:
78
+ # Test with a simple molecule (non-blocking to avoid long wait)
79
+ result = rowan_admet("test_admet", "CCO", blocking=False)
80
+ print("ADMET test successful!")
81
+ print(f"Result length: {len(result)} characters")
82
+ return True
83
+ except Exception as e:
84
+ print(f"ADMET test failed: {e}")
85
+ return False
86
+
87
+
88
+ if __name__ == "__main__":
89
+ test_rowan_admet()
@@ -0,0 +1,106 @@
1
+ """
2
+ Calculate bond dissociation energy (BDE) for molecules to determine bond strength - useful for reaction prediction, stability analysis, or understanding molecular fragmentation. Modes: reckless/rapid/careful/meticulous. Input: molecule (SMILES), optional atoms to break.
3
+ """
4
+
5
+ import os
6
+ import rowan
7
+ from typing import Optional, List, Union
8
+
9
+ # Set up logging
10
+ import logging
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+
15
+ def log_rowan_api_call(workflow_type: str, **kwargs):
16
+ """Log Rowan API calls and let Rowan handle its own errors."""
17
+
18
+ # Simple logging for calculations
19
+ logger.info(f" Starting {workflow_type.replace('_', ' ')}...")
20
+
21
+ # Let Rowan handle everything - no custom error handling
22
+ return rowan.compute(workflow_type=workflow_type, **kwargs)
23
+
24
+ def rowan_bde(
25
+ name: str,
26
+ molecule: str,
27
+ mode: str = "rapid",
28
+ atoms: Optional[List[int]] = None,
29
+ optimize_fragments: Optional[bool] = None,
30
+ all_CH: bool = False,
31
+ all_CX: bool = False,
32
+ folder_uuid: Optional[str] = None,
33
+ blocking: bool = True,
34
+ ping_interval: int = 5
35
+ ) -> str:
36
+ """Calculate bond dissociation energy (BDE) for molecules.
37
+
38
+ Args:
39
+ name: Name for the calculation
40
+ molecule: Molecule SMILES string
41
+ mode: Calculation mode - reckless/rapid/careful/meticulous (default: rapid)
42
+ atoms: Specific atoms to dissociate (1-indexed)
43
+ optimize_fragments: Whether to optimize fragments (default depends on mode)
44
+ all_CH: Dissociate all C-H bonds (default: False)
45
+ all_CX: Dissociate all C-X bonds where X is halogen (default: False)
46
+ folder_uuid: UUID of folder to organize calculation in
47
+ blocking: Whether to wait for completion (default: True)
48
+ ping_interval: How often to check status in seconds (default: 5)
49
+
50
+ Returns:
51
+ Bond dissociation energy calculation results
52
+ """
53
+ try:
54
+ # Build kwargs for API call
55
+ kwargs = {
56
+ "workflow_type": "bde",
57
+ "name": name,
58
+ "molecule": molecule,
59
+ "mode": mode.lower(),
60
+ "folder_uuid": folder_uuid,
61
+ "blocking": blocking,
62
+ "ping_interval": ping_interval
63
+ }
64
+
65
+ # Add optional parameters only if specified
66
+ if atoms is not None:
67
+ kwargs["atoms"] = atoms
68
+ if optimize_fragments is not None:
69
+ kwargs["optimize_fragments"] = optimize_fragments
70
+ if all_CH:
71
+ kwargs["all_CH"] = all_CH
72
+ if all_CX:
73
+ kwargs["all_CX"] = all_CX
74
+
75
+ result = log_rowan_api_call(**kwargs)
76
+
77
+ return str(result)
78
+
79
+ except Exception as e:
80
+ error_response = {
81
+ "error": f"BDE calculation failed: {str(e)}",
82
+ "name": name,
83
+ "molecule": molecule
84
+ }
85
+ return str(error_response)
86
+
87
+
88
+ def test_rowan_bde():
89
+ """Test the rowan_bde function."""
90
+ try:
91
+ # Test with ethane C-C bond
92
+ result = rowan_bde(
93
+ name="test_ethane_CC",
94
+ molecule="CC",
95
+ mode="rapid"
96
+ )
97
+ print("✅ BDE test successful!")
98
+ print(f"Result: {result}")
99
+ return True
100
+ except Exception as e:
101
+ print(f"BDE test failed: {e}")
102
+ return False
103
+
104
+
105
+ if __name__ == "__main__":
106
+ test_rowan_bde()
@@ -0,0 +1,89 @@
1
+ """
2
+ Rowan calculation retrieval functions for MCP tool integration.
3
+ """
4
+
5
+ from typing import Optional, Dict, Any
6
+ import rowan
7
+
8
+ def rowan_calculation_retrieve(calculation_uuid: str) -> str:
9
+ """Retrieve details of a specific calculation.
10
+
11
+ Args:
12
+ calculation_uuid: UUID of the calculation to retrieve
13
+
14
+ Returns:
15
+ Calculation details
16
+ """
17
+
18
+ try:
19
+ calculation = rowan.Calculation.retrieve(uuid=calculation_uuid)
20
+ return format_calculation_details(calculation, calculation_uuid)
21
+
22
+ except Exception as e:
23
+ return f" Error retrieving calculation: {str(e)}"
24
+
25
+ # Fallback error handling removed - keeping function simple and focused
26
+
27
+ def format_calculation_details(calculation: Dict[str, Any], calculation_uuid: str) -> str:
28
+ """Format calculation details for display."""
29
+
30
+ formatted = f"⚙ **Calculation Details:**\n\n"
31
+ formatted += f" Name: {calculation.get('name', 'N/A')}\n"
32
+ formatted += f"UUID: {calculation_uuid}\n"
33
+ formatted += f" Status: {calculation.get('status', 'Unknown')}\n"
34
+ formatted += f" Elapsed: {calculation.get('elapsed', 0):.3f}s\n"
35
+
36
+ # Settings information
37
+ settings = calculation.get('settings', {})
38
+ if settings:
39
+ formatted += f"\n⚙ **Settings:**\n"
40
+ formatted += f" Method: {settings.get('method', 'N/A')}\n"
41
+ if settings.get('basis_set'):
42
+ formatted += f" Basis Set: {settings.get('basis_set')}\n"
43
+ if settings.get('tasks'):
44
+ formatted += f" Tasks: {', '.join(settings.get('tasks', []))}\n"
45
+
46
+ # Molecule information
47
+ molecules = calculation.get('molecules', [])
48
+ if molecules:
49
+ formatted += f"\n **Molecules:** {len(molecules)} structure(s)\n"
50
+ if len(molecules) > 0 and isinstance(molecules[0], dict):
51
+ first_mol = molecules[0]
52
+ if 'smiles' in first_mol:
53
+ formatted += f" Primary SMILES: {first_mol['smiles']}\n"
54
+
55
+ # Results/output data
56
+ if 'output' in calculation:
57
+ output = calculation['output']
58
+ formatted += f"\n **Results Available:**\n"
59
+ if isinstance(output, dict):
60
+ for key, value in list(output.items())[:5]: # Show first 5 items
61
+ if isinstance(value, (int, float)):
62
+ formatted += f" {key}: {value:.4f}\n"
63
+ elif isinstance(value, str) and len(value) < 50:
64
+ formatted += f" {key}: {value}\n"
65
+ elif isinstance(value, list) and len(value) < 10:
66
+ formatted += f" {key}: {value}\n"
67
+ else:
68
+ formatted += f" {key}: <complex data>\n"
69
+ else:
70
+ formatted += f" Raw output: {str(output)[:200]}...\n"
71
+
72
+ return formatted
73
+
74
+ # Specialized object data formatting removed - keeping function simple
75
+
76
+ def test_rowan_calculation_retrieve():
77
+ """Test the calculation retrieve function."""
78
+ try:
79
+ # Test with a dummy UUID to see error handling
80
+ result = rowan_calculation_retrieve("test-uuid-123")
81
+ print(" Calculation retrieve test successful!")
82
+ print(f"Sample result: {result[:200]}...")
83
+ return True
84
+ except Exception as e:
85
+ print(f" Calculation retrieve test failed: {e}")
86
+ return False
87
+
88
+ if __name__ == "__main__":
89
+ test_rowan_calculation_retrieve()
@@ -0,0 +1,77 @@
1
+ """
2
+ Rowan conformers function for conformational analysis.
3
+ """
4
+
5
+ import os
6
+ import logging
7
+ import time
8
+ from typing import Optional
9
+
10
+ try:
11
+ import rowan
12
+ except ImportError:
13
+ rowan = None
14
+
15
+ # Setup logging
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+
20
+ def log_rowan_api_call(workflow_type: str, **kwargs):
21
+ """Log Rowan API calls and let Rowan handle its own errors."""
22
+
23
+ # Simple logging for long-running calculations
24
+ if workflow_type in ["multistage_opt", "conformer_search"]:
25
+ blocking = kwargs.get('blocking', True)
26
+ if blocking:
27
+ logger.info(f" Starting {workflow_type.replace('_', ' ')}...")
28
+ else:
29
+ logger.info(f" Submitting {workflow_type.replace('_', ' ')} without waiting")
30
+
31
+ # Let Rowan handle everything - no custom error handling
32
+ return rowan.compute(workflow_type=workflow_type, **kwargs)
33
+
34
+ def rowan_conformers(
35
+ name: str,
36
+ molecule: str,
37
+ max_conformers: int = 50,
38
+ mode: str = "rapid",
39
+ folder_uuid: Optional[str] = None,
40
+ blocking: bool = True,
41
+ ping_interval: int = 5
42
+ ):
43
+ """Generate and optimize molecular conformers using Rowan's conformer_search workflow. Valid modes are "reckless", "rapid", "careful", and "meticulous", and default to using SMILES strings for the "molecule" parameter.
44
+
45
+ Args:
46
+ name: Name for the calculation
47
+ molecule: Molecule SMILES string or common name
48
+ max_conformers: Maximum number of conformers to generate (default: 50)
49
+ mode: Conformer search mode - "reckless", "rapid", "careful", "meticulous" (default: "rapid")
50
+ folder_uuid: UUID of folder to organize calculation in
51
+ blocking: Whether to wait for completion (default: True)
52
+ ping_interval: How often to check status in seconds (default: 5)
53
+
54
+ Returns:
55
+ Conformer search results (actual results if blocking=True)
56
+ """
57
+
58
+ # Validate mode parameter
59
+ valid_modes = ["reckless", "rapid", "careful", "meticulous"]
60
+ if mode not in valid_modes:
61
+ raise ValueError(
62
+ f"Invalid mode '{mode}'. Valid modes are: {', '.join(valid_modes)}"
63
+ )
64
+
65
+ return log_rowan_api_call(
66
+ workflow_type="conformer_search",
67
+ name=name,
68
+ molecule=molecule,
69
+ mode=mode,
70
+ max_conformers=max_conformers,
71
+ folder_uuid=folder_uuid,
72
+ blocking=blocking,
73
+ ping_interval=ping_interval
74
+ )
75
+
76
+ if __name__ == "__main__":
77
+ pass
@@ -0,0 +1,89 @@
1
+ """
2
+ Rowan molecular descriptors function for QSAR modeling.
3
+ """
4
+
5
+ import os
6
+ import logging
7
+ import time
8
+ from typing import Optional
9
+
10
+ try:
11
+ import rowan
12
+ except ImportError:
13
+ rowan = None
14
+
15
+ # Setup logging
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+
20
+ def log_rowan_api_call(workflow_type: str, **kwargs):
21
+ """Log Rowan API calls with detailed parameters."""
22
+
23
+ try:
24
+ start_time = time.time()
25
+ result = rowan.compute(workflow_type=workflow_type, **kwargs)
26
+ api_time = time.time() - start_time
27
+
28
+ if isinstance(result, dict) and 'uuid' in result:
29
+ job_status = result.get('status', result.get('object_status', 'Unknown'))
30
+ status_names = {0: "Queued", 1: "Running", 2: "Completed", 3: "Failed", 4: "Stopped", 5: "Awaiting Queue"}
31
+ status_text = status_names.get(job_status, f"Unknown ({job_status})")
32
+
33
+ return result
34
+
35
+ except Exception as e:
36
+ api_time = time.time() - start_time
37
+ raise e
38
+
39
+ def rowan_descriptors(
40
+ name: str,
41
+ molecule: str,
42
+ folder_uuid: Optional[str] = None,
43
+ blocking: bool = True,
44
+ ping_interval: int = 5
45
+ ) -> str:
46
+ """Calculate molecular descriptors for data science.
47
+
48
+ Generates comprehensive molecular descriptors including:
49
+ - Topological and geometric descriptors
50
+ - Electronic and physicochemical properties
51
+ - Graph-based molecular features
52
+ - Machine learning ready feature vectors
53
+
54
+ Use this for: QSAR modeling, machine learning, chemical space analysis
55
+
56
+ Args:
57
+ name: Name for the calculation
58
+ molecule: Molecule SMILES string
59
+ folder_uuid: Optional folder UUID for organization
60
+ blocking: Whether to wait for completion (default: True)
61
+ ping_interval: Check status interval in seconds (default: 5)
62
+
63
+ Returns:
64
+ Molecular descriptors results
65
+ """
66
+ result = log_rowan_api_call(
67
+ workflow_type="descriptors",
68
+ name=name,
69
+ molecule=molecule,
70
+ folder_uuid=folder_uuid,
71
+ blocking=blocking,
72
+ ping_interval=ping_interval
73
+ )
74
+ return str(result)
75
+
76
+ def test_rowan_descriptors():
77
+ """Test the rowan_descriptors function."""
78
+ try:
79
+ # Test with a simple molecule (non-blocking to avoid long wait)
80
+ result = rowan_descriptors("test_descriptors", "CCO", blocking=False)
81
+ print(" Descriptors test successful!")
82
+ print(f"Result length: {len(result)} characters")
83
+ return True
84
+ except Exception as e:
85
+ print(f" Descriptors test failed: {e}")
86
+ return False
87
+
88
+ if __name__ == "__main__":
89
+ test_rowan_descriptors()