@@ -106,15 +106,116 @@ def make(
106
106
dir2 = relax2 .output .dir_name
107
107
struct1 = relax1 .output .structure
108
108
struct2 = relax2 .output .structure
109
+ add_info1 = {"relaxed_uuid" : relax1 .uuid , "distorted_uuid" : relax2 .uuid }
110
+ add_info2 = {"relaxed_uuid" : relax2 .uuid , "distorted_uuid" : relax1 .uuid }
109
111
112
+ deformations1 , deformations2 , ccd_job = self .get_deformation_and_ccd_jobs (
113
+ struct1 , struct2 , dir1 , dir2 , add_info1 , add_info2
114
+ )
115
+
116
+ return Flow (
117
+ jobs = [
118
+ charged_structures ,
119
+ relax1 ,
120
+ relax2 ,
121
+ deformations1 ,
122
+ deformations2 ,
123
+ ccd_job ,
124
+ ],
125
+ output = ccd_job .output ,
126
+ name = name ,
127
+ )
128
+
129
+ def make_from_relaxed_structures (
130
+ self ,
131
+ structure1 : Structure ,
132
+ structure2 : Structure ,
133
+ ) -> Flow :
134
+ """
135
+ Make a job for the calculation of the configuration coordinate diagram.
136
+
137
+ Parameters
138
+ ----------
139
+ structure1
140
+ The relaxed structure for charge state 1.
141
+ structure2
142
+ The relaxed structure for charge state 2.
143
+
144
+ Returns
145
+ -------
146
+ Flow
147
+ The full workflow for the calculation of the configuration coordinate
148
+ diagram.
149
+ """
150
+ # use a more descriptive name when possible
151
+ if not isinstance (structure1 , OutputReference ):
152
+ name = f"{ self .name } : { structure1 .formula } "
153
+ if not (
154
+ isinstance (structure1 , OutputReference )
155
+ or isinstance (structure2 , OutputReference )
156
+ ):
157
+ name = (
158
+ f"{ self .name } : { structure1 .formula } "
159
+ "({structure1.charge}-{structure2.charge})"
160
+ )
161
+
162
+ deformations1 , deformations2 , ccd_job = self .get_deformation_and_ccd_jobs (
163
+ structure1 , structure2
164
+ )
165
+
166
+ return Flow (
167
+ jobs = [
168
+ deformations1 ,
169
+ deformations2 ,
170
+ ccd_job ,
171
+ ],
172
+ output = ccd_job .output ,
173
+ name = name ,
174
+ )
175
+
176
+ def get_deformation_and_ccd_jobs (
177
+ self ,
178
+ struct1 : Structure ,
179
+ struct2 : Structure ,
180
+ dir1 : str | None = None ,
181
+ dir2 : str | None = None ,
182
+ add_info1 : dict | None = None ,
183
+ add_info2 : dict | None = None ,
184
+ ) -> tuple [Job , Job , Job ]:
185
+ """Get the deformation and CCD jobs for the given structures.
186
+
187
+ Parameters
188
+ ----------
189
+ struct1: Structure
190
+ The first structure.
191
+ struct2: Structure
192
+ The second structure.
193
+ dir1: str
194
+ The directory of the first structure.
195
+ dir2: str
196
+ The directory of the second structure.
197
+ add_info1: dict
198
+ Additional information to write
199
+ add_info2: dict
200
+ Additional information to write
201
+
202
+ Returns
203
+ -------
204
+ deformations1: Job
205
+ The deformation job for the first structure.
206
+ deformations2: Job
207
+ The deformation job for the second structure.
208
+ ccd_job: Job
209
+ The Job to construct the CCD document.
210
+ """
110
211
deformations1 = spawn_energy_curve_calcs (
111
212
struct1 ,
112
213
struct2 ,
113
214
distortions = self .distortions ,
114
215
static_maker = self .static_maker ,
115
216
prev_dir = dir1 ,
116
217
add_name = "q1" ,
117
- add_info = { "relaxed_uuid" : relax1 . uuid , "distorted_uuid" : relax2 . uuid } ,
218
+ add_info = add_info1 ,
118
219
)
119
220
120
221
deformations2 = spawn_energy_curve_calcs (
@@ -124,7 +225,7 @@ def make(
124
225
static_maker = self .static_maker ,
125
226
prev_dir = dir2 ,
126
227
add_name = "q2" ,
127
- add_info = { "relaxed_uuid" : relax2 . uuid , "distorted_uuid" : relax1 . uuid } ,
228
+ add_info = add_info2 ,
128
229
)
129
230
130
231
deformations1 .append_name (" q1" )
@@ -139,18 +240,7 @@ def make(
139
240
deformations1 .output , deformations2 .output , undistorted_index = min_abs_index
140
241
)
141
242
142
- return Flow (
143
- jobs = [
144
- charged_structures ,
145
- relax1 ,
146
- relax2 ,
147
- deformations1 ,
148
- deformations2 ,
149
- ccd_job ,
150
- ],
151
- output = ccd_job .output ,
152
- name = name ,
153
- )
243
+ return deformations1 , deformations2 , ccd_job
154
244
155
245
156
246
@dataclass
@@ -161,6 +251,15 @@ class FormationEnergyMaker(Maker, ABC):
161
251
this maker is the `defect_relax_maker` which contains the settings for the atomic
162
252
relaxations that each defect supercell will undergo.
163
253
254
+ This maker can be used as a stand-alone maker to calculate all of the data
255
+ needed to populate the `DefectEntry` object. However, for you can also use this
256
+ maker with `uc_bulk` set to True (also set `collect_defect_entry_data` to False
257
+ and `bulk_relax_maker` to None). This will skip the bulk supercell calculations
258
+ assuming that bulk unit cell calculations are of high enough quality to be used
259
+ directly. In these cases, the bulk SC electrostatic potentials need to be
260
+ constructed without running a separate bulk SC calculation. This is currently
261
+ implemented through the grid re-sampling tools in `mp-pyrho`.
262
+
164
263
Attributes
165
264
----------
166
265
defect_relax_maker: Maker
@@ -189,6 +288,10 @@ class FormationEnergyMaker(Maker, ABC):
189
288
ng_settings = dict(zip(params, ng + ngf))
190
289
relax_maker = update_user_incar_settings(relax_maker, ng_settings)
191
290
291
+ uc_bulk: bool
292
+ If True, skip the bulk supercell calculation and only perform the defect
293
+ supercell calculations. This is useful for large-scale defect databases.
294
+
192
295
name: str
193
296
The name of the flow created by this maker.
194
297
@@ -251,6 +354,7 @@ class FormationEnergyMaker(Maker, ABC):
251
354
252
355
defect_relax_maker : Maker
253
356
bulk_relax_maker : Maker | None = None
357
+ uc_bulk : bool = False
254
358
name : str = "formation energy"
255
359
relax_radius : float | str | None = None
256
360
perturb : float | None = None
@@ -260,8 +364,15 @@ class FormationEnergyMaker(Maker, ABC):
260
364
def __post_init__ (self ) -> None :
261
365
"""Apply post init updates."""
262
366
self .validate_maker ()
263
- if self .bulk_relax_maker is None :
264
- self .bulk_relax_maker = self .defect_relax_maker
367
+ if self .uc_bulk :
368
+ if self .bulk_relax_maker is not None :
369
+ raise ValueError ("bulk_relax_maker should be None when uc_bulk is True" )
370
+ if self .collect_defect_entry_data :
371
+ raise ValueError (
372
+ "collect_defect_entry_data should be False when uc_bulk is True"
373
+ )
374
+ else :
375
+ self .bulk_relax_maker = self .bulk_relax_maker or self .defect_relax_maker
265
376
266
377
def make (
267
378
self ,
@@ -296,27 +407,41 @@ def make(
296
407
The workflow to calculate the formation energy diagram.
297
408
"""
298
409
jobs = []
299
- if bulk_supercell_dir is None :
300
- get_sc_job = bulk_supercell_calculation (
301
- uc_structure = defect .structure ,
302
- relax_maker = self .bulk_relax_maker ,
303
- sc_mat = supercell_matrix ,
304
- get_planar_locpot = self .get_planar_locpot ,
305
- )
306
- sc_mat = get_sc_job .output ["sc_mat" ]
307
- lattice = get_sc_job .output ["sc_struct" ].lattice
308
- bulk_supercell_dir = get_sc_job .output ["dir_name" ]
410
+ if not self .uc_bulk :
411
+ if bulk_supercell_dir is None :
412
+ get_sc_job = bulk_supercell_calculation (
413
+ uc_structure = defect .structure ,
414
+ relax_maker = self .bulk_relax_maker ,
415
+ sc_mat = supercell_matrix ,
416
+ get_planar_locpot = self .get_planar_locpot ,
417
+ )
418
+ sc_mat = get_sc_job .output ["sc_mat" ]
419
+ lattice = get_sc_job .output ["sc_struct" ].lattice
420
+ bulk_supercell_dir = get_sc_job .output ["dir_name" ]
421
+ sc_uuid = get_sc_job .output ["uuid" ]
422
+ else :
423
+ # all additional reader functions need to be in this job
424
+ # b/c they might receive Response objects instead of data.
425
+ get_sc_job = get_supercell_from_prv_calc (
426
+ uc_structure = defect .structure ,
427
+ prv_calc_dir = bulk_supercell_dir ,
428
+ sc_entry_and_locpot_from_prv = self .sc_entry_and_locpot_from_prv ,
429
+ sc_mat_ref = supercell_matrix ,
430
+ )
431
+ sc_mat = get_sc_job .output ["sc_mat" ]
432
+ lattice = get_sc_job .output ["lattice" ]
433
+ sc_uuid = get_sc_job .output ["uuid" ]
434
+ jobs .append (get_sc_job )
309
435
else :
310
- # all additional reader functions need to be in this job
311
- # b/c they might receive Response objects instead of data.
312
- get_sc_job = get_supercell_from_prv_calc (
313
- uc_structure = defect .structure ,
314
- prv_calc_dir = bulk_supercell_dir ,
315
- sc_entry_and_locpot_from_prv = self .sc_entry_and_locpot_from_prv ,
316
- sc_mat_ref = supercell_matrix ,
317
- )
318
- sc_mat = get_sc_job .output ["sc_mat" ]
319
- lattice = get_sc_job .output ["lattice" ]
436
+ if bulk_supercell_dir is not None :
437
+ raise ValueError (
438
+ "bulk_supercell_dir should be None when uc_bulk is True."
439
+ "We will be using a uc bulk calculation, so no bulk supercell "
440
+ "is needed."
441
+ )
442
+ sc_mat = supercell_matrix
443
+ lattice = None
444
+ sc_uuid = None
320
445
321
446
spawn_output = spawn_defect_q_jobs (
322
447
defect = defect ,
@@ -327,13 +452,26 @@ def make(
327
452
add_info = {
328
453
"bulk_supercell_dir" : bulk_supercell_dir ,
329
454
"bulk_supercell_matrix" : sc_mat ,
330
- "bulk_supercell_uuid" : get_sc_job . uuid ,
455
+ "bulk_supercell_uuid" : sc_uuid ,
331
456
},
332
457
relax_radius = self .relax_radius ,
333
458
perturb = self .perturb ,
334
459
validate_charge = self .validate_charge ,
335
460
)
336
- jobs .extend ([get_sc_job , spawn_output ])
461
+
462
+ if self .uc_bulk :
463
+ # run the function here so we can get the charge state
464
+ # calculations ASAP
465
+ response = spawn_output .function (
466
+ * spawn_output .function_args , ** spawn_output .function_kwargs
467
+ )
468
+ jobs .append (response .replace )
469
+ output_ = response .output
470
+ else :
471
+ # execute this as job so you can string a single bulk sc with multiple
472
+ # defect scs
473
+ jobs .append (spawn_output )
474
+ output_ = spawn_output .output
337
475
338
476
if self .collect_defect_entry_data :
339
477
collection_job = get_defect_entry (
@@ -344,7 +482,7 @@ def make(
344
482
345
483
return Flow (
346
484
jobs = jobs ,
347
- output = spawn_output . output ,
485
+ output = output_ ,
348
486
name = self .name ,
349
487
)
350
488
0 commit comments