cape.cfdx.case: Case control module

This module contains templates for interacting with and executing individual cases. Since this is one of the most highly customized modules of the CAPE system, there are few functions here, and the functions that are present are mostly templates.

In general, the case module is used for actually running the CFD solver (and any additional binaries that may be required as part of the run process), and it contains other capabilities for renaming files and determining the settings for a particular casecntl. CAPE saves many settings for the CFD solver and archiving in a file called case.json within each case folder, which allows for the settings of one case to diverge from the other cases in the same run matrix.

Actual functionality is left to individual modules listed below.

  • cape.pycart.case

  • cape.pyfun.case

  • cape.pyover.case

class cape.cfdx.casecntl.CaseRunner(fdir=None)

Class to handle running of individual CAPE cases

Call:
>>> runner = CaseRunner(fdir=None)
Inputs:
fdir: {None} | str

Optional case folder (by default os.getcwd())

Outputs:
runner: CaseRunner

Controller to run one case of solver

archive(test: bool = False)

Run the --archive archiving action

Call:
>>> runner.archive(test=False)
Inputs:
runner: CaseRunner

Controller to run one case of solver

test: True | {False}

Option to log all actions but not actually copy/delete

Versions:
  • 2024-09-18 ``@ddalle`: v1.0

assert_not_running()

Check if a case is already running, raise exception if so

Call:
>>> runner.assert_not_running()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Versions:
  • 2023-06-02 @ddalle: v1.0

  • 2023-06-20 @ddalle: v1.1; instance method

  • 2024-06-16 @ddalle: v2.0; was check_running()

callf(cmdi: list, f: str | None = None, e: str | None = None, shell: bool = False) int

Execute a function and save returncode

Call:
>>> ierr = runner.callf(cmdi, f=None, e=None)
Inputs:
runner: CaseRunner

Controller to run one case of solver

f: {None} | str

Name of file to write STDOUT

e: {f} | str

Name of file to write STDERR

shell: True | {False}

Option to run subprocess in shell

Outputs:
ierr: int

Return code

Versions:
  • 2024-07-16 @ddalle: v1.0

  • 2024-08-03 @ddalle: v1.1; add log messages

check_complete() bool

Check if a case is complete (DONE)

Call:
>>> q = runner.check_complete()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Versions:
  • 2023-06-20 @ddalle: v1.0

  • 2023-07-08 @ddalle: v1.1; support STOP

check_error() bool

Check for other errors; rewrite for each solver

Call:
>>> q = runner.check_error()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
q: bool

Whether case appears to be an error

Versions:
  • 2023-06-20 @ddalle: v1.0

check_exit(ja: int) bool

Check if a case should exit for any reason

Reasons a case should exit include

  • The case is finished.

  • A CAPE-STOP-PHASE file was found.

  • A CAPE-STOP-ITER file was found.

  • The relevant StartNextPhase option is False.

  • The relevant RestartSamePhase option is False.

Call:
>>> q = runner.check_exit(ja)
Inputs:
runner: CaseRunner

Controller to run one case of solver

ja: int

Phase at beginning of run

Outputs:
q: True | False

Whether case should exit

Versions:
  • 2025-05-26 @ddalle: v1.0

check_running() bool

Check if a case is currently running

Call:
>>> q = runner.check_running()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
q: bool

Whether case appears to be running

Versions:
  • 2023-06-16 @ddalle: v1.0

clean(test: bool = False)

Run the --clean archiving action

Call:
>>> runner.clean(test=False)
Inputs:
runner: CaseRunner

Controller to run one case of solver

test: True | {False}

Option to log all actions but not actually copy/delete

Versions:
  • 2024-09-18 ``@ddalle`: v1.0

copy_file(src: str, dst: str, f: bool = False)

Copy a file and log results

Call:
>>> runner.copy_file(src, dst, f=False)
Inputs:
runner: CaseRunner

Controller to run one case of solver

src: str

Name of input file, before renaming

dst: str

Name of renamed file

f: True | {False}

Option to overwrite existing dst

Versions:
  • 2024-08-14 @ddalle: v1.0

extend_case(m: int = 1, j: int | None = None, nmax: int | None = None) int | None

Extend the case by one execution of final phase

Call:
>>> nnew = runner.extend_case(m=1, nmax=None)
Inputs:
runner: CaseRunner

Controller to run one case of solver

m: {1} | int

Number of additional times to execute final phase

j: {None} | int

Phase to extend

nmax: {None} | int

Do not exceed this iteration

Outputs:
nnew: None | int

Number of iters after extension, if changed

Versions:
  • 2024-08-26 @ddalle: v1.0

