Skip to content

Commit 109ca17

Browse files
Merge pull request #37 from AsymmetryChou/clean_input
Clean up and standardize input JSON format
2 parents 57f1493 + 09d8c99 commit 109ca17

15 files changed

Lines changed: 4096 additions & 2768 deletions

File tree

docs/input_params/common_options.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
========================================
1+
==============
22
Common Options
3-
========================================
3+
==============
4+
45
.. _`common_options`:
56

67
common_options:
@@ -37,7 +38,7 @@ common_options:
3738
| type: ``str``, optional, default: ``float32``
3839
| argument path: ``common_options/dtype``
3940
40-
The digital number's precison, choose among:
41+
The digital number's precison, choose among:
4142
Default: `float32`
4243
- `float32`: indicating torch.float32
4344
- `float64`: indicating torch.float64

docs/input_params/run_options.rst

Lines changed: 3134 additions & 791 deletions
Large diffs are not rendered by default.

dpnegf/entrypoints/run.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ def run(
5454
jdata = j_loader(INPUT)
5555
jdata = normalize_run(jdata)
5656

57+
if output:
58+
with open(os.path.join(str(output), "run_config.json"), "w") as fp:
59+
json.dump(jdata, fp, indent=4)
60+
5761
task_options = j_must_have(jdata, "task_options")
5862
task = task_options["task"]
5963
use_gui = jdata.get("use_gui", False)

dpnegf/negf/lead_property.py

Lines changed: 50 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -523,32 +523,33 @@ def _estimate_worker_memory(lead_L, lead_R, kpoint=None, temp_allocation_factor=
523523
return total_estimate
524524

525525

526-
def _get_safe_n_jobs(lead_L, lead_R, requested_n_jobs=-1, max_memory_fraction=0.9,
527-
min_workers=1, kpoint=None, n_cpus=None):
526+
def _get_safe_n_workers(lead_L, lead_R, requested_n_workers=-1, max_memory_fraction=0.9,
527+
min_workers=1, kpoint=None, cpu_budget=None):
528528
"""
529529
Calculate safe number of parallel workers based on available system memory.
530530
531531
Parameters
532532
----------
533533
lead_L, lead_R : LeadProperty
534534
Lead objects for memory estimation.
535-
requested_n_jobs : int
536-
User-requested n_jobs. -1 means auto-detect.
535+
requested_n_workers : int
536+
User-requested n_workers. -1 means auto-detect.
537537
max_memory_fraction : float
538538
Maximum fraction of available memory to use. Default 0.9.
539539
min_workers : int
540540
Minimum number of workers to use. Default 1.
541541
kpoint : array-like, optional
542542
A sample k-point for fetching Hamiltonian matrices to estimate memory.
543-
n_cpus : int or None
544-
Number of CPU cores to use for memory estimation. If None, uses os.cpu_count().
543+
cpu_budget : int or None
544+
Total CPU cores the self-energy pool is allowed to size against.
545+
If None, uses os.cpu_count().
545546
546547
Returns
547548
-------
548549
int
549550
Safe number of parallel workers.
550551
"""
551-
cpu_count = n_cpus if n_cpus is not None else os.cpu_count()
552+
cpu_count = cpu_budget if cpu_budget is not None else os.cpu_count()
552553
if cpu_count is None or cpu_count < 1:
553554
cpu_count = 1
554555
log.warning("os.cpu_count() returned None or invalid value. Defaulting to 1 CPU core.")
@@ -570,37 +571,37 @@ def _get_safe_n_jobs(lead_L, lead_R, requested_n_jobs=-1, max_memory_fraction=0.
570571
max_workers = min(max_workers_by_memory, cpu_count)
571572

572573
safe_n_worker = 0
573-
# check requested_n_jobs is a number
574-
if not isinstance(requested_n_jobs, int):
575-
log.warning(f"Requested n_jobs={requested_n_jobs} is not an integer. \n"
574+
# check requested_n_workers is a number
575+
if not isinstance(requested_n_workers, int):
576+
log.warning(f"Requested n_workers={requested_n_workers} is not an integer. \n"
576577
f"Using min_workers={min_workers}.")
577578
safe_n_worker = min_workers
578579

579-
if requested_n_jobs == -1:
580+
if requested_n_workers == -1:
580581
safe_n_worker = max_workers
581-
elif requested_n_jobs == 0:
582-
log.warning(f"Requested n_jobs=0 is invalid. Using min_workers={min_workers}.")
582+
elif requested_n_workers == 0:
583+
log.warning(f"Requested n_workers=0 is invalid. Using min_workers={min_workers}.")
583584
safe_n_worker = min_workers
584-
elif requested_n_jobs > 0:
585-
if requested_n_jobs > max_workers:
586-
log.warning(f"Requested n_jobs={requested_n_jobs} may exceed available memory. "
585+
elif requested_n_workers > 0:
586+
if requested_n_workers > max_workers:
587+
log.warning(f"Requested n_workers={requested_n_workers} may exceed available memory. "
587588
f"Limiting to {max_workers} workers "
588589
f"(available: {available_memory / 1e9:.1f} GB, "
589590
f"est. per worker: {memory_per_worker / 1e9:.1f} GB)")
590591
safe_n_worker = max_workers
591592
else:
592-
safe_n_worker = requested_n_jobs
593+
safe_n_worker = requested_n_workers
593594
else:
594-
# Negative values other than -1: joblib interprets as (cpu_count + 1 + n_jobs)
595-
effective_n_jobs = max(cpu_count + 1 + requested_n_jobs, min_workers)
596-
safe_n_worker = min(effective_n_jobs, max_workers)
595+
# Negative values other than -1: joblib interprets as (cpu_count + 1 + n_workers)
596+
effective_n_workers = max(cpu_count + 1 + requested_n_workers, min_workers)
597+
safe_n_worker = min(effective_n_workers, max_workers)
597598

598-
log.info(f"Estimated safe n_jobs={safe_n_worker} based on available memory.")
599+
log.info(f"Estimated safe n_workers={safe_n_worker} based on available memory.")
599600
return safe_n_worker
600601

601602

602603
def _autotune_blas_threads(leadL_pack, sample_kpoint, sample_energy, eta_lead,
603-
n_jobs, cpu_count, se_numba_jit, requested=None):
604+
n_workers, cpu_count, se_numba_jit, requested=None):
604605
"""Pick per-worker BLAS threads by timing the real surface-green code path.
605606
606607
The Lopez-Sancho loop in ``surface_green._surface_green_{numba,scipy}_core``
@@ -624,7 +625,7 @@ def _autotune_blas_threads(leadL_pack, sample_kpoint, sample_energy, eta_lead,
624625
computed so N and dtype match production.
625626
eta_lead : float
626627
Same broadening as production.
627-
n_jobs : int
628+
n_workers : int
628629
Number of joblib workers about to be launched; bounds per-worker CPU.
629630
cpu_count : int
630631
Total CPU budget.
@@ -640,8 +641,8 @@ def _autotune_blas_threads(leadL_pack, sample_kpoint, sample_energy, eta_lead,
640641
int
641642
BLAS threads to give each loky worker via ``threadpool_limits``.
642643
"""
643-
n_jobs = max(int(n_jobs), 1)
644-
per_worker_budget = max(int(cpu_count) // n_jobs, 1)
644+
n_workers = max(int(n_workers), 1)
645+
per_worker_budget = max(int(cpu_count) // n_workers, 1)
645646

646647
if requested is not None:
647648
if not isinstance(requested, int) or requested < 1:
@@ -650,7 +651,7 @@ def _autotune_blas_threads(leadL_pack, sample_kpoint, sample_energy, eta_lead,
650651
else:
651652
if requested > per_worker_budget:
652653
log.warning(f"Requested blas_threads={requested} exceeds per-worker "
653-
f"CPU budget ({per_worker_budget} = cpu_count // n_jobs). "
654+
f"CPU budget ({per_worker_budget} = cpu_count // n_workers). "
654655
f"Clamping to {per_worker_budget}.")
655656
threads = min(requested, per_worker_budget)
656657
log.info(f"BLAS threads per worker: {threads} (user-requested).")
@@ -707,7 +708,7 @@ def _autotune_blas_threads(leadL_pack, sample_kpoint, sample_energy, eta_lead,
707708

708709
best = min(timings, key=timings.get)
709710
log.info(f"BLAS threads per worker: {best} "
710-
f"(autotuned, N={sample_dim}, n_jobs={n_jobs}, cpu_count={cpu_count}).")
711+
f"(autotuned, N={sample_dim}, n_workers={n_workers}, cpu_count={cpu_count}).")
711712
return best
712713

713714

@@ -734,7 +735,7 @@ def _sample_principal_layer_dim(pack, sample_kpoint):
734735

735736
def compute_all_self_energy(eta, lead_L, lead_R, kpoints_grid, energy_grid,
736737
self_energy_save_path=None, ek_batch_size=200,
737-
n_cpus=None, n_jobs=-1, se_numba_jit=None, blas_threads=None):
738+
cpu_budget=None, n_workers=-1, se_numba_jit=None, blas_threads=None):
738739
"""
739740
Computes and saves self-energy matrices for all combinations of k-points and energy values
740741
for left and right leads.
@@ -758,17 +759,19 @@ def compute_all_self_energy(eta, lead_L, lead_R, kpoints_grid, energy_grid,
758759
Directory to save self-energy files. If None, uses lead_L's results_path.
759760
ek_batch_size : int, optional
760761
Number of (k, e) tasks per parallel batch. Default is 200.
761-
n_cpus : int or None, optional
762-
Number of CPU cores to use for memory estimation. If None, uses os.cpu_count().
763-
n_jobs : int, optional
764-
Number of parallel jobs to use. Default is -1 (use all available CPUs).
762+
cpu_budget : int or None, optional
763+
Total CPU cores the self-energy pool is allowed to size against.
764+
If None, uses os.cpu_count().
765+
n_workers : int, optional
766+
Number of parallel joblib workers to use. Default is -1 (auto-select
767+
based on `cpu_budget` and available memory).
765768
se_numba_jit : bool or None, optional
766769
Boolean flag controlling whether to use the Numba-accelerated surface Green's function core.
767770
If None, Numba will be used when available. Default is None.
768771
blas_threads : int or None, optional
769772
BLAS threads to give each worker. None (default) autotunes by timing
770-
`_compute_self_energy_from_pack` across a few candidate thread counts and picking the fastest.
771-
Pass an int to force that value; it is still clamped to `cpu_count // n_jobs` to avoid
773+
`_compute_self_energy_from_pack` across a few candidate thread counts and picking the fastest.
774+
Pass an int to force that value; it is still clamped to `cpu_budget // n_workers` to avoid
772775
oversubscription.
773776
774777
Returns
@@ -785,14 +788,14 @@ def compute_all_self_energy(eta, lead_L, lead_R, kpoints_grid, energy_grid,
785788
# Calculate safe number of workers based on available memory
786789
# Use first k-point for memory estimation
787790
sample_kpoint = kpoints_grid[0] if len(kpoints_grid) > 0 else None
788-
safe_n_jobs = _get_safe_n_jobs(lead_L, lead_R,
789-
requested_n_jobs=n_jobs,
790-
kpoint=sample_kpoint,
791-
n_cpus=n_cpus)
792-
if n_jobs == -1:
793-
log.info(f"Auto-detected safe n_jobs={safe_n_jobs} based on available memory")
794-
elif safe_n_jobs < n_jobs:
795-
log.info(f"Adjusted n_jobs from {n_jobs} to {safe_n_jobs} due to memory constraints")
791+
safe_n_workers = _get_safe_n_workers(lead_L, lead_R,
792+
requested_n_workers=n_workers,
793+
kpoint=sample_kpoint,
794+
cpu_budget=cpu_budget)
795+
if n_workers == -1:
796+
log.info(f"Auto-detected safe n_workers={safe_n_workers} based on available memory")
797+
elif safe_n_workers < n_workers:
798+
log.info(f"Adjusted n_workers from {n_workers} to {safe_n_workers} due to memory constraints")
796799

797800
# Precompute all k-dependent matrices in the parent so workers receive
798801
# only plain tensors (the hamiltonian holds a torch.jit.ScriptFunction-
@@ -803,19 +806,19 @@ def compute_all_self_energy(eta, lead_L, lead_R, kpoints_grid, energy_grid,
803806
# Choose BLAS threads-per-worker by autotuning on the real code path at the
804807
# sample (k, E). Cheaper than a hardware-agnostic table and always correct
805808
# for the actual N / BLAS backend the workers will run under.
806-
cpu_budget = n_cpus if n_cpus is not None else os.cpu_count()
809+
cpu_count = cpu_budget if cpu_budget is not None else os.cpu_count()
807810
sample_energy = energy_grid[0] if len(energy_grid) > 0 else 0.0
808811
blas_threads_per_worker = _autotune_blas_threads(
809812
leadL_pack, sample_kpoint, sample_energy, eta,
810-
safe_n_jobs, cpu_budget, se_numba_jit, requested=blas_threads,
813+
safe_n_workers, cpu_count, se_numba_jit, requested=blas_threads,
811814
)
812815

813816
total_tasks = [(k, e) for k in kpoints_grid for e in energy_grid]
814817
# Capture the parent's log level so loky workers (which start with a clean
815818
# logging state and the WARNING default) can match it when they reinit.
816819
parent_log_level = logging.getLogger().getEffectiveLevel()
817820
if len(total_tasks) <= ek_batch_size:
818-
Parallel(n_jobs=min(safe_n_jobs, len(total_tasks)), backend="loky")(
821+
Parallel(n_jobs=min(safe_n_workers, len(total_tasks)), backend="loky")(
819822
delayed(_self_energy_worker_blas)(k, e, eta, leadL_pack, leadR_pack,
820823
self_energy_save_path, se_numba_jit,
821824
parent_log_level, blas_threads_per_worker)
@@ -824,7 +827,7 @@ def compute_all_self_energy(eta, lead_L, lead_R, kpoints_grid, energy_grid,
824827
else:
825828
for i in range(0, len(total_tasks), ek_batch_size):
826829
batch = total_tasks[i:i+ek_batch_size]
827-
Parallel(n_jobs=min(safe_n_jobs, len(batch)), backend="loky")(
830+
Parallel(n_jobs=min(safe_n_workers, len(batch)), backend="loky")(
828831
delayed(_self_energy_worker_blas)(k, e, eta, leadL_pack, leadR_pack,
829832
self_energy_save_path, se_numba_jit,
830833
parent_log_level, blas_threads_per_worker)
@@ -1077,7 +1080,7 @@ def _self_energy_worker_blas(k, e, eta, leadL_pack, leadR_pack, self_energy_save
10771080
`solve` / `inv` / matmul calls whose payoff from BLAS threading depends on
10781081
the principal-layer dimension N: single-threaded already wins for small N,
10791082
but multi-threaded solve/GEMM helps for larger N when the CPU budget left
1080-
over from the memory-driven `n_jobs` cap is not zero.
1083+
over from the memory-driven `n_workers` cap is not zero.
10811084
10821085
`blas_threads` is chosen by `_autotune_blas_threads` in the parent and
10831086
passed in per call; default 1 preserves the historical behavior for any

0 commit comments

Comments
 (0)