agent-materials-science 0.2.1__py3-none-any.whl

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.
@@ -0,0 +1,26 @@
1
+ """
2
+ Materials Science Adsorption Agent
3
+
4
+ A Python agent for automated materials science workflows, focusing on
5
+ surface adsorption analysis using Materials Project data, ASE/pymatgen
6
+ tools, and FairChem UMA machine learning potentials.
7
+ """
8
+
9
+ from .config import AgentConfig
10
+ from .agent import (
11
+ MaterialsScienceAgent,
12
+ AgentResult,
13
+ InteractiveAgent,
14
+ run_analysis,
15
+ )
16
+
17
+ __version__ = "0.2.1"
18
+ __author__ = "Materials Science Agent"
19
+
20
+ __all__ = [
21
+ "MaterialsScienceAgent",
22
+ "AgentConfig",
23
+ "AgentResult",
24
+ "InteractiveAgent",
25
+ "run_analysis",
26
+ ]
@@ -0,0 +1,12 @@
1
+ """
2
+ Main entry point for running the agent as a module.
3
+
4
+ Usage:
5
+ python -m agent_materials_science --material Si --miller 1,1,1 --adsorbate H
6
+ """
7
+
8
+ import sys
9
+ from .cli import main
10
+
11
+ if __name__ == "__main__":
12
+ sys.exit(main())
@@ -0,0 +1,384 @@
1
+ """
2
+ Materials Science Adsorption Agent
3
+
4
+ This module provides the main agent class that orchestrates the complete
5
+ workflow for surface adsorption analysis.
6
+ """
7
+
8
+ from typing import Optional, Tuple, List, Dict, Any
9
+ from dataclasses import dataclass
10
+
11
+ from .config import AgentConfig, get_available_adsorbates, get_available_models
12
+ from .core.workflow import AdsorptionWorkflow, WorkflowResult
13
+ from .core.surface import SurfaceBuilder, TerminationInfo
14
+ from .core.adsorption import AdsorptionSiteFinder, AdsorptionSite
15
+ from .tools.materials_project import MaterialsProjectTool
16
+ from .tools.ase_tools import structure_to_atoms, get_slab_info
17
+ from .tools.converters import create_adsorbate, list_available_adsorbates
18
+ from .tools.fairchem_calc import FAIRCHEM_AVAILABLE, check_fairchem_installation
19
+
20
+
21
+ # Re-export WorkflowResult as AgentResult for cleaner API
22
+ AgentResult = WorkflowResult
23
+
24
+
25
+ class MaterialsScienceAgent:
26
+ """
27
+ Main agent for materials science adsorption analysis.
28
+
29
+ This agent provides a complete workflow for:
30
+ 1. Fetching materials from Materials Project
31
+ 2. Creating surface slabs with specific Miller indices and terminations
32
+ 3. Identifying adsorption sites on surfaces
33
+ 4. Calculating adsorption energies using ML potentials (FairChem)
34
+ 5. Ranking sites and generating reports
35
+
36
+ Example:
37
+ ```python
38
+ from agent_materials_science import MaterialsScienceAgent, AgentConfig
39
+
40
+ config = AgentConfig(
41
+ material="Si",
42
+ miller_indices=(1, 1, 1),
43
+ adsorbate="H",
44
+ calculate_energies=True,
45
+ )
46
+
47
+ agent = MaterialsScienceAgent(config)
48
+ result = agent.run()
49
+
50
+ print(result.summary())
51
+ ```
52
+ """
53
+
54
+ def __init__(self, config: AgentConfig):
55
+ """
56
+ Initialize the agent.
57
+
58
+ Args:
59
+ config: Agent configuration specifying material, surface,
60
+ adsorbate, and calculation parameters.
61
+ """
62
+ self.config = config
63
+ self._workflow = None
64
+ self._result = None
65
+
66
+ def run(self) -> AgentResult:
67
+ """
68
+ Execute the complete adsorption analysis workflow.
69
+
70
+ Returns:
71
+ AgentResult containing all analysis data, including:
72
+ - Material and slab information
73
+ - Identified adsorption sites
74
+ - Calculated energies (if requested)
75
+ - Output file paths
76
+ """
77
+ self._workflow = AdsorptionWorkflow(self.config)
78
+ self._result = self._workflow.run()
79
+ return self._result
80
+
81
+ @property
82
+ def result(self) -> Optional[AgentResult]:
83
+ """Get the last workflow result, if available."""
84
+ return self._result
85
+
86
+ def get_summary(self) -> str:
87
+ """
88
+ Get a human-readable summary of the results.
89
+
90
+ Returns:
91
+ Formatted summary string
92
+ """
93
+ if self._result is None:
94
+ return "No results available. Run the agent first."
95
+ return self._result.summary()
96
+
97
+ def get_best_site(self) -> Optional[Dict[str, Any]]:
98
+ """
99
+ Get the best adsorption site from the last run.
100
+
101
+ Returns:
102
+ Dictionary with site information, or None
103
+ """
104
+ if self._result is None:
105
+ return None
106
+ return self._result.best_site
107
+
108
+ def get_output_files(self) -> List[str]:
109
+ """
110
+ Get list of output files from the last run.
111
+
112
+ Returns:
113
+ List of file paths
114
+ """
115
+ if self._result is None:
116
+ return []
117
+ return self._result.output_files
118
+
119
+ @staticmethod
120
+ def check_installation() -> Dict[str, Any]:
121
+ """
122
+ Check the installation status of required dependencies.
123
+
124
+ Returns:
125
+ Dictionary with installation status
126
+ """
127
+ status = {
128
+ "fairchem": check_fairchem_installation(),
129
+ "available_adsorbates": get_available_adsorbates(),
130
+ "available_models": get_available_models(),
131
+ }
132
+
133
+ # Check Materials Project API
134
+ try:
135
+ from mp_api.client import MPRester
136
+ status["mp_api"] = True
137
+ except ImportError:
138
+ status["mp_api"] = False
139
+
140
+ # Check pymatgen
141
+ try:
142
+ from pymatgen.core import Structure
143
+ status["pymatgen"] = True
144
+ except ImportError:
145
+ status["pymatgen"] = False
146
+
147
+ # Check ASE
148
+ try:
149
+ from ase.atoms import Atoms
150
+ status["ase"] = True
151
+ except ImportError:
152
+ status["ase"] = False
153
+
154
+ return status
155
+
156
+ @staticmethod
157
+ def list_adsorbates() -> List[str]:
158
+ """
159
+ List available adsorbate species.
160
+
161
+ Returns:
162
+ List of adsorbate names
163
+ """
164
+ return list_available_adsorbates()
165
+
166
+ @staticmethod
167
+ def list_models() -> List[str]:
168
+ """
169
+ List available FairChem models.
170
+
171
+ Returns:
172
+ List of model names
173
+ """
174
+ return get_available_models()
175
+
176
+
177
+ class InteractiveAgent:
178
+ """
179
+ Interactive version of the agent with step-by-step control.
180
+
181
+ This class allows users to execute individual steps of the workflow
182
+ and inspect intermediate results.
183
+ """
184
+
185
+ def __init__(self, api_key: Optional[str] = None):
186
+ """
187
+ Initialize the interactive agent.
188
+
189
+ Args:
190
+ api_key: Materials Project API key (optional, uses env var if not provided)
191
+ """
192
+ self.mp_tool = MaterialsProjectTool(api_key=api_key)
193
+ self._bulk_structure = None
194
+ self._material_id = None
195
+ self._slab = None
196
+ self._terminations = None
197
+ self._sites = None
198
+ self._adsorbate = None
199
+
200
+ def fetch_material(self, material: str) -> Dict[str, Any]:
201
+ """
202
+ Fetch a material from Materials Project.
203
+
204
+ Args:
205
+ material: Formula (e.g., 'Si') or MP ID (e.g., 'mp-149')
206
+
207
+ Returns:
208
+ Material properties dictionary
209
+ """
210
+ if material.startswith("mp-"):
211
+ self._bulk_structure = self.mp_tool.get_structure_by_mp_id(material)
212
+ self._material_id = material
213
+ else:
214
+ self._bulk_structure, self._material_id = self.mp_tool.get_structure_by_formula(material)
215
+
216
+ props = self.mp_tool.get_material_properties(self._material_id)
217
+ return props
218
+
219
+ def list_terminations(
220
+ self,
221
+ miller: Tuple[int, int, int] = (1, 1, 1),
222
+ min_slab_size: float = 10.0,
223
+ vacuum: float = 15.0,
224
+ ) -> List[Dict[str, Any]]:
225
+ """
226
+ List available surface terminations.
227
+
228
+ Args:
229
+ miller: Miller indices
230
+ min_slab_size: Minimum slab thickness (Å)
231
+ vacuum: Vacuum thickness (Å)
232
+
233
+ Returns:
234
+ List of termination info dictionaries
235
+ """
236
+ if self._bulk_structure is None:
237
+ raise RuntimeError("Fetch a material first with fetch_material()")
238
+
239
+ builder = SurfaceBuilder(self._bulk_structure, miller)
240
+ self._terminations = builder.get_available_terminations(min_slab_size, vacuum)
241
+
242
+ return [t.to_dict() for t in self._terminations]
243
+
244
+ def create_slab(
245
+ self,
246
+ miller: Tuple[int, int, int] = (1, 1, 1),
247
+ termination: int = 0,
248
+ min_slab_size: float = 10.0,
249
+ vacuum: float = 15.0,
250
+ supercell: Tuple[int, int] = (1, 1),
251
+ ) -> Dict[str, Any]:
252
+ """
253
+ Create a surface slab.
254
+
255
+ Args:
256
+ miller: Miller indices
257
+ termination: Termination index (use list_terminations to see options)
258
+ min_slab_size: Minimum slab thickness (Å)
259
+ vacuum: Vacuum thickness (Å)
260
+ supercell: In-plane supercell (nx, ny)
261
+
262
+ Returns:
263
+ Slab info dictionary
264
+ """
265
+ if self._bulk_structure is None:
266
+ raise RuntimeError("Fetch a material first with fetch_material()")
267
+
268
+ builder = SurfaceBuilder(self._bulk_structure, miller)
269
+ self._slab = builder.build_slab(
270
+ termination=termination,
271
+ min_slab_size=min_slab_size,
272
+ min_vacuum_size=vacuum,
273
+ supercell=supercell,
274
+ )
275
+
276
+ return get_slab_info(self._slab)
277
+
278
+ def find_sites(
279
+ self,
280
+ height_offset: float = 2.0,
281
+ site_types: Optional[List[str]] = None,
282
+ ) -> List[Dict[str, Any]]:
283
+ """
284
+ Find adsorption sites on the slab.
285
+
286
+ Args:
287
+ height_offset: Height above surface for adsorbate (Å)
288
+ site_types: Types to include (default: all)
289
+
290
+ Returns:
291
+ List of site dictionaries
292
+ """
293
+ if self._slab is None:
294
+ raise RuntimeError("Create a slab first with create_slab()")
295
+
296
+ finder = AdsorptionSiteFinder(self._slab, height_offset=height_offset)
297
+ sites = finder.find_all_sites()
298
+
299
+ if site_types:
300
+ sites = finder.filter_by_type(sites, site_types)
301
+
302
+ sites = finder.remove_duplicates(sites)
303
+ self._sites = sites
304
+
305
+ return [s.to_dict() for s in sites]
306
+
307
+ def select_adsorbate(self, species: str) -> Dict[str, Any]:
308
+ """
309
+ Select an adsorbate species.
310
+
311
+ Args:
312
+ species: Adsorbate name (e.g., 'H', 'CO', 'OH')
313
+
314
+ Returns:
315
+ Adsorbate info dictionary
316
+ """
317
+ self._adsorbate = create_adsorbate(species, height=0)
318
+
319
+ return {
320
+ "species": species,
321
+ "n_atoms": len(self._adsorbate),
322
+ "elements": list(set(self._adsorbate.get_chemical_symbols())),
323
+ }
324
+
325
+ def get_slab(self):
326
+ """Get the current slab (ASE Atoms object)."""
327
+ return self._slab
328
+
329
+ def get_sites(self) -> List[AdsorptionSite]:
330
+ """Get the current sites (AdsorptionSite objects)."""
331
+ return self._sites or []
332
+
333
+ def get_adsorbate(self):
334
+ """Get the current adsorbate (ASE Atoms object)."""
335
+ return self._adsorbate
336
+
337
+
338
+ def run_analysis(
339
+ material: str,
340
+ miller: Tuple[int, int, int] = (1, 1, 1),
341
+ adsorbate: str = "H",
342
+ output_dir: str = "outputs",
343
+ calculate_energies: bool = False,
344
+ **kwargs
345
+ ) -> AgentResult:
346
+ """
347
+ Convenience function to run a complete analysis.
348
+
349
+ Args:
350
+ material: Material formula or MP ID
351
+ miller: Miller indices
352
+ adsorbate: Adsorbate species
353
+ output_dir: Output directory
354
+ calculate_energies: Whether to calculate adsorption energies
355
+ **kwargs: Additional configuration options
356
+
357
+ Returns:
358
+ AgentResult with analysis data
359
+
360
+ Example:
361
+ ```python
362
+ from agent_materials_science import run_analysis
363
+
364
+ result = run_analysis(
365
+ material="Pt",
366
+ miller=(1, 1, 1),
367
+ adsorbate="CO",
368
+ calculate_energies=True,
369
+ )
370
+
371
+ print(f"Best site: {result.best_site}")
372
+ ```
373
+ """
374
+ config = AgentConfig(
375
+ material=material,
376
+ miller_indices=miller,
377
+ adsorbate=adsorbate,
378
+ output_dir=output_dir,
379
+ calculate_energies=calculate_energies,
380
+ **kwargs
381
+ )
382
+
383
+ agent = MaterialsScienceAgent(config)
384
+ return agent.run()