finalize_files(j: int)

Clean up files after running one cycle of phase j

Call:
>>> runner.finalize_files(j)
Inputs:
runner: CaseRunner

Controller to run one case of solver

j: int

Phase number

Versions:
  • 2023-06-20 @ddalle: v1.0 (abstract method)

get_archivist() CaseArchivist

Get or read archivist instance

Call:
>>> a = runner.get_archivist()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
a: CaseArchivist

Archive controller for one case

Versions:
  • 2024-09-13 @ddalle: v1.0

  • 2024-12-09 @ddalle: v1.1; prefer cntl opts over case

get_cape_stdoutfiles() list

Get list of STDOUT files in order they were run

Call:
>>> runfiles = runner.get_cape_stdoutfiles()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
runfiles: list[str]

List of run files, in ascending order

Versions:
  • 2024-08-09 @ddalle: v1.0

get_case_index() int | None

Get index of a case in the current run matrix

Call:
>>> i = runner.get_case_index()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
i: int | None

Index of case with name frun in run matrix, if present

Versions:
  • 2024-08-15 @ddalle: v1.0

get_case_name() str

Get name of this case according to CAPE run matrix

Call:
>>> casename = runner.get_case_name()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
casename: str

Name of case, using / as path sep

Versions:
  • 2024-08-15 @ddalle: v1.0

get_cntl_rootdir() str

Get name of this case according to CAPE run matrix

Call:
>>> rootdir = runner.get_cntl_rootdir()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
rootdir: str

Absolute path to base of run matrix that runs this case

Versions:
  • 2024-08-26 @ddalle: v1.0

get_cpu_time()

Read most appropriate total CPU usage for current case

Call:
>>> corehrs = runner.get_cpu_time()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
corehrs: None | float

Core hours since last start or None if not running

Versions:
  • 2015-12-22 @ddalle: v1.0 (Cntl.GetCPUTimeBoth)

  • 2016-08-30 @ddalle: v1.1; check for RUNNING

  • 2023-07-09 @ddalle: v2.0; rename, CaseRunner

get_cpu_time_start()

Read total core hours since start of current running phase

Call:
>>> corehrs = runner.get_cpu_time_start()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
corehrs: None | float

Core hours since last start or None if not running

Versions:
  • 2015-08-30 @ddalle: v1.0 (Cntl.GetCPUTimeFromStart)

  • 2023-07-09 @ddalle: v2.0

get_cpu_time_user()

Read total core hours from completed phase cycles

Call:
>>> corehrs = runner.get_cpu_time_user()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
corehrs: None | float

Total core hours used or None if no log file found

Versions:
  • 2015-12-22 @ddalle: v1.0 (Cntl.GetCPUTimeFromFile)

  • 2023-07-09 @ddalle: v2.0

get_funcname(frame: int = 1) str

Get name of calling function, mostly for log messages

Call:
>>> funcname = runner.get_funcname(frame=1)
Inputs:
runner: CaseRunner

Controller to run one case of solver

frame: {1} | int

Depth of function to seek title of

Outputs:
funcname: str

Name of calling function

Versions:
  • 2024-08-16 @ddalle

get_iter(f=True)

Detect most recent iteration

Call:
>>> n = runner.get_iter(f=True)
Inputs:
runner: CaseRunner

Controller to run one case of solver

f: {True} | False

Force recalculation of phase

Outputs:
n: int

Iteration number

Versions:
  • 2023-06-20 @ddalle: v1.0

get_job_id() str

Get PBS/Slurm job ID, if any

Call:
>>> job_id = runner.get_job_id()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
job_id: str

Text form of job ID; '' if no job found

Versions:
  • 2023-06-16 @ddalle: v1.0

  • 2023-07-05 @ddalle: v1.1; eliminate j arg

  • 2024-06-10 @ddalle: v2.0; use get_job_ids()

get_job_ids() list

Get list of PBS/Slurm job IDs, if appropriate

Call:
>>> job_id = runner.get_job_id()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
job_ids: list[str]

List of PBS/Slurm job IDs, as text

Versions:
  • 2024-06-10 @ddalle: v1.0

get_last_iter() int

Get min iteration required for a given case

Call:
>>> nmax = runner.get_last_iter()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
nmax: int

Number of iterations

Versions:
  • 2024-06-17 @ddalle: v1.0

  • 2024-07-18 @ddalle: v1.1; better exception handling

get_last_phase() int

Get min phase required for a given case

Call:
>>> jmax = runner.get_last_phase()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
jmax: int

Min phase required for job

