@@ -111,7 +111,7 @@ def _get_doc_models():
111111 SeedDataNodeConfig ,
112112 OptimizationOptions , FoamJobRunnerOptions , VariableSubstOptions ,
113113 FileSubstOptions , BaselineOptions , StoreOptions ,
114- TrialSelector , TrialAction , TrialDependency ,
114+ TrialSelector , TrialDependency ,
115115 DimensionalityReductionOptions ,
116116 )
117117 from .metrics import FoamJob , LocalJobMetric
@@ -135,7 +135,6 @@ def _get_doc_models():
135135 (StoreOptions , "store" ),
136136 (TrialDependency , "trial_dependencies[]" ),
137137 (TrialSelector , "trial_dependencies[].source" ),
138- (TrialAction , "trial_dependencies[].actions[]" ),
139138 (FoamJob , "internal.FoamJob" ),
140139 ]
141140
@@ -210,13 +209,6 @@ def get_default_config() -> Dict[str, Any]:
210209 "strategy" : "best" ,
211210 "fallback" : "skip" ,
212211 },
213- "actions" : [
214- {
215- "type" : "run_command" ,
216- "command" : "cp -rT $FOAMBO_SOURCE_TRIAL/0.5 $FOAMBO_TARGET_TRIAL/0" ,
217- "phase" : "immediate" ,
218- },
219- ],
220212 },
221213 {
222214 "name" : "reuse_mesh" ,
@@ -226,13 +218,6 @@ def get_default_config() -> Dict[str, Any]:
226218 "group" : "geometry" ,
227219 "fallback" : "skip" ,
228220 },
229- "actions" : [
230- {
231- "type" : "run_command" ,
232- "command" : "cp -rT $FOAMBO_SOURCE_TRIAL/constant/polyMesh $FOAMBO_TARGET_TRIAL/constant/polyMesh" ,
233- "phase" : "pre_mesh" ,
234- },
235- ],
236221 },
237222 ]
238223
@@ -571,94 +556,83 @@ def get_config_docs() -> Dict[str, Any]:
571556 source:
572557 strategy: best
573558 fallback: skip
574- actions:
575- - type: run_command
576- command: "cp -rT $FOAMBO_SOURCE_TRIAL/0.5 $FOAMBO_TARGET_TRIAL/0"
577- phase: immediate # default — runs before the runner starts
578559 ```
579560
580- Copy mesh before meshing (deferred to `$FOAMBO_PRE_MESH` hook) :
561+ Reuse mesh when geometry parameters haven't changed :
581562 ```yaml
582563 trial_dependencies:
583- - name: mesh_inherit
564+ - name: reuse_mesh
584565 source:
585- strategy: nearest
566+ strategy: matching_group
567+ group: "geometry"
586568 fallback: skip
587- actions:
588- - type: run_command
589- command: "cp -rT $FOAMBO_SOURCE_TRIAL/constant/polyMesh $FOAMBO_TARGET_TRIAL/constant/polyMesh"
590- phase: pre_mesh
591- ```
592-
593- Use mapFields after meshing (deferred to `$FOAMBO_PRE_SOLVE` hook):
594- ```yaml
595- trial_dependencies:
596- - name: map_fields
569+ - name: warm_fields
597570 source:
598- strategy: best
571+ strategy: nearest
572+ similarity_threshold: 0.3
599573 fallback: skip
600- actions:
601- - type: run_command
602- command: "mapFields $FOAMBO_SOURCE_TRIAL -sourceTime latestTime -case $FOAMBO_TARGET_TRIAL"
603- phase: pre_solve
604- ```
605-
606- With the corresponding Allrun script calling the hooks:
607- ```bash
608- #!/bin/bash
609- $FOAMBO_PRE_INIT # no-op unless configured
610- blockMesh
611- $FOAMBO_PRE_MESH # no-op unless configured
612- $FOAMBO_PRE_SOLVE # runs mapFields from source trial
613- simpleFoam
614- $FOAMBO_POST_SOLVE # no-op unless configured
615574 ```
616575
617- **Phases and environment variables:**
618- - `immediate` (default): action runs before the runner starts
619- - `pre_init` / `pre_mesh` / `pre_solve` / `post_solve`: action is
620- written to a hook script (`.foambo_<phase>.sh`) in the case directory
621- and exposed via `$FOAMBO_<PHASE>` environment variable
622-
623- Reuse mesh when geometry parameters haven't changed (`matching_group`):
576+ With parameters tagged by group:
624577 ```yaml
625578 experiment:
626579 parameters:
627580 - name: angle1
628581 bounds: [20, 40]
629- groups: ["geometry"] # tag parameters with groups
582+ groups: ["geometry"]
630583 - name: relaxation
631- bounds: [0.1, 0.9] # no group — changes freely
632-
633- trial_dependencies:
634- - name: reuse_mesh
635- source:
636- strategy: matching_group
637- group: "geometry" # match on all params tagged "geometry"
638- fallback: skip # mesh from scratch if no match
639- actions:
640- - type: run_command
641- command: "cp -rT $FOAMBO_SOURCE_TRIAL/constant/polyMesh $FOAMBO_TARGET_TRIAL/constant/polyMesh"
642- phase: pre_mesh
584+ bounds: [0.1, 0.9]
643585 ```
644- When the new trial's geometry parameters exactly match a completed trial,
645- the mesh is copied instead of regenerated. Parameters can belong to
646- multiple groups (e.g. `groups: ["geometry", "mesh"]`).
647586
648- All hooks default to no-op on the first trial (no source yet).
587+ **How it works:** foamBO resolves each dependency and writes
588+ `.foambo_deps.json` to the trial case directory. The manifest
589+ path is available via `$FOAMBO_DEPS`. The runner script reads
590+ it and decides what to do:
649591
650- **Additional env vars available to the runner:**
651- - `$FOAMBO_CASE_PATH` / `$FOAMBO_CASE_NAME` — trial case directory
652- - `$FOAMBO_SOURCE_TRIAL` — resolved source path (unset if no match)
653- - `$FOAMBO_TARGET_TRIAL` — current trial path
592+ ```bash
593+ #!/bin/bash
594+ deps="$FOAMBO_DEPS"
595+
596+ # Mesh: reuse or generate
597+ if jq -e '.reuse_mesh.resolved' "$deps" >/dev/null 2>&1; then
598+ src=$(jq -r '.reuse_mesh.source_path' "$deps")
599+ cp -rT "$src/constant/polyMesh" constant/polyMesh
600+ else
601+ blockMesh
602+ fi
603+
604+ # Fields: warm-start or cold start
605+ if jq -e '.warm_fields.resolved' "$deps" >/dev/null 2>&1; then
606+ src=$(jq -r '.warm_fields.source_path' "$deps")
607+ mapFields "$src" -sourceTime latestTime
608+ else
609+ potentialFoam -writep > log.potentialFoam 2>&1
610+ fi
654611
655- **Non-shell runners** (Python, binary) can execute hook scripts directly:
612+ simpleFoam
613+ ```
614+
615+ **Non-shell runners** (Python, binary):
656616 ```python
657- import subprocess, os
658- subprocess.run(os.environ["FOAMBO_PRE_SOLVE"])
659- # or: subprocess.run("./.foambo_pre_solve.sh")
617+ import json, os
618+ deps = json.load(open(os.environ["FOAMBO_DEPS"]))
619+ if deps["reuse_mesh"]["resolved"]:
620+ shutil.copytree(deps["reuse_mesh"]["source_path"] + "/constant/polyMesh",
621+ "constant/polyMesh", dirs_exist_ok=True)
622+ ```
623+
624+ **Manifest format** (`.foambo_deps.json`):
625+ ```json
626+ {
627+ "reuse_mesh": {"resolved": true, "source_trial_index": 3, "source_path": "/path/to/trial_0003"},
628+ "warm_fields": {"resolved": false}
629+ }
660630 ```
661631
632+ **Environment variables available to the runner:**
633+ - `$FOAMBO_DEPS` — path to `.foambo_deps.json`
634+ - `$FOAMBO_CASE_PATH` / `$FOAMBO_CASE_NAME` — trial case directory
635+
662636 The dependency resolution result is recorded in
663637 `run_metadata["dependencies"]` for traceability.
664638 """ ,
@@ -1064,11 +1038,8 @@ def __init__(self, **kwargs):
10641038 .stop(max_trials=50, improvement_bar=0.1)
10651039 .early_stop(type="percentile", metric_names=["residuals"],
10661040 percentile_threshold=25, min_progression=5)
1067- .depend("warm_start", source="best",
1068- command="cp -rT $FOAMBO_SOURCE_TRIAL/0.5 $FOAMBO_TARGET_TRIAL/0")
1069- .depend("map_fields", source="nearest",
1070- command="mapFields $FOAMBO_SOURCE_TRIAL -case $FOAMBO_TARGET_TRIAL -sourceTime latestTime",
1071- phase="pre_solve")
1041+ .depend("warm_start", source="best")
1042+ .depend("reuse_mesh", source="matching_group", group="geometry")
10721043 .run(parallelism=3, poll_interval=10, ttl=600)
10731044 )
10741045 predictions = client.predict([{"x": 10, "y": "A"}])
@@ -1114,7 +1085,7 @@ def my_objective(parameters):
11141085 - `.stop(max_trials, improvement_bar)` — global stopping
11151086 - `.early_stop(type, ...)` — trial-level early stopping
11161087 - `.reduce(after_trials, min_importance)` — auto-fix irrelevant parameters
1117- - `.depend(name, source, command )` — trial-to-trial dependencies
1088+ - `.depend(name, source)` — trial-to-trial dependencies
11181089 - `.preflight(dry_run=True)` — validate config before running
11191090 - `.run(parallelism, ...)` — execute and return the Ax Client
11201091 - `FoamBO.load(name)` — load a saved experiment for analysis
0 commit comments