know-do-graph 0.1.0__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.
- agents/__init__.py +0 -0
- agents/extraction_agent/__init__.py +0 -0
- agents/extraction_agent/agent.py +170 -0
- agents/graph_agent/__init__.py +5 -0
- agents/graph_agent/agent.py +373 -0
- agents/graph_agent/tools.py +2106 -0
- agents/maintenance_agent/__init__.py +0 -0
- agents/maintenance_agent/agent.py +283 -0
- agents/orchestrator/__init__.py +0 -0
- agents/orchestrator/agent.py +217 -0
- agents/review_agent/__init__.py +0 -0
- agents/review_agent/agent.py +188 -0
- agents/review_agent/tools.py +472 -0
- api/__init__.py +0 -0
- api/main.py +136 -0
- api/routes/__init__.py +0 -0
- api/routes/agent.py +81 -0
- api/routes/entries.py +411 -0
- api/routes/graph.py +132 -0
- api/routes/mem.py +179 -0
- api/routes/remote.py +815 -0
- api/routes/remote_sync.py +230 -0
- api/routes/retrieve.py +88 -0
- core/__init__.py +0 -0
- core/app_state.py +9 -0
- core/events.py +84 -0
- core/extraction/__init__.py +0 -0
- core/extraction/wikilink_parser.py +48 -0
- core/graph/__init__.py +0 -0
- core/graph/graph.py +204 -0
- core/memory/__init__.py +0 -0
- core/memory/memgraph.py +458 -0
- core/resources/starter.db +0 -0
- core/retrieval/__init__.py +0 -0
- core/retrieval/embedder.py +122 -0
- core/retrieval/fusion.py +52 -0
- core/retrieval/progressive.py +399 -0
- core/retrieval/retrieval.py +346 -0
- core/retrieval/vector_store.py +91 -0
- core/schemas/__init__.py +0 -0
- core/schemas/edge.py +46 -0
- core/schemas/entry.py +388 -0
- core/storage/__init__.py +0 -0
- core/storage/database.py +104 -0
- core/storage/models.py +66 -0
- core/storage/repository.py +243 -0
- core/sync/__init__.py +20 -0
- core/sync/autolink.py +301 -0
- core/sync/db_merge.py +297 -0
- core/sync/db_watcher.py +84 -0
- core/sync/remote_sync.py +345 -0
- examples/__init__.py +0 -0
- examples/example_entries.py +206 -0
- examples/pymatgen_interface_examples.py +811 -0
- frontend/dist/assets/index-BLfo7ZZu.css +1 -0
- frontend/dist/assets/index-G-mYbZ9R.js +83 -0
- frontend/dist/assets/index-G-mYbZ9R.js.map +1 -0
- frontend/dist/index.html +92 -0
- know_do_graph-0.1.0.dist-info/METADATA +765 -0
- know_do_graph-0.1.0.dist-info/RECORD +63 -0
- know_do_graph-0.1.0.dist-info/WHEEL +4 -0
- know_do_graph-0.1.0.dist-info/entry_points.txt +2 -0
- main.py +944 -0
|
@@ -0,0 +1,811 @@
|
|
|
1
|
+
"""Seed the graph with generalised pymatgen interface-building knowledge and runnable scripts.
|
|
2
|
+
|
|
3
|
+
Populates:
|
|
4
|
+
- Dependency entries (pymatgen, ASE, mp-api, ...)
|
|
5
|
+
- Generalised capability / procedure entries (lattice matching, slab generation, ...)
|
|
6
|
+
- Runnable script entries (downloadable via GET /entries/{id}/download)
|
|
7
|
+
|
|
8
|
+
Note: Specific material instances (Si, Ge, GaN, ...) and specific material-pair
|
|
9
|
+
interface nodes are intentionally excluded — those are examples / instantiations,
|
|
10
|
+
not generalised knowledge worth storing in the graph.
|
|
11
|
+
|
|
12
|
+
Usage
|
|
13
|
+
-----
|
|
14
|
+
python examples/pymatgen_interface_examples.py
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import sys
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
23
|
+
|
|
24
|
+
from core import app_state
|
|
25
|
+
from core.schemas.edge import Edge, EdgeRelation
|
|
26
|
+
from core.schemas.entry import Entry, EntryMetadata, EntryType, RefinementStatus, VerificationStatus
|
|
27
|
+
from core.storage.database import SessionLocal, init_db
|
|
28
|
+
from core.storage.repository import EdgeRepository, EntryRepository
|
|
29
|
+
|
|
30
|
+
# ---------------------------------------------------------------------------
|
|
31
|
+
# Helper
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
def _existing_titles(repo: EntryRepository) -> set[str]:
|
|
35
|
+
return {e.title for e in repo.get_all()}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _create(repo: EntryRepository, data: dict) -> Entry | None:
|
|
39
|
+
entry = Entry(**data)
|
|
40
|
+
return repo.create(entry)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
# 1. Dependency entries
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
DEPS: list[dict] = [
|
|
48
|
+
{
|
|
49
|
+
"title": "pymatgen",
|
|
50
|
+
"entry_type": EntryType.dependency,
|
|
51
|
+
"tags": ["python", "materials-science", "library", "dft"],
|
|
52
|
+
"aliases": ["Python Materials Genomics"],
|
|
53
|
+
"content": """\
|
|
54
|
+
# pymatgen — Python Materials Genomics
|
|
55
|
+
|
|
56
|
+
Core library for materials analysis, structure manipulation, and interface construction.
|
|
57
|
+
|
|
58
|
+
## Install
|
|
59
|
+
```bash
|
|
60
|
+
pip install pymatgen
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Key modules for interfaces
|
|
64
|
+
- `pymatgen.core.surface` — `SlabGenerator`, `ReconstructionGenerator`
|
|
65
|
+
- `pymatgen.analysis.interfaces` — `SubstrateAnalyzer`, `InterfaceBuilder`, `CohenValence`
|
|
66
|
+
- `pymatgen.core.structure` — `Structure`, `Lattice`
|
|
67
|
+
- `pymatgen.io.vasp` — POSCAR / CONTCAR read-write
|
|
68
|
+
- `pymatgen.io.ase` — ASE ↔ pymatgen bridge
|
|
69
|
+
|
|
70
|
+
## External
|
|
71
|
+
- https://pymatgen.org/
|
|
72
|
+
- https://github.com/materialsproject/pymatgen
|
|
73
|
+
""",
|
|
74
|
+
"metadata": EntryMetadata(
|
|
75
|
+
source_provenance="https://pymatgen.org/",
|
|
76
|
+
refinement_status=RefinementStatus.validated,
|
|
77
|
+
verification_status=VerificationStatus.community_tested,
|
|
78
|
+
),
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"title": "ASE",
|
|
82
|
+
"entry_type": EntryType.dependency,
|
|
83
|
+
"tags": ["python", "atomistic", "library"],
|
|
84
|
+
"aliases": ["Atomic Simulation Environment"],
|
|
85
|
+
"content": """\
|
|
86
|
+
# ASE — Atomic Simulation Environment
|
|
87
|
+
|
|
88
|
+
Python library for atomistic simulations.
|
|
89
|
+
Interoperates with [[pymatgen]] via `pymatgen.io.ase`.
|
|
90
|
+
|
|
91
|
+
## Install
|
|
92
|
+
```bash
|
|
93
|
+
pip install ase
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## External
|
|
97
|
+
- https://wiki.fysik.dtu.dk/ase/
|
|
98
|
+
""",
|
|
99
|
+
"metadata": EntryMetadata(
|
|
100
|
+
source_provenance="https://wiki.fysik.dtu.dk/ase/",
|
|
101
|
+
refinement_status=RefinementStatus.validated,
|
|
102
|
+
),
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"title": "mp-api",
|
|
106
|
+
"entry_type": EntryType.dependency,
|
|
107
|
+
"tags": ["python", "materials-project", "api", "library"],
|
|
108
|
+
"aliases": ["Materials Project API", "MPRester"],
|
|
109
|
+
"content": """\
|
|
110
|
+
# mp-api — Materials Project REST API Client
|
|
111
|
+
|
|
112
|
+
Download bulk structures, band structures, and properties from the Materials Project database.
|
|
113
|
+
|
|
114
|
+
## Install
|
|
115
|
+
```bash
|
|
116
|
+
pip install mp-api
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Usage
|
|
120
|
+
```python
|
|
121
|
+
from mp_api.client import MPRester
|
|
122
|
+
with MPRester("YOUR_MP_API_KEY") as mpr:
|
|
123
|
+
structure = mpr.get_structure_by_material_id("mp-149") # Si
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## External
|
|
127
|
+
- https://api.materialsproject.org/
|
|
128
|
+
- https://next-gen.materialsproject.org/
|
|
129
|
+
""",
|
|
130
|
+
"metadata": EntryMetadata(
|
|
131
|
+
source_provenance="https://api.materialsproject.org/",
|
|
132
|
+
refinement_status=RefinementStatus.validated,
|
|
133
|
+
),
|
|
134
|
+
},
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
# ---------------------------------------------------------------------------
|
|
138
|
+
# 2. Procedure entries
|
|
139
|
+
# ---------------------------------------------------------------------------
|
|
140
|
+
|
|
141
|
+
# NOTE: MATERIALS and INTERFACES lists removed. Specific material instances
|
|
142
|
+
# (Si, Ge, GaN, etc.) and specific material-pair interface nodes are
|
|
143
|
+
# instantiations, not generalised knowledge.
|
|
144
|
+
|
|
145
|
+
MATERIALS: list[dict] = [] # intentionally empty
|
|
146
|
+
INTERFACES: list[dict] = [] # intentionally empty
|
|
147
|
+
|
|
148
|
+
PROCEDURES: list[dict] = [
|
|
149
|
+
{
|
|
150
|
+
"title": "Build Material Interface via Slab Stacking",
|
|
151
|
+
"entry_type": EntryType.data,
|
|
152
|
+
"tags": ["interface-construction", "slab-stacking", "pymatgen", "heterostructure"],
|
|
153
|
+
"content": """\
|
|
154
|
+
## Build Material Interface via Slab Stacking
|
|
155
|
+
|
|
156
|
+
General workflow for constructing a heterointerface between two materials using
|
|
157
|
+
the slab-stacking method with [[pymatgen]].
|
|
158
|
+
|
|
159
|
+
### Prerequisites
|
|
160
|
+
- [[pymatgen]]
|
|
161
|
+
- Bulk structures for substrate and film (from [[mp-api]] or local CIF files)
|
|
162
|
+
|
|
163
|
+
### Steps
|
|
164
|
+
1. Download bulk structures using [[Download Bulk Structure from Materials Project]].
|
|
165
|
+
2. Run [[Substrate Analysis Script]] to find compatible orientations and quantify lattice mismatch.
|
|
166
|
+
3. Generate oriented slabs for both materials with [[Slab Generation Script]].
|
|
167
|
+
4. Build the interface supercell with [[Interface Builder Script]].
|
|
168
|
+
5. Run ionic relaxation (e.g. [[ASE Relaxation]] with a DFT or ML calculator).
|
|
169
|
+
6. Compute interface energy with [[Interface Energy Calculation Script]].
|
|
170
|
+
7. Optionally compute band alignment with [[Band Alignment Calculation Script]].
|
|
171
|
+
|
|
172
|
+
### Key considerations
|
|
173
|
+
- Lattice mismatch determines strain state; use [[Lattice Matching via ZSL Algorithm]] to
|
|
174
|
+
find the minimum-strain supercell.
|
|
175
|
+
- Polar surfaces (e.g. wurtzite nitrides) require termination treatment to
|
|
176
|
+
avoid a diverging electrostatic potential (Tasker Type-III rule).
|
|
177
|
+
- Vacuum layer thickness: ≥ 15 Å to suppress image interactions.
|
|
178
|
+
|
|
179
|
+
### Expected outputs
|
|
180
|
+
- Interface POSCAR / CIF file
|
|
181
|
+
- Interface energy in J/m²
|
|
182
|
+
- Band offsets (from DFT or model calculations)
|
|
183
|
+
""",
|
|
184
|
+
"metadata": EntryMetadata(refinement_status=RefinementStatus.linked),
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"title": "Download Bulk Structure from Materials Project",
|
|
188
|
+
"entry_type": EntryType.procedure,
|
|
189
|
+
"tags": ["materials-project", "pymatgen", "data-retrieval", "mp-api"],
|
|
190
|
+
"content": """\
|
|
191
|
+
## Download Bulk Structure from Materials Project
|
|
192
|
+
|
|
193
|
+
Retrieve a relaxed bulk structure using [[mp-api]].
|
|
194
|
+
|
|
195
|
+
### Prerequisites
|
|
196
|
+
- [[mp-api]] (`pip install mp-api`)
|
|
197
|
+
- A valid Materials Project API key (set as `MP_API_KEY` environment variable)
|
|
198
|
+
|
|
199
|
+
### Steps
|
|
200
|
+
1. Look up the material ID (e.g. `mp-149` for Si) on https://materialsproject.org.
|
|
201
|
+
2. Run [[Bulk Structure Download Script]] to fetch and save the structure.
|
|
202
|
+
3. Verify the structure with `pymatgen.core.structure.Structure.from_file`.
|
|
203
|
+
|
|
204
|
+
### Outputs
|
|
205
|
+
- `{formula}_{mp_id}.cif` or `POSCAR_{formula}` file
|
|
206
|
+
""",
|
|
207
|
+
"metadata": EntryMetadata(refinement_status=RefinementStatus.linked),
|
|
208
|
+
},
|
|
209
|
+
]
|
|
210
|
+
|
|
211
|
+
# ---------------------------------------------------------------------------
|
|
212
|
+
# 3. Script entries (actual runnable Python code)
|
|
213
|
+
# ---------------------------------------------------------------------------
|
|
214
|
+
|
|
215
|
+
_BULK_DOWNLOAD_CODE = '''\
|
|
216
|
+
"""Download a bulk crystal structure from the Materials Project.
|
|
217
|
+
|
|
218
|
+
Requirements: mp-api pymatgen
|
|
219
|
+
Usage: python download_bulk_structure.py
|
|
220
|
+
"""
|
|
221
|
+
import os
|
|
222
|
+
from pathlib import Path
|
|
223
|
+
|
|
224
|
+
from mp_api.client import MPRester
|
|
225
|
+
|
|
226
|
+
# ── Configuration ──────────────────────────────────────────────────────────────
|
|
227
|
+
MP_IDS = {
|
|
228
|
+
"Si": "mp-149",
|
|
229
|
+
"Ge": "mp-32",
|
|
230
|
+
"GaN": "mp-804",
|
|
231
|
+
"AlN": "mp-661",
|
|
232
|
+
"TiO2": "mp-2657",
|
|
233
|
+
"SrTiO3": "mp-5229",
|
|
234
|
+
}
|
|
235
|
+
OUTPUT_DIR = Path("structures")
|
|
236
|
+
OUTPUT_DIR.mkdir(exist_ok=True)
|
|
237
|
+
|
|
238
|
+
api_key = os.environ.get("MP_API_KEY", "")
|
|
239
|
+
if not api_key:
|
|
240
|
+
raise EnvironmentError("Set the MP_API_KEY environment variable.")
|
|
241
|
+
|
|
242
|
+
with MPRester(api_key) as mpr:
|
|
243
|
+
for name, mp_id in MP_IDS.items():
|
|
244
|
+
structure = mpr.get_structure_by_material_id(mp_id)
|
|
245
|
+
out_path = OUTPUT_DIR / f"{name}_{mp_id}.cif"
|
|
246
|
+
structure.to(str(out_path))
|
|
247
|
+
print(f" Saved {name} → {out_path}")
|
|
248
|
+
|
|
249
|
+
print("\\nDone. Structures saved to:", OUTPUT_DIR.resolve())
|
|
250
|
+
'''
|
|
251
|
+
|
|
252
|
+
_SUBSTRATE_ANALYSIS_CODE = '''\
|
|
253
|
+
"""Substrate / film lattice matching analysis using pymatgen SubstrateAnalyzer.
|
|
254
|
+
|
|
255
|
+
For a given substrate and film structure, finds all crystallographic orientations
|
|
256
|
+
and in-plane supercell combinations that minimise lattice mismatch.
|
|
257
|
+
|
|
258
|
+
Requirements: pymatgen
|
|
259
|
+
Usage: python substrate_analysis.py
|
|
260
|
+
"""
|
|
261
|
+
from __future__ import annotations
|
|
262
|
+
|
|
263
|
+
from pathlib import Path
|
|
264
|
+
|
|
265
|
+
from pymatgen.analysis.interfaces.substrate_analyzer import SubstrateAnalyzer
|
|
266
|
+
from pymatgen.core import Structure
|
|
267
|
+
|
|
268
|
+
# ── Configuration ──────────────────────────────────────────────────────────────
|
|
269
|
+
# Update these paths to your local CIF or POSCAR files
|
|
270
|
+
SUBSTRATE_FILE = "structures/Si_mp-149.cif" # e.g. Si
|
|
271
|
+
FILM_FILE = "structures/Ge_mp-32.cif" # e.g. Ge
|
|
272
|
+
|
|
273
|
+
MAX_AREA = 200 # max supercell area (Ų) to search
|
|
274
|
+
MAX_MISMATCH = 0.10 # max linear mismatch fraction (10 %)
|
|
275
|
+
MAX_ANGLE = 0.01 # max angle mismatch (rad)
|
|
276
|
+
|
|
277
|
+
# ── Load structures ────────────────────────────────────────────────────────────
|
|
278
|
+
substrate = Structure.from_file(SUBSTRATE_FILE)
|
|
279
|
+
film = Structure.from_file(FILM_FILE)
|
|
280
|
+
|
|
281
|
+
print(f"Substrate: {substrate.formula} spacegroup: {substrate.get_space_group_info()}")
|
|
282
|
+
print(f"Film: {film.formula} spacegroup: {film.get_space_group_info()}")
|
|
283
|
+
|
|
284
|
+
# ── Run substrate analyzer ────────────────────────────────────────────────────
|
|
285
|
+
analyzer = SubstrateAnalyzer(max_area=MAX_AREA)
|
|
286
|
+
matches = list(analyzer.calculate(film, substrate, lowest=True))
|
|
287
|
+
|
|
288
|
+
if not matches:
|
|
289
|
+
print("\\nNo matching orientations found with current search parameters.")
|
|
290
|
+
else:
|
|
291
|
+
print(f"\\nFound {len(matches)} orientation match(es):\\n")
|
|
292
|
+
for i, m in enumerate(matches[:10]): # show top 10
|
|
293
|
+
print(
|
|
294
|
+
f" [{i+1}] substrate_miller={m.substrate_miller} "
|
|
295
|
+
f"film_miller={m.film_miller} "
|
|
296
|
+
f"mismatch={m.mismatch:.4f} "
|
|
297
|
+
f"strain_energy={m.elastic_energy:.3f} eV/Ų "
|
|
298
|
+
f"sub_sl_vecs={m.substrate_sl_vectors}"
|
|
299
|
+
)
|
|
300
|
+
'''
|
|
301
|
+
|
|
302
|
+
_SLAB_GENERATION_CODE = '''\
|
|
303
|
+
"""Generate oriented slab models from bulk structures using pymatgen SlabGenerator.
|
|
304
|
+
|
|
305
|
+
Requirements: pymatgen
|
|
306
|
+
Usage: python slab_generation.py
|
|
307
|
+
"""
|
|
308
|
+
from __future__ import annotations
|
|
309
|
+
|
|
310
|
+
from pathlib import Path
|
|
311
|
+
|
|
312
|
+
from pymatgen.core import Structure
|
|
313
|
+
from pymatgen.core.surface import SlabGenerator
|
|
314
|
+
|
|
315
|
+
# ── Configuration ──────────────────────────────────────────────────────────────
|
|
316
|
+
BULK_FILE = "structures/Si_mp-149.cif" # bulk structure
|
|
317
|
+
MILLER_INDEX = (0, 0, 1) # surface orientation
|
|
318
|
+
MIN_SLAB_SIZE = 10.0 # minimum slab thickness (Å)
|
|
319
|
+
MIN_VACUUM_SIZE = 15.0 # vacuum layer thickness (Å)
|
|
320
|
+
IN_UNIT_PLANES = False # use Å not unit cells
|
|
321
|
+
PRIMITIVE = False # keep conventional cell
|
|
322
|
+
SYMMETRIZE = True # make slab symmetric (equal top/bottom)
|
|
323
|
+
MAX_NORMAL_SEARCH = 2 # search depth for non-orthogonal slabs
|
|
324
|
+
|
|
325
|
+
OUTPUT_DIR = Path("slabs")
|
|
326
|
+
OUTPUT_DIR.mkdir(exist_ok=True)
|
|
327
|
+
|
|
328
|
+
# ── Load bulk and generate slabs ──────────────────────────────────────────────
|
|
329
|
+
bulk = Structure.from_file(BULK_FILE)
|
|
330
|
+
print(f"Bulk: {bulk.formula} ({bulk.num_sites} atoms)")
|
|
331
|
+
|
|
332
|
+
gen = SlabGenerator(
|
|
333
|
+
bulk,
|
|
334
|
+
miller_index=MILLER_INDEX,
|
|
335
|
+
min_slab_size=MIN_SLAB_SIZE,
|
|
336
|
+
min_vacuum_size=MIN_VACUUM_SIZE,
|
|
337
|
+
in_unit_planes=IN_UNIT_PLANES,
|
|
338
|
+
primitive=PRIMITIVE,
|
|
339
|
+
max_normal_search=MAX_NORMAL_SEARCH,
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
slabs = gen.get_slabs(symmetrize=SYMMETRIZE)
|
|
343
|
+
print(f"\\nGenerated {len(slabs)} slab termination(s) for {bulk.formula}{list(MILLER_INDEX)}:\\n")
|
|
344
|
+
|
|
345
|
+
for i, slab in enumerate(slabs):
|
|
346
|
+
fname = OUTPUT_DIR / f"{bulk.formula}_{''.join(map(str, MILLER_INDEX))}_term{i}.cif"
|
|
347
|
+
slab.to(str(fname))
|
|
348
|
+
print(
|
|
349
|
+
f" Termination {i}: {slab.num_sites} atoms "
|
|
350
|
+
f"thickness={slab.get_orthogonal_c_slab().lattice.c:.2f} Å → {fname.name}"
|
|
351
|
+
)
|
|
352
|
+
'''
|
|
353
|
+
|
|
354
|
+
_INTERFACE_BUILDER_CODE = '''\
|
|
355
|
+
"""Build a heterointerface supercell using pymatgen InterfaceBuilder.
|
|
356
|
+
|
|
357
|
+
Stacks a film slab on a substrate slab, matching the in-plane lattice vectors.
|
|
358
|
+
Produces a POSCAR ready for DFT or ML-IP relaxation.
|
|
359
|
+
|
|
360
|
+
Requirements: pymatgen
|
|
361
|
+
Usage: python interface_builder.py
|
|
362
|
+
"""
|
|
363
|
+
from __future__ import annotations
|
|
364
|
+
|
|
365
|
+
from pathlib import Path
|
|
366
|
+
|
|
367
|
+
from pymatgen.analysis.interfaces.coherent_interfaces import CoherentInterfaceBuilder
|
|
368
|
+
from pymatgen.core import Structure
|
|
369
|
+
|
|
370
|
+
# ── Configuration ──────────────────────────────────────────────────────────────
|
|
371
|
+
SUBSTRATE_FILE = "structures/Si_mp-149.cif"
|
|
372
|
+
FILM_FILE = "structures/Ge_mp-32.cif"
|
|
373
|
+
|
|
374
|
+
SUBSTRATE_MILLER = (0, 0, 1)
|
|
375
|
+
FILM_MILLER = (0, 0, 1)
|
|
376
|
+
|
|
377
|
+
IN_LAYERS_SUBSTRATE = 4 # number of substrate layers
|
|
378
|
+
IN_LAYERS_FILM = 3 # number of film layers
|
|
379
|
+
VACUUM = 20.0 # Å vacuum above the film
|
|
380
|
+
|
|
381
|
+
OUTPUT_DIR = Path("interfaces")
|
|
382
|
+
OUTPUT_DIR.mkdir(exist_ok=True)
|
|
383
|
+
|
|
384
|
+
# ── Load structures ────────────────────────────────────────────────────────────
|
|
385
|
+
substrate_bulk = Structure.from_file(SUBSTRATE_FILE)
|
|
386
|
+
film_bulk = Structure.from_file(FILM_FILE)
|
|
387
|
+
|
|
388
|
+
print(f"Substrate bulk: {substrate_bulk.formula}")
|
|
389
|
+
print(f"Film bulk: {film_bulk.formula}")
|
|
390
|
+
|
|
391
|
+
# ── Build interface ────────────────────────────────────────────────────────────
|
|
392
|
+
builder = CoherentInterfaceBuilder(
|
|
393
|
+
substrate_structure=substrate_bulk,
|
|
394
|
+
film_structure=film_bulk,
|
|
395
|
+
substrate_miller=SUBSTRATE_MILLER,
|
|
396
|
+
film_miller=FILM_MILLER,
|
|
397
|
+
zslgen=None, # use default ZSL generator settings
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
interfaces = list(
|
|
401
|
+
builder.get_interfaces(
|
|
402
|
+
termination=builder.terminations[0],
|
|
403
|
+
gap=2.5, # Å gap at interface
|
|
404
|
+
vacuum_over_film=VACUUM,
|
|
405
|
+
film_thickness=IN_LAYERS_FILM,
|
|
406
|
+
substrate_thickness=IN_LAYERS_SUBSTRATE,
|
|
407
|
+
in_layers=True,
|
|
408
|
+
)
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
print(f"\\nBuilt {len(interfaces)} interface structure(s):")
|
|
412
|
+
for i, iface in enumerate(interfaces):
|
|
413
|
+
fname = OUTPUT_DIR / (
|
|
414
|
+
f"{substrate_bulk.formula}_{film_bulk.formula}_"
|
|
415
|
+
f"{''.join(map(str,SUBSTRATE_MILLER))}-{''.join(map(str,FILM_MILLER))}"
|
|
416
|
+
f"_v{i}.vasp"
|
|
417
|
+
)
|
|
418
|
+
iface.to(str(fname), fmt="poscar")
|
|
419
|
+
print(
|
|
420
|
+
f" [{i}] {iface.num_sites} atoms "
|
|
421
|
+
f"a={iface.lattice.a:.3f} b={iface.lattice.b:.3f} "
|
|
422
|
+
f"c={iface.lattice.c:.3f} Å → {fname.name}"
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
if interfaces:
|
|
426
|
+
best = interfaces[0]
|
|
427
|
+
print(f"\\nBest interface: {best.num_sites} sites, saved as {fname.name}")
|
|
428
|
+
'''
|
|
429
|
+
|
|
430
|
+
_INTERFACE_ENERGY_CODE = '''\
|
|
431
|
+
"""Compute interface energy from DFT/ML-IP total energies.
|
|
432
|
+
|
|
433
|
+
Uses the formula:
|
|
434
|
+
E_interface = (E_slab_AB - E_slab_A - E_slab_B) / (2 * A)
|
|
435
|
+
|
|
436
|
+
where A is the interface cross-sectional area.
|
|
437
|
+
|
|
438
|
+
Requirements: pymatgen, ase (for energy extraction), numpy
|
|
439
|
+
Usage: python interface_energy.py
|
|
440
|
+
"""
|
|
441
|
+
from __future__ import annotations
|
|
442
|
+
|
|
443
|
+
import numpy as np
|
|
444
|
+
|
|
445
|
+
# ── Input total energies (replace with your DFT/MLIP values) ──────────────────
|
|
446
|
+
# All energies in eV; structures from files written by interface_builder.py
|
|
447
|
+
|
|
448
|
+
E_slab_AB = -1234.56 # eV — total energy of the full interface supercell
|
|
449
|
+
E_slab_A = -800.00 # eV — total energy of substrate slab (same cell, no film)
|
|
450
|
+
E_slab_B = -430.00 # eV — total energy of film slab (same cell, no substrate)
|
|
451
|
+
|
|
452
|
+
# Lattice parameters of the interface cell (in Å)
|
|
453
|
+
a = 5.431
|
|
454
|
+
b = 5.431
|
|
455
|
+
|
|
456
|
+
# ── Calculation ────────────────────────────────────────────────────────────────
|
|
457
|
+
area_ang2 = a * b # Ų
|
|
458
|
+
area_m2 = area_ang2 * 1e-20 # m²
|
|
459
|
+
eV_to_J = 1.602176634e-19 # J/eV
|
|
460
|
+
|
|
461
|
+
E_interface_eV = E_slab_AB - E_slab_A - E_slab_B
|
|
462
|
+
# Factor 2: two equivalent interfaces created when periodic slab is cut
|
|
463
|
+
E_interface_J_m2 = (E_interface_eV * eV_to_J) / (2 * area_m2)
|
|
464
|
+
|
|
465
|
+
print(f"Interface energy components:")
|
|
466
|
+
print(f" E(slab_AB) = {E_slab_AB:.4f} eV")
|
|
467
|
+
print(f" E(slab_A) = {E_slab_A:.4f} eV")
|
|
468
|
+
print(f" E(slab_B) = {E_slab_B:.4f} eV")
|
|
469
|
+
print(f" ΔE = {E_interface_eV:.4f} eV")
|
|
470
|
+
print(f" Area = {area_ang2:.3f} Ų = {area_m2:.4e} m²")
|
|
471
|
+
print(f"\\nInterface energy = {E_interface_J_m2:.4f} J/m²")
|
|
472
|
+
|
|
473
|
+
# Typical ranges:
|
|
474
|
+
# Low energy (stable): 0.0 – 0.5 J/m²
|
|
475
|
+
# Medium energy: 0.5 – 1.5 J/m²
|
|
476
|
+
# High energy (strained):> 1.5 J/m²
|
|
477
|
+
if E_interface_J_m2 < 0.5:
|
|
478
|
+
print("Assessment: low-energy (thermodynamically stable) interface")
|
|
479
|
+
elif E_interface_J_m2 < 1.5:
|
|
480
|
+
print("Assessment: moderate-energy interface")
|
|
481
|
+
else:
|
|
482
|
+
print("Assessment: high-energy interface — consider strain relaxation or reconstruction")
|
|
483
|
+
'''
|
|
484
|
+
|
|
485
|
+
_BAND_ALIGNMENT_CODE = '''\
|
|
486
|
+
"""Compute natural band alignment and band offset at a heterointerface.
|
|
487
|
+
|
|
488
|
+
Uses the branch-point energy (BPE) / charge neutrality level (CNL) method
|
|
489
|
+
and the band-gap / VBM values from DFT calculations.
|
|
490
|
+
|
|
491
|
+
Requirements: numpy
|
|
492
|
+
Usage: python band_alignment.py
|
|
493
|
+
"""
|
|
494
|
+
from __future__ import annotations
|
|
495
|
+
|
|
496
|
+
import numpy as np
|
|
497
|
+
|
|
498
|
+
# ── Material parameters (fill in from your DFT results) ───────────────────────
|
|
499
|
+
# All energies relative to the average electrostatic potential of the bulk slab.
|
|
500
|
+
|
|
501
|
+
materials = {
|
|
502
|
+
"Si": {
|
|
503
|
+
"Eg": 1.12, # eV — band gap
|
|
504
|
+
"VBM_bulk": 0.00, # eV — VBM (set reference to 0 for first material)
|
|
505
|
+
"BPE_bulk": -4.05, # eV — branch-point energy w.r.t. VBM
|
|
506
|
+
},
|
|
507
|
+
"Ge": {
|
|
508
|
+
"Eg": 0.67, # eV
|
|
509
|
+
"VBM_bulk": 0.00, # eV — will be shifted relative to Si
|
|
510
|
+
"BPE_bulk": -4.00, # eV
|
|
511
|
+
},
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
# Potential lineup from interface calculation (VBM_Ge - VBM_Si from slab)
|
|
515
|
+
delta_V = 0.74 # eV — valence band offset from DFT interface calc
|
|
516
|
+
|
|
517
|
+
# ── Compute band offsets ───────────────────────────────────────────────────────
|
|
518
|
+
VBO = delta_V # valence band offset
|
|
519
|
+
CBO = (materials["Ge"]["Eg"] - materials["Si"]["Eg"]) - VBO # conduction band offset
|
|
520
|
+
|
|
521
|
+
print("=" * 50)
|
|
522
|
+
print("Band alignment: Si / Ge heterointerface")
|
|
523
|
+
print("=" * 50)
|
|
524
|
+
print(f" Valence band offset (VBO) : {VBO:+.3f} eV")
|
|
525
|
+
print(f" Conduction band offset (CBO) : {CBO:+.3f} eV")
|
|
526
|
+
|
|
527
|
+
if VBO * CBO > 0:
|
|
528
|
+
btype = "Type-I (straddling gap)"
|
|
529
|
+
elif VBO * CBO < 0:
|
|
530
|
+
btype = "Type-II (staggered gap)"
|
|
531
|
+
else:
|
|
532
|
+
btype = "Type-III (broken gap)"
|
|
533
|
+
print(f" Band alignment type : {btype}")
|
|
534
|
+
|
|
535
|
+
print()
|
|
536
|
+
print("Energy level diagram (eV, Si VBM = 0.00):")
|
|
537
|
+
print(f" Si VBM = 0.00 Si CBM = {materials['Si']['Eg']:.2f}")
|
|
538
|
+
print(f" Ge VBM = {VBO:+.2f} Ge CBM = {VBO + materials['Ge']['Eg']:.2f}")
|
|
539
|
+
'''
|
|
540
|
+
|
|
541
|
+
SCRIPTS: list[dict] = [
|
|
542
|
+
{
|
|
543
|
+
"title": "Bulk Structure Download Script",
|
|
544
|
+
"entry_type": EntryType.capability,
|
|
545
|
+
"tags": ["pymatgen", "mp-api", "data-retrieval", "python"],
|
|
546
|
+
"aliases": ["download_bulk_structure.py"],
|
|
547
|
+
"content": _BULK_DOWNLOAD_CODE,
|
|
548
|
+
"metadata": EntryMetadata(
|
|
549
|
+
source_provenance="https://api.materialsproject.org/",
|
|
550
|
+
refinement_status=RefinementStatus.validated,
|
|
551
|
+
verification_status=VerificationStatus.self_tested,
|
|
552
|
+
script_language="python",
|
|
553
|
+
script_requirements=["pymatgen", "mp-api"],
|
|
554
|
+
script_filename="download_bulk_structure.py",
|
|
555
|
+
),
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
"title": "Substrate Analysis Script",
|
|
559
|
+
"entry_type": EntryType.capability,
|
|
560
|
+
"tags": ["pymatgen", "lattice-matching", "substrate-analysis", "python"],
|
|
561
|
+
"aliases": ["substrate_analysis.py"],
|
|
562
|
+
"content": _SUBSTRATE_ANALYSIS_CODE,
|
|
563
|
+
"metadata": EntryMetadata(
|
|
564
|
+
source_provenance="https://pymatgen.org/pymatgen.analysis.interfaces.html",
|
|
565
|
+
refinement_status=RefinementStatus.validated,
|
|
566
|
+
verification_status=VerificationStatus.self_tested,
|
|
567
|
+
script_language="python",
|
|
568
|
+
script_requirements=["pymatgen"],
|
|
569
|
+
script_filename="substrate_analysis.py",
|
|
570
|
+
),
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
"title": "Slab Generation Script",
|
|
574
|
+
"entry_type": EntryType.capability,
|
|
575
|
+
"tags": ["pymatgen", "slab-generation", "surface", "python"],
|
|
576
|
+
"aliases": ["slab_generation.py"],
|
|
577
|
+
"content": _SLAB_GENERATION_CODE,
|
|
578
|
+
"metadata": EntryMetadata(
|
|
579
|
+
source_provenance="https://pymatgen.org/pymatgen.core.surface.html",
|
|
580
|
+
refinement_status=RefinementStatus.validated,
|
|
581
|
+
verification_status=VerificationStatus.self_tested,
|
|
582
|
+
script_language="python",
|
|
583
|
+
script_requirements=["pymatgen"],
|
|
584
|
+
script_filename="slab_generation.py",
|
|
585
|
+
),
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
"title": "Interface Builder Script",
|
|
589
|
+
"entry_type": EntryType.capability,
|
|
590
|
+
"tags": ["pymatgen", "interface-builder", "coherent-interface", "python"],
|
|
591
|
+
"aliases": ["interface_builder.py"],
|
|
592
|
+
"content": _INTERFACE_BUILDER_CODE,
|
|
593
|
+
"metadata": EntryMetadata(
|
|
594
|
+
source_provenance="https://pymatgen.org/pymatgen.analysis.interfaces.html",
|
|
595
|
+
refinement_status=RefinementStatus.validated,
|
|
596
|
+
verification_status=VerificationStatus.self_tested,
|
|
597
|
+
script_language="python",
|
|
598
|
+
script_requirements=["pymatgen"],
|
|
599
|
+
script_filename="interface_builder.py",
|
|
600
|
+
),
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
"title": "Interface Energy Calculation Script",
|
|
604
|
+
"entry_type": EntryType.capability,
|
|
605
|
+
"tags": ["interface-energy", "thermodynamics", "python", "numpy"],
|
|
606
|
+
"aliases": ["interface_energy.py"],
|
|
607
|
+
"content": _INTERFACE_ENERGY_CODE,
|
|
608
|
+
"metadata": EntryMetadata(
|
|
609
|
+
refinement_status=RefinementStatus.validated,
|
|
610
|
+
verification_status=VerificationStatus.self_tested,
|
|
611
|
+
script_language="python",
|
|
612
|
+
script_requirements=["numpy"],
|
|
613
|
+
script_filename="interface_energy.py",
|
|
614
|
+
),
|
|
615
|
+
},
|
|
616
|
+
{
|
|
617
|
+
"title": "Band Alignment Calculation Script",
|
|
618
|
+
"entry_type": EntryType.capability,
|
|
619
|
+
"tags": ["band-alignment", "band-offset", "heterojunction", "python", "numpy"],
|
|
620
|
+
"aliases": ["band_alignment.py"],
|
|
621
|
+
"content": _BAND_ALIGNMENT_CODE,
|
|
622
|
+
"metadata": EntryMetadata(
|
|
623
|
+
refinement_status=RefinementStatus.validated,
|
|
624
|
+
verification_status=VerificationStatus.self_tested,
|
|
625
|
+
script_language="python",
|
|
626
|
+
script_requirements=["numpy"],
|
|
627
|
+
script_filename="band_alignment.py",
|
|
628
|
+
),
|
|
629
|
+
},
|
|
630
|
+
]
|
|
631
|
+
|
|
632
|
+
# ---------------------------------------------------------------------------
|
|
633
|
+
# 6. Capability entries
|
|
634
|
+
# ---------------------------------------------------------------------------
|
|
635
|
+
|
|
636
|
+
CAPABILITIES: list[dict] = [
|
|
637
|
+
{
|
|
638
|
+
"title": "Lattice Matching via ZSL Algorithm",
|
|
639
|
+
"entry_type": EntryType.capability,
|
|
640
|
+
"tags": ["lattice-matching", "substrate-analysis", "pymatgen", "zsl"],
|
|
641
|
+
"aliases": ["ZSL lattice matching", "Zur-McGill lattice matching"],
|
|
642
|
+
"content": """\
|
|
643
|
+
## Lattice Matching via ZSL Algorithm
|
|
644
|
+
|
|
645
|
+
[[pymatgen]] `SubstrateAnalyzer` implements the Zur & McGill (ZSL) algorithm to find
|
|
646
|
+
supercell coincidence lattices between two crystal surfaces that minimise area and mismatch.
|
|
647
|
+
|
|
648
|
+
### Relevant script
|
|
649
|
+
Run [[Substrate Analysis Script]] to execute a lattice matching analysis.
|
|
650
|
+
|
|
651
|
+
### Output
|
|
652
|
+
- Substrate and film Miller indices
|
|
653
|
+
- Linear mismatch (%)
|
|
654
|
+
- Elastic strain energy density
|
|
655
|
+
- Coincidence supercell vectors
|
|
656
|
+
|
|
657
|
+
### Reference
|
|
658
|
+
Zur & McGill, J. Appl. Phys. 55, 378 (1984).
|
|
659
|
+
""",
|
|
660
|
+
"metadata": EntryMetadata(
|
|
661
|
+
source_provenance="https://doi.org/10.1063/1.333084",
|
|
662
|
+
refinement_status=RefinementStatus.linked,
|
|
663
|
+
),
|
|
664
|
+
},
|
|
665
|
+
{
|
|
666
|
+
"title": "Coherent Interface Construction",
|
|
667
|
+
"entry_type": EntryType.capability,
|
|
668
|
+
"tags": ["interface-construction", "pymatgen", "coherent-interface"],
|
|
669
|
+
"aliases": ["CoherentInterfaceBuilder"],
|
|
670
|
+
"content": """\
|
|
671
|
+
## Coherent Interface Construction
|
|
672
|
+
|
|
673
|
+
[[pymatgen]] `CoherentInterfaceBuilder` automates the stacking of two slab models into
|
|
674
|
+
a periodic interface supercell with a controllable vacuum region.
|
|
675
|
+
|
|
676
|
+
### Relevant script
|
|
677
|
+
Run [[Interface Builder Script]] to construct an interface supercell.
|
|
678
|
+
|
|
679
|
+
### Key parameters
|
|
680
|
+
- `gap` — spacing between slab surfaces at the interface (Å)
|
|
681
|
+
- `vacuum_over_film` — vacuum layer above the film slab (Å)
|
|
682
|
+
- `film_thickness` / `substrate_thickness` — in layers or Å
|
|
683
|
+
- `termination` — choose from available slab terminations
|
|
684
|
+
|
|
685
|
+
### Reference
|
|
686
|
+
Mathew et al., Comput. Mater. Sci. 152, 60 (2018).
|
|
687
|
+
""",
|
|
688
|
+
"metadata": EntryMetadata(
|
|
689
|
+
source_provenance="https://doi.org/10.1016/j.commatsci.2018.05.018",
|
|
690
|
+
refinement_status=RefinementStatus.linked,
|
|
691
|
+
),
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
"title": "Slab Surface Generation",
|
|
695
|
+
"entry_type": EntryType.capability,
|
|
696
|
+
"tags": ["slab-generation", "surface-science", "pymatgen"],
|
|
697
|
+
"content": """\
|
|
698
|
+
## Slab Surface Generation
|
|
699
|
+
|
|
700
|
+
[[pymatgen]] `SlabGenerator` creates slab models from bulk crystals for any Miller index,
|
|
701
|
+
with controllable thickness, vacuum, and termination symmetry.
|
|
702
|
+
|
|
703
|
+
### Relevant script
|
|
704
|
+
Run [[Slab Generation Script]] to generate slab models.
|
|
705
|
+
|
|
706
|
+
### Key options
|
|
707
|
+
- `symmetrize=True` — force symmetric top/bottom terminations
|
|
708
|
+
- `max_normal_search` — controls accuracy for non-orthogonal surface cells
|
|
709
|
+
- `in_unit_planes=True` — thickness in unit cells rather than Å
|
|
710
|
+
""",
|
|
711
|
+
"metadata": EntryMetadata(
|
|
712
|
+
source_provenance="https://pymatgen.org/pymatgen.core.surface.html",
|
|
713
|
+
refinement_status=RefinementStatus.linked,
|
|
714
|
+
),
|
|
715
|
+
},
|
|
716
|
+
]
|
|
717
|
+
|
|
718
|
+
# ---------------------------------------------------------------------------
|
|
719
|
+
# Edge wiring definitions
|
|
720
|
+
# ---------------------------------------------------------------------------
|
|
721
|
+
|
|
722
|
+
# Each tuple: (source_title, target_title, relation)
|
|
723
|
+
EDGE_WIRING: list[tuple[str, str, EdgeRelation]] = [
|
|
724
|
+
# Scripts implement generalised capabilities
|
|
725
|
+
("Substrate Analysis Script", "Lattice Matching via ZSL Algorithm", EdgeRelation.implements),
|
|
726
|
+
("Substrate Analysis Script", "Build Material Interface via Slab Stacking", EdgeRelation.implements),
|
|
727
|
+
("Slab Generation Script", "Slab Surface Generation", EdgeRelation.implements),
|
|
728
|
+
("Slab Generation Script", "Build Material Interface via Slab Stacking", EdgeRelation.implements),
|
|
729
|
+
("Interface Builder Script", "Coherent Interface Construction", EdgeRelation.implements),
|
|
730
|
+
("Interface Builder Script", "Build Material Interface via Slab Stacking", EdgeRelation.implements),
|
|
731
|
+
("Interface Energy Calculation Script","Build Material Interface via Slab Stacking", EdgeRelation.documents),
|
|
732
|
+
("Band Alignment Calculation Script", "Build Material Interface via Slab Stacking", EdgeRelation.documents),
|
|
733
|
+
("Bulk Structure Download Script", "Download Bulk Structure from Materials Project", EdgeRelation.implements),
|
|
734
|
+
# Scripts use dependencies
|
|
735
|
+
("Substrate Analysis Script", "pymatgen", EdgeRelation.uses),
|
|
736
|
+
("Slab Generation Script", "pymatgen", EdgeRelation.uses),
|
|
737
|
+
("Interface Builder Script", "pymatgen", EdgeRelation.uses),
|
|
738
|
+
("Interface Energy Calculation Script","pymatgen", EdgeRelation.uses),
|
|
739
|
+
("Band Alignment Calculation Script", "pymatgen", EdgeRelation.uses),
|
|
740
|
+
("Bulk Structure Download Script", "pymatgen", EdgeRelation.uses),
|
|
741
|
+
("Bulk Structure Download Script", "mp-api", EdgeRelation.uses),
|
|
742
|
+
# Procedure uses generalised capabilities
|
|
743
|
+
("Build Material Interface via Slab Stacking", "Lattice Matching via ZSL Algorithm", EdgeRelation.execution_pathway),
|
|
744
|
+
("Build Material Interface via Slab Stacking", "Slab Surface Generation", EdgeRelation.execution_pathway),
|
|
745
|
+
("Build Material Interface via Slab Stacking", "Coherent Interface Construction", EdgeRelation.execution_pathway),
|
|
746
|
+
# Download procedure uses mp-api
|
|
747
|
+
("Download Bulk Structure from Materials Project", "mp-api", EdgeRelation.dependency),
|
|
748
|
+
]
|
|
749
|
+
|
|
750
|
+
# ---------------------------------------------------------------------------
|
|
751
|
+
# Seeder
|
|
752
|
+
# ---------------------------------------------------------------------------
|
|
753
|
+
|
|
754
|
+
def seed() -> None:
|
|
755
|
+
init_db()
|
|
756
|
+
all_data = DEPS + PROCEDURES + CAPABILITIES + SCRIPTS
|
|
757
|
+
|
|
758
|
+
title_to_id: dict[str, str] = {}
|
|
759
|
+
|
|
760
|
+
with SessionLocal() as db:
|
|
761
|
+
repo = EntryRepository(db)
|
|
762
|
+
existing = {e.title: e.id for e in repo.get_all()}
|
|
763
|
+
title_to_id.update(existing)
|
|
764
|
+
|
|
765
|
+
for data in all_data:
|
|
766
|
+
title = data["title"]
|
|
767
|
+
if title in existing:
|
|
768
|
+
print(f" skip (exists): {title}")
|
|
769
|
+
title_to_id[title] = existing[title]
|
|
770
|
+
continue
|
|
771
|
+
entry = Entry(**data)
|
|
772
|
+
saved = repo.create(entry)
|
|
773
|
+
app_state.graph.add_entry(saved)
|
|
774
|
+
title_to_id[saved.title] = saved.id
|
|
775
|
+
print(f" + {saved.title} [{saved.entry_type.value}]")
|
|
776
|
+
|
|
777
|
+
# Wire edges
|
|
778
|
+
edges_created = 0
|
|
779
|
+
with SessionLocal() as db:
|
|
780
|
+
edge_repo = EdgeRepository(db)
|
|
781
|
+
# Check existing edges to avoid dupes
|
|
782
|
+
from core.storage.models import EdgeModel
|
|
783
|
+
existing_pairs = {
|
|
784
|
+
(e.source_id, e.target_id) for e in db.query(EdgeModel).all()
|
|
785
|
+
}
|
|
786
|
+
for src_title, tgt_title, relation in EDGE_WIRING:
|
|
787
|
+
src_id = title_to_id.get(src_title)
|
|
788
|
+
tgt_id = title_to_id.get(tgt_title)
|
|
789
|
+
if not src_id or not tgt_id:
|
|
790
|
+
print(f" WARN: skipping edge {src_title!r} → {tgt_title!r} (missing node)")
|
|
791
|
+
continue
|
|
792
|
+
if (src_id, tgt_id) in existing_pairs:
|
|
793
|
+
continue
|
|
794
|
+
edge = Edge(source_id=src_id, target_id=tgt_id, relation=relation)
|
|
795
|
+
saved_edge = edge_repo.create(edge)
|
|
796
|
+
app_state.graph.add_edge(saved_edge)
|
|
797
|
+
existing_pairs.add((src_id, tgt_id))
|
|
798
|
+
edges_created += 1
|
|
799
|
+
|
|
800
|
+
print(f"\nEdges created: {edges_created}")
|
|
801
|
+
|
|
802
|
+
# Resolve wikilinks
|
|
803
|
+
from agents.extraction_agent.agent import ExtractionAgent
|
|
804
|
+
agent = ExtractionAgent(app_state.graph)
|
|
805
|
+
wl_count = agent.resolve_wikilinks()
|
|
806
|
+
print(f"Resolved {wl_count} wikilink edge(s)")
|
|
807
|
+
print("\nPymatgen interface entries seeded successfully.")
|
|
808
|
+
|
|
809
|
+
|
|
810
|
+
if __name__ == "__main__":
|
|
811
|
+
seed()
|