Versions:
  • 2024-06-17 @ddalle: v1.0

  • 2024-07-18 @ddalle: v1.1; force reread, handle errors

get_logger() CaseLogger

Get or create logger instance

Call:
>>> logger = runner.get_logger()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
logger: CaseLogger

Logger instance

Versions:
  • 2024-08-16 @ddalle: v1.0

get_mach() float

Get Mach number even if mach is not a run matrix key

This uses cape.cfdx.runmatrix.RunMatrix.GetMach() to combine information from all run matrix keys.

Call:
>>> mach = runner.get_mach()
Inputs:
runner: CaseRunner

Controller to run one case of solver

key: str

Name of run matrix key to query

f: True | {False}

Option to force re-read

Outputs:
mach: float

Mach number

Versions:
  • 2024-12-03 @ddalle: v1.0

get_next_phase(j: int) int | None

Get the number of the phase that follows j

Call:
>>> jnext = runner.get_next_phase(j)
Inputs:
runner: CaseRunner

Controller to run one case of solver

j: int

Current phase number

Outputs:
jnext: int | None

Next phase number, if applicable

Versions:
  • 2024-08-11 @ddalle: v1.0

get_pbs_script(j=None)

Get file name of PBS script

… or Slurm script or execution script

Call:
>>> fpbs = runner.get_pbs_script(j=None)
Inputs:
runner: CaseRunner

Controller to run one case of solver

j: {None} | int

Phase number

Outputs:
fpbs: str

Name of main script to run case

Versions:
  • 2014-12-01 @ddalle: v1.0 (pycart)

  • 2015-10-19 @ddalle: v1.0 (pyfun)

  • 2023-06-18 @ddalle: v1.1; instance method

get_phase(f=True) int

Determine phase number in present case

Call:
>>> j = runner.get_phase(n, f=True)
Inputs:
runner: CaseRunner

Controller to run one case of solver

f: {True} | False

Force recalculation of phase

Outputs:
j: int

Phase number for next restart

Versions:
  • 2014-10-02 @ddalle: v1.0

  • 2015-10-19 @ddalle: v1.1; FUN3D version

  • 2016-04-14 @ddalle: v1.2; CFDX version

  • 2023-06-16 @ddalle: v2.0; CaseRunner method

get_phase_index(j: int) int | None

Get index of phase in "PhaseSequence"

Call:
>>> k = runner.get_phase_index(j)
Inputs:
runner: CaseRunner

Controller to run one case of solver

j: int

Phase number

Outputs:
k: None | int

Index of j in PhaseSequence; None if j is not one of the prescribed phases

Versions:
  • 2024-08-11 @ddalle: v1.0

get_phase_iters(j: int) int

Get min iteration required for completion of phase j

Call:
>>> nmax = runner.get_phase_iters(j)
Inputs:
runner: CaseRunner

Controller to run one case of solver

j: int

Phase index

Outputs:
n: int

Number of iterations

Versions:
  • 2024-07-18 @ddalle: v1.0

get_phase_sequence() list

Get list of prescribed phases for a case

Call:
>>> phases = runner.get_phase_sequence()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
phases: int[int]

Min phase required for job

Versions:
  • 2024-07-18 @ddalle: v1.0

get_project_rootname(j: int | None = None) str

Read namelist and return project namelist

Call:
>>> rname = runner.get_project_rootname(j=None)
Inputs:
runner: CaseRunner

Controller to run one case of solver

j: {None} | int

Phase number

Outputs:
rname: str

Project rootname

Versions:
  • 2024-11-05 @ddalle: v1.0

get_reportfiles() list

Generate list of report files

Call:
>>> filelist = runner.get_reportfiles()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
filelist: list[str]

List of files to protect

Verions:
  • 2024-09-14 @ddalle: v1.0

get_restart_file(j: int | None = None) str

Get the most recent restart file for phase j

Call:
>>> restartfile = runner.get_restart_file(j=None)
Inputs:
runner: CaseRunner

Controller to run one case of solver

j: {None} | int

Phase number

Versions:
  • 2024-11-05 @ddalle: v1.0

get_restart_iter(f=True)

Get number of iteration if case should restart

Call:
>>> nr = runner.get_restart_iter(f=True)
Inputs:
runner: CaseRunner

Controller to run one case of solver

f: {True} | False

Force recalculation of phase

Outputs:
nr: int

Restart iteration number

Versions:
  • 2023-06-20 @ddalle: v1.0; cfdx abstract version

get_restartfiles() list

Generate list of restart files

Call:
>>> filelist = runner.get_restartfiles()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
filelist: list[str]

List of files to protect

Verions:
  • 2024-09-14 @ddalle: v1.0

get_returncode() int

Check for other errors; rewrite for each solver

Call:
>>> ierr = runner.get_returncode()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
ierr: int

Return code

Versions:
  • 2023-06-20 @ddalle: v1.0

  • 2024-06-17 @ddalle: v1.1; was check_error()

  • 2024-07-16 @ddalle: v1.2; use self.returncode

get_runlog() ndarray

Create a 2D array of CAPE exit phases and iters

Each row of the output is the phase number and iteration at which CAPE exited. The array is sorted by ascending phase then iteration.

Call:
>>> runlog = runner.get_runlog()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
runlog: np.ndarray[int]

2D array of all CAPE exit phase and iteration numbers

Versions:
  • 20254-03-23 @ddalle: v1.0

get_runlog_iter()

Get phase and iteration from most recent CAPE log file name

Call:
>>> phase, iter = runner.get_runlog_iter()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
phase: int

Phase number reported by CAPE

iter: int

Iteration number reported by CAPE

Versions:
  • 2024-03-22 @ddalle: v1.0

get_status() str

Calculate status of current job

Call:
>>> sts = runner.get_status()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
sts: str

One of several possible job statuses

  • DONE: not running and meets finishing criteria

  • ERROR: error detected

  • RUNNING: case is currently running

  • INCOMP: case not running and not finished

get_stop_iter()

Read iteration at which to stop

Call:
>>> nstop = runner.get_stop_iter()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
nstop: int | None

Iteration at which to stop, if any

Versions:
  • 2023-06-20 @ddalle: v1.0

get_working_folder() str

Get working folder, . for generic solver

Call:
>>> fdir = runner.get_working_folder()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
fdir: "."

Working folder relative to roo, where next phase is run

Versions:
  • 2024-08-11 @ddalle: v1.0

get_working_folder_() str

Get working folder, but replace '.' with ''

This results in cleaner results with os.path.join().

Call:
>>> fdir = runner.get_working_folder()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
fdir: "" | str

Working folder relative to roo, where next phase is run

Versions:
  • 2024-08-14 @ddalle: v1.0

getx_iter()

Calculate most recent iteration

Call:
>>> n = runner.getx_iter()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
n: int

Iteration number

Versions:
  • 2023-06-20 @ddalle: v1.0

getx_phase(n: int)

Calculate phase based on present files

The x in the title implies this case might be rewritten for each module.

Call:
>>> j = runner.getx_phase(n)
Inputs:
runner: CaseRunner

Controller to run one case of solver

n: int

Iteration number

Outputs:
j: int

Phase number for next restart

Versions:
  • 2023-06-16 @ddalle: v1.0

  • 2023-07-06 @ddalle: v1.1; PhaseSequence repeats ok

  • 2024-08-12 @ddalle: v1.2; refine file names slightly

getx_restart_iter()

Calculate number of iteration if case should restart

Call:
>>> nr = runner.gets_restart_iter()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
nr: int

Restart iteration number

Versions:
  • 2023-06-20 @ddalle: v1.0; cfdx abstract version

import_cntlmod()

Import appropriate run matrix-level cntl module

Call:
>>> mod = runner.import_cntlmod()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
mod: module

Module imported

Versions:
  • 2024-08-14 @ddalle: v1.0

  • 2024-09-07 @ddalle: v1.1; mod for moving cntl to cfdx

init_post()

Custom initialization hook

Call:
>>> runner.init_post()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Versions:
  • 2023-06-28 @ddalle: v1.0

init_timer()

Mark a case as RUNNING and initialize a timer

Call:
>>> tic = runner.init_timer()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
tic: datetime.datetime

Time at which case was started

Versions:
  • 2021-10-21 @ddalle: v1.0; from run_fun3d()

  • 2023-06-20 @ddalle: v1.1; instance method, no mark()

Copy a link and log results

Call:
>>> runner.link_file(src, dst, f=False)
Inputs:
runner: CaseRunner

Controller to run one case of solver

src: str

Name of input file, before renaming

dst: str

Name of renamed file

f: True | {False}

Option to overwrite existing dst

Versions:
  • 2024-08-14 @ddalle: v1.0

log_both(msg: str, title: str | None = None, parent: int = 0)

Write a message to both primary and verbose logs

Call:
>>> runner.log_both(title, msg)
Inputs:
runner: CaseRunner

Controller to run one case of solver

msg: str

Primary content of message

title: {None} | str

Manual title (default is name of calling function)

parent: {0} | int

Extra levels to use for calling function name

Versions:
  • 2024-08-01 @ddalle: v1.0

log_data(data: dict, title: str | None = None, parent: int = 0)

Write dict to verbose log as JSON

Call:
>>> runner.log_data(title, data)
Inputs:
runner: CaseRunner

Controller to run one case of solver

data: dict

Parameters to write to verbose log as JSON

msg: str

Primary content of message

title: {None} | str

Manual title (default is name of calling function)

parent: {0} | int

Extra levels to use for calling function name

Versions:
  • 2024-08-01 @ddalle: v1.0

log_main(msg: str, title: str | None = None, parent: int = 0)

Write a message to primary log

Call:
>>> runner.log_main(msg, title, parent=0)
Inputs:
runner: CaseRunner

Controller to run one case of solver

msg: str

Primary content of message

title: {None} | str

Manual title (default is name of calling function)

parent: {0} | int

Extra levels to use for calling function name

Versions:
  • 2024-08-01 @ddalle: v1.0

log_verbose(msg: str, title: str | None = None, parent: int = 0)

Write a message to verbose log

Call:
>>> runner.log_verbose(title, msg)
Inputs:
runner: CaseRunner

Controller to run one case of solver

msg: str

Primary content of message

title: {None} | str

Manual title (default is name of calling function)

parent: {0} | int

Extra levels to use for calling function name

Versions:
  • 2024-08-01 @ddalle: v1.0

mark_failure(msg='no details')

Mark the current folder in failure status using FAIL file

Call:
>>> runner.mark_failure(msg="no details")
Inputs:
runner: CaseRunner

Controller to run one case of solver

msg: {"no details"} | str

Error message for output file

Versions:
  • 2023-06-02 @ddalle: v1.0

  • 2023-06-20 @ddalle: v1.1; instance method

mark_running()

Check if cases already running and create RUNNING otherwise

Call:
>>> runner.mark_running()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Versions:
  • 2023-06-02 @ddalle: v1.0

  • 2023-06-20 @ddalle: v1.1; instance method, no check()

mark_stopped()

Delete the RUNNING file if it exists

Call:
>>> mark_stopped()
Versions:
  • 2023-06-02 @ddalle: v1.0

  • 2024-08-03 @ddalle: v1.1; add log message

prepare_env(j: int)

Set environment vars, alter resource limits (ulimit)

This function relies on the system module resource

Call:
>>> runner.prepare_env(rc, i=0)
Inputs:
runner: CaseRunner

Controller to run one case of solver

j: int

Phase number

See also:
Versions:
  • 2015-11-10 @ddalle: v1.0 (PrepareEnvironment())

  • 2023-06-02 @ddalle: v1.1; fix logic for appending
    • E.g. "PATH": "+$HOME/bin"

    • This is designed to append to path

  • 2023-06-20 @ddalle: v1.2; instance mthod

prepare_files(j: int)

Prepare files for phase j

Call:
>>> runner.prepare_files(j)
Inputs:
runner: CaseRunner

Controller to run one case of solver

j: int

Phase index

Versions:
  • 2021-10-21 @ddalle: v1.0 (abstract cfdx method)

read_archive_opts() ArchiveOpts

Read the Archive options for this case

This prefers the parent-folder JSON settings to those found in case.json. If no run matrix JSON settings can be read, the case.json settings will be used.

Call:
>>> opts = runner.read_archive_opts()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
opts: ArchiveOpts

Options interface from case.json

Versions:
  • 2024-08-28 @ddalle: v1.0

  • 2024-12-09 @ddalle: v2.0; prefer top-level JSON

read_case_json() RunControlOpts

Read case.json if not already

Call:
>>> rc = runner.read_case_json()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
rc: RunControlOpts

Options interface from case.json

Versions:
  • 2023-06-15 @ddalle: v1.0

  • 2024-07-18 @ddalle: v2.0; remove f option, use mtime

read_cntl()

Read the parent run-matrix control that owns this case

Call:
>>> cntl = runner.read_cntl()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
cntl: Cntl | None

Run matrix control instance

Versions:
  • 2024-08-15 @ddalle: v1.0

  • 2024-08-28 @ddalle: v1.1; can work w/o case.json

read_condition(key: str, f=False)

Read conditions.json if not already

Call:
>>> v = runner.read_condition(key, f=False)
Inputs:
runner: CaseRunner

Controller to run one case of solver

key: str

Name of run matrix key to query

f: True | {False}

Option to force re-read

Outputs:
v: int | float | str

Value of run matrix key key

Versions:
  • 2023-06-16 @ddalle: v1.0

read_conditions(f: bool = False) dict

Read conditions.json if not already

Call:
>>> xi = runner.read_conditions(f=False)
Inputs:
runner: CaseRunner

Controller to run one case of solver

f: True | {False}

Option to force re-read

Outputs:
xi: dict

Run matrix conditions for this case

Versions:
  • 2023-06-16 @ddalle: v1.0

read_start_time()

Read the most recent start time to file

Call:
>>> nProc, tic = runner.read_start_time()
Inputs:
runner: CaseRunner

Controller to run one case of solver

fname: str

Name of file containing CPU usage history

Outputs:
nProc: None | int

Number of cores

tic: None | datetime.datetime

Time at which most recent run was started

Versions:
  • 2016-08-30 @ddalle: v1.0 (stand-alone)

  • 2023-06-17 @ddalle: v2.0; CaseRunner method

read_stop_phase() Tuple[bool, int | None]

Read CAPE-STOP-PHASE file for local stopping criterion

Call:
>>> q, j = runner.read_stop_phase()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
q: True | False

Whether CAPE-STOP-PHASE file exists

j: None | int

Phase at which to stop (every phase if None)

Versions:
  • 2024-05-26 @ddalle: v1.0

relpath(fname: str) str

Get path to file relative to case root directory

Call:
>>> frel = runner.relpath(fname)
Inputs:
runner: CaseRunner

Controller to run one case of solver

fname: str

File name, relative to PWD or absolute

Outputs:
frel: str

Path to fname relative to runner.root_dir

Versions:
  • 2024-08-14 @ddalle: v1.0

Delete a link [file if f] if it exists

Call:
>>> runner.remove_link(dst, f=False)
Inputs:
runner: CaseRunner

Controller to run one case of solver

dst: str

Name of link to delete

f: True | {False}

Option to overwrite dst, even if not a link

Versions:
  • 2024-08-14 @ddalle: v1.0

rename_file(src: str, dst: str, f: bool = False)

Rename a file and log results

Call:
>>> runner.rename_file(src, dst, f=False)
Inputs:
runner: CaseRunner

Controller to run one case of solver

src: str

Name of input file, before renaming

dst: str

Name of renamed file

f: True | {False}

Option to overwrite existing dst

Versions:
  • 2024-08-13 @ddalle: v1.0

resubmit_case(j0: int)

Resubmit a case as a new job if appropriate

Call:
>>> q = runner.resubmit_case(j0)
Inputs:
runner: CaseRunner

Controller to run one case of solver

j0: int

Index of phase most recently run prior (may differ from current phase)

Outputs:
q: True | False

Whether or not a new job was submitted to queue

Versions:
  • 2022-01-20 @ddalle: v1.0 (cape.pykes.case)

  • 2023-06-02 @ddalle: v1.0

  • 2024-05-25 @ddalle: v1.1; rename options

run()

Setup and run appropriate solver commands

Call:
>>> ierr = runner.run()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
ierr: int

Return code; 0 for success

Versions:
  • 2014-10-02 @ddalle: v1.0

  • 2021-10-08 @ddalle: v1.1 (run_overflow)

  • 2023-06-21 @ddalle: v2.0; instance method

  • 2024-05-26 @ddalle: v2.1; more exit causes

run_aflr3(j: int, proj: str, fmt='lb8.ugrid')

Create volume mesh using aflr3

This function looks for several files to determine the most appropriate actions to take:

  • {proj}.i.tri: Triangulation file

  • {proj}.surf: AFLR3 surface file

  • {proj}.aflr3bc: AFLR3 boundary conditions

  • {proj}.xml: Surface component ID mapping file

  • {proj}.{fmt}: Output volume mesh

  • {proj}.FAIL.surf: AFLR3 surface indicating failure

If the volume grid file already exists, this function takes no action. If the surf file does not exist, the function attempts to create it by reading the tri, xml, and aflr3bc files using cape.trifile.Tri. The function then calls cape.bin.aflr3() and finally checks for the FAIL file.

Call:
>>> runner.run_aflr3(j, proj, fmt='lb8.ugrid')
Inputs:
runner: CaseRunner

Controller to run one case of solver

j: int

Phase number

proj: str

Project root name

fmt: {"lb8.ugrid"} | str

AFLR3 volume mesh format

Versions:
  • 2016-04-05 @ddalle: v1.0 (CaseAFLR3())

  • 2023-06-02 @ddalle: v1.1; use get_aflr3_run()

  • 2023-06-20 @ddalle: v1.1; instance method

  • 2024-08-22 @ddalle: v1.2; add log messages

run_intersect(j: int, proj: str = 'Components')

Run intersect to combine surface triangulations

This is a multi-step process in order to preserve all the component IDs of the input triangulations. Normally intersect requires each intersecting component to have a single component ID, and each component must be a water-tight surface.

Cape utilizes two input files, Components.c.tri, which is the original triangulation file with intersections and the original component IDs, and Components.tri, which maps each individual original tri file to a single component. The files involved are tabulated below.

  • Components.tri: Intersecting comps, each w/ single compID

  • Components.c.tri: Original intersecting tris and compIDs

  • Components.o.tri: Output of intersect w/ few compIDs

  • Components.i.tri: Orig compIDs mapped to intersected tris

More specifically, these files are {proj}.i.tri, etc.; the default project name is "Components". This function also calls the Chimera Grid Tools program triged to remove unused nodes from the intersected triangulation and optionally remove small triangles.

Call:
>>> runner.run_intersect(j, proj="Components")
Inputs:
runner: CaseRunner

Controller to run one case of solver

j: int

Phase number

proj: {'Components'} | str

Project root name

See also:
Versions:
  • 2015-09-07 @ddalle: v1.0 (CaseIntersect)

  • 2016-04-05 @ddalle: v1.1; generalize to cfdx

  • 2023-06-21 @ddalle: v1.2; update name, instance method

  • 2024-08-22 @ddalle: v1.3; add log messages

run_more_cases() int

Submit more cases to the queue

Call:
>>> ierr = runner.run_more_cases()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
ierr: int

Return code, 0 if no new cases submitted

Versions:
  • 2023-12-13 @dvicker: v1.0

run_phase(j: int) int

Run one phase using appropriate commands

Call:
>>> ierr = runner.run_phase(j)
Inputs:
runner: CaseRunner

Controller to run one case of solver

j: int

Phase number

Outputs:
ierr: int

Return code

Versions:
  • 2023-06-05 @ddalle: v1.0 (pyover)

  • 2023-06-14 @ddalle: v1.0

run_post_shell_cmds(j: int)

Run PostShellCmds after successful run_phase() exit

Call:
>>> runner.run_post_shell_cmds(j)
Inputs:
runner: CaseRunner

Controller to run one case of solver

j: int

Phase number

Versions:
  • 2023-07-17 @ddalle: v1.0

  • 2024-08-21 @ddalle: v1.1; log messages

run_verify(j: int, proj='Components')

Run verify to check triangulation if appropriate

This function checks the validity of triangulation in file "%s.i.tri" % proj. It calls cape.bin.verify().

Call:
>>> runner.run_verify(j, proj='Components', fpre='run')
Inputs:
runner: CaseRunner

Controller to run one case of solver

j: int

Phase number

proj: {'Components'} | str

Project root name

Versions:
  • 2015-09-07 @ddalle: v1.0; from run_flowCart()

  • 2016-04-05 @ddalle: v1.1; generalize to cape

  • 2023-06-21 @ddalle: v2.0; instance method

  • 2024-08-22 @ddalle: v2.1; add log messages

save_reportfiles()

Update list of protected files for generating reports

Call:
>>> runner.save_reportfiles()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Versions:
  • 2024-09-14 @ddalle: v1.0

save_restartfiles()

Update list of protected files for restarting case

Call:
>>> runner.save_restartfiles()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Versions:
  • 2024-09-14 @ddalle: v1.0

set_rlimit(r: int, u: str, j: int = 0, unit: int = 1024)

Set resource limit for one variable

Call:
>>> runner.set_rlimit(r, u, j=0, unit=1024)
Inputs:
runner: CaseRunner

Controller to run one case of solver

r: int

Integer code of particular limit, from resource

u: str

Name of limit to set

j: {0} | int

Phase number

unit: {1024} | int

Multiplier, usually for a kbyte

See also:
  • cape.options.ulimit

Versions:
  • 2016-03-13 @ddalle: v1.0

  • 2021-10-21 @ddalle: v1.1; check if Windows

  • 2023-06-20 @ddalle: v1.2; was SetResourceLimit()

skeleton(test: bool = False)

Run the --skeleton archiving action

Call:
>>> runner.skeleton(test=False)
Inputs:
runner: CaseRunner

Controller to run one case of solver

test: True | {False}

Option to log all actions but not actually copy/delete

Versions:
  • 2024-09-18 ``@ddalle`: v1.0

start()

Start or submit initial job

Call:
>>> ierr, job_id = runner.start()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Outputs:
ierr: int

Return code; 0 for success

job_id: None | int

PBS/Slurm job ID number if appropriate

Versions:
  • 2023-06-23 @ddalle: v1.0

stop_case()

Stop a case by deleting PBS job and removing RUNNING file

Call:
>>> runner.stop_case()
Inputs:
runner: CaseRunner

Controller to run one case of solver

Versions:
  • 2014-12-27 @ddalle: v1.0 (StopCase())

  • 2023-06-20 @ddalle: v1.1; instance method

  • 2023-07-05 @ddalle: v1.2; use CaseRunner.get_job_id()

touch_file(fname: str)

Create an empty file if necessary, or update mtime

Call:
>>> runner.touch_file(fname)
Inputs:
runner: CaseRunner

Controller to run one case of solver

fname: str

Name of file to “touch”

Version:
  • 2024-08-14 @ddalle: v1.0

unarchive(test: bool = False)

Run the --unarchive archiving action

Call:
>>> runner.unarchive(test=False)
Inputs:
runner: CaseRunner

Controller to run one case of solver

test: True | {False}

Option to log all actions but not actually copy/delete

Versions:
  • 2024-09-20 ``@ddalle`: v1.0

validate_dstfile(dst: str)

Check that dst is a valid destination for rename/copy

Checks that dst is inside case folder

Call:
>>> runner.validate_dstfile(dst)
Inputs:
runner: CaseRunner

Controller to run one case of solver

src: str

Name of input file, before renaming

Versions:
  • 2024-08-14 @ddalle: v1.0

validate_srcfile(src: str)

Check that src exists and is a file

Checks that src is a file or a valid link.

Call:
>>> runner.validate_srcfile(src)
Inputs:
runner: CaseRunner

Controller to run one case of solver

src: str

Name of input file, before renaming

Versions:
  • 2024-08-14 @ddalle: v1.0

write_case_json(rc: RunControlOpts)

Write the current settinsg to case.json

Call:
>>> runner.write_case_json(rc)
Inputs:
runner: CaseRunner

Controller to run one case of solver

rc: RunControlOpts

Options interface from case.json

Versions:
  • 2024-08-24 @ddalle: v1.0

write_start_time(j: int)

Write current start time, runner.tic, to file

Call:
>>> runner.write_start_time(tic, j)
Inputs:
runner: CaseRunner

Controller to run one case of solver

j: int

Phase number

Versions:
  • 2015-12-09 @ddalle: v1.0 (pycart)

  • 2015-12-22 @ddalle: v1.0; module function

  • 2023-06-16 @ddalle: v2.0; CaseRunner method

write_user_time(j: int)

Write time usage since time tic to file

Call:
>>> runner.write_user_time(tic, j)
Inputs:
runner: CaseRunner

Controller to run one case of solver

j: int

Phase number

Versions:
  • 2015-12-09 @ddalle: v1.0 (pycart)

  • 2015-12-22 @ddalle: v1.0; module function

  • 2023-06-16 @ddalle: v2.0; CaseRunner method

cape.cfdx.casecntl.GetTriqFile(proj='Components')

Get most recent triq file and its associated iterations

This is a template version with specific implementations for each solver. The cape.cfdx version simply returns the most recent triq file in the folder with no iteration information.

Call:
>>> ftriq, n, i0, i1 = GetTriqFile(proj='Components')
Inputs:
proj: {"Components"} | str

File root name

Outputs:
ftriq: str

Name of most recently modified triq file

n: {None}

Number of iterations included

i0: {None}

First iteration in the averaging

i1: {None}

Last iteration in the averaging

Versions:
  • 2016-12-19 @ddalle: v1.0

cape.cfdx.casecntl.StartCase()

Empty template for starting a case

The function is empty but does not raise an error

Call:
>>> cape.casecntl.StartCase()
See also:
  • cape.pycart.casecntl.StartCase()

  • cape.pyfun.casecntl.StartCase()

  • cape.pyover.casecntl.StartCase()

Versions:
  • 2015-09-27 @ddalle: v1.0

  • 2023-06-02 @ddalle: v2.0; empty

cape.cfdx.casecntl.set_rlimit(r: int, ulim: ULimitOpts, u: str, i: int = 0, unit: int = 1024)

Set resource limit for one variable

Call:
>>> set_rlimit(r, ulim, u, i=0, unit=1024)
Inputs:
r: int

Integer code of particular limit, from resource

ulim: ULimitOpts

System resource options interface

u: str

Name of limit to set

i: int

Phase number

unit: int

Multiplier, usually for a kbyte

See also:
  • cape.options.ulimit

Versions:
  • 2016-03-13 @ddalle: v1.0

  • 2021-10-21 @ddalle: v1.1; check if Windows

  • 2023-06-20 @ddalle: v1.2; was SetResourceLimit()