Skip to content

Commit

Permalink
Add workflow for generating base and uq runs from a single template c…
Browse files Browse the repository at this point in the history
…onfig (#603)

* Add support for `-p` flag in `duqduq status`
* Add cli option `--output` to setup to set output directory
* Rename `--base` to `--no-sampling` in `duqduq create`
* Better output names for status and setup
  • Loading branch information
stefsmeets committed Apr 25, 2023
1 parent b142c76 commit 94dabec
Show file tree
Hide file tree
Showing 9 changed files with 56 additions and 26 deletions.
2 changes: 1 addition & 1 deletion src/duqtools/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ def cli_setup(**kwargs):
@click.option('--force',
is_flag=True,
help='Overwrite existing run directories and IDS data.')
@click.option('--base',
@click.option('--no-sampling',
is_flag=True,
help='Create base run (ignores `dimensions`/`sampler`).')
@common_options(*all_options)
Expand Down
6 changes: 3 additions & 3 deletions src/duqtools/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ def create_run(self, model: Run, *, force: bool = False):
def create(*,
force,
config,
base: bool,
no_sampling: bool = False,
absolute_dirpath: bool = False,
**kwargs):
"""Create input for jetto and IDS data structures.
Expand All @@ -239,15 +239,15 @@ def create(*,
Override protection if data and directories already exist.
config : Path
Config file location
base : bool
no_sampling : bool
If true, create base run by ignoring `sampler`/`dimensions`.
**kwargs
Unused.
"""
create_mgr = CreateManager()

ops_dict = create_mgr.generate_ops_dict(base_only=base)
ops_dict = create_mgr.generate_ops_dict(base_only=no_sampling)

runs = create_mgr.make_run_models(
ops_dict=ops_dict,
Expand Down
9 changes: 8 additions & 1 deletion src/duqtools/large_scale_validation/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def cli(**kwargs):
@click.option('--force',
is_flag=True,
help='Overwrite existing run config directories')
@click.option('-o', '--output', type=str, help='Output subdirectory')
@common_options(*logging_options, yes_option, dry_run_option)
def cli_setup(**kwargs):
"""Set up large scale validation."""
Expand Down Expand Up @@ -75,7 +76,7 @@ def cli_setup(**kwargs):
help=
'Only create data for configs where `template_data` matches a handle in this data.csv.'
)
@click.option('--base',
@click.option('--no-sampling',
is_flag=True,
help='Create base runs (ignores `dimensions`/`sampler`).')
@common_options(*logging_options, yes_option, dry_run_option)
Expand Down Expand Up @@ -139,6 +140,12 @@ def cli_submit(**kwargs):
@cli.command('status')
@click.option('--detailed', is_flag=True, help='Detailed info on progress')
@click.option('--progress', is_flag=True, help='Fancy progress bar')
@click.option(
'-p',
'--pattern',
type=str,
help=
'Show status only for runs in subdirectories matching this glob pattern.')
@common_options(*logging_options)
def cli_status(**kwargs):
"""Check status large scale validation runs."""
Expand Down
6 changes: 3 additions & 3 deletions src/duqtools/large_scale_validation/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
from ..utils import read_imas_handles_from_file, work_directory


def create(*, base: bool, input_file: str, pattern: str, **kwargs):
def create(*, no_sampling: bool, input_file: str, pattern: str, **kwargs):
"""Create runs for large scale validation.
Parameters
----------
base : bool
no_sampling : bool
If true, create base runs by ignoring `sampler`/`dimensions`.
input_file : str
Only create for configs where template_data matches a handle in the data.csv
Expand Down Expand Up @@ -39,5 +39,5 @@ def create(*, base: bool, input_file: str, pattern: str, **kwargs):
with work_directory(config_dir):
duqtools_create(config=config_file,
absolute_dirpath=True,
base=base,
no_sampling=no_sampling,
**kwargs)
11 changes: 7 additions & 4 deletions src/duqtools/large_scale_validation/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
from ..utils import read_imas_handles_from_file


def setup(*, template_file, input_file, force, **kwargs):
def setup(*, template_file, input_file, force: bool, output: str, **kwargs):
"""Setup large scale validation runs for template."""
if not input_file:
raise OSError('Input file not defined.')

handles = read_imas_handles_from_file(input_file)

substitute_templates(handles=handles,
template_file=template_file,
force=force)
substitute_templates(
handles=handles,
template_file=template_file,
force=force,
output=output,
)
12 changes: 8 additions & 4 deletions src/duqtools/large_scale_validation/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from ..status import Status, StatusError


def status(*, progress: bool, detailed: bool, **kwargs):
def status(*, progress: bool, detailed: bool, pattern: str, **kwargs):
"""Show status of nested runs.
Parameters
Expand All @@ -16,11 +16,15 @@ def status(*, progress: bool, detailed: bool, **kwargs):
Show progress bar.
detailed : bool
Show detailed progress for every job.
pattern : str
Show status only for subdirectories matching this glob pattern
"""
if pattern is None:
pattern = '**'

cwd = Path.cwd()

config_files = cwd.glob('**/duqtools.yaml')
config_files = cwd.glob(f'{pattern}/duqtools.yaml')

all_jobs: list[Job] = []

Expand All @@ -39,11 +43,11 @@ def status(*, progress: bool, detailed: bool, **kwargs):
jobs = [Job(run.dirname) for run in Locations(config_dir).runs]
all_jobs.extend(jobs)

name = config_file.parent.name
dirname = config_file.parent.relative_to(cwd)
tag = cfg.tag
status = ''.join(job.status_symbol for job in jobs)

click.echo(f'{name} ({tag}): {status}')
click.echo(f'{dirname} ({tag}): {status}')

click.echo()

Expand Down
4 changes: 2 additions & 2 deletions src/duqtools/large_scale_validation/submit.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ def submit(*, array, force, max_jobs, schedule, max_array_size: int,

jobs: list[Job] = []

for dir in dirs:
config_file = dir / 'duqtools.yaml'
for drc in dirs:
config_file = drc / 'duqtools.yaml'
cfg.parse_file(config_file)

if handles and (cfg.create.template_data not in handles):
Expand Down
4 changes: 4 additions & 0 deletions src/duqtools/models/_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ def to_imas_handle(self) -> ImasHandle:
def from_path(cls, path: Path):
return cls(shortname=path, dirname=path.resolve())

@property
def is_base(self):
return self.shortname == 'base'


class Runs(BaseModel):
__root__: list[Run] = []
Expand Down
28 changes: 20 additions & 8 deletions src/duqtools/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from collections import defaultdict
from pathlib import Path
from types import SimpleNamespace
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Any, Optional

import yaml
from jinja2 import Environment, FileSystemLoader
Expand Down Expand Up @@ -150,8 +150,14 @@ def __getattr__(self, key: str):
return value


def substitute_templates(*, handles: dict[str, ImasHandle], template_file: str,
force: bool):
def substitute_templates(
*,
handles: dict[str, ImasHandle],
template_file: str,
force: bool,
base: bool = False,
output: Optional[str] = None,
):
"""Handle template substitution.
Parameters
Expand All @@ -162,6 +168,8 @@ def substitute_templates(*, handles: dict[str, ImasHandle], template_file: str,
Path to template file.
force : bool
Overwrite files if set to true.
output : str
Output subdirectory.
"""
cwd = Path.cwd()

Expand All @@ -173,7 +181,7 @@ def substitute_templates(*, handles: dict[str, ImasHandle], template_file: str,
add_system_attrs = no_op # default to no-op

for name, handle in handles.items():
run = SimpleNamespace(name=name)
run = SimpleNamespace(name=name, output=output)

add_system_attrs(run=run, handle=handle)

Expand All @@ -183,11 +191,15 @@ def substitute_templates(*, handles: dict[str, ImasHandle], template_file: str,

Config.parse_raw(cfg) # make sure config is valid

out_drc = cwd / name
if output:
out_drc = cwd / output / name
else:
out_drc = cwd / name

if out_drc.exists() and not force:
op_queue.add_no_op(description='Directory exists',
extra_description=name)
op_queue.add_no_op(
description='Directory exists',
extra_description=f'{name} ({out_drc.relative_to(cwd)})')
op_queue.warning(description='Warning',
extra_description='Some targets already exist, '
'use --force to override')
Expand All @@ -200,7 +212,7 @@ def substitute_templates(*, handles: dict[str, ImasHandle], template_file: str,
'force': force
},
description='Setup run',
extra_description=name,
extra_description=f'{name} ({out_drc.relative_to(cwd)})',
)


Expand Down

0 comments on commit 94dabec

Please sign in to comment.