Plan Execution¶
RAS Commander provides three modes for executing HEC-RAS plans, each optimized for different workflows.
Lean Batch Installation¶
Batch workers that only need project inventory, plan computation, computation messages, and HDF result extraction can install the compute dependency profile:
Use the intentionally narrow facade in integration code:
from ras_commander.compute import (
HdfResultsPlan,
RasCmdr,
ResultsParser,
init_ras_project,
)
This facade does not export RasControl or RasControlResult. Call
RasCmdr.compute_plan() rather than constructing a Ras.exe -c command in the
integrating application. The method retains normal ras-commander behavior:
command construction, process and dialog supervision, compute-message handling,
caller-controlled semantic verification, and post-compute project/result
DataFrame refresh.
Single Plan Execution¶
Execute one plan with full parameter control using RasCmdr.compute_plan().
from ras_commander import init_ras_project, RasCmdr
init_ras_project("/path/to/project", "6.5")
# Basic execution
success = RasCmdr.compute_plan("01")
Parameters¶
| Parameter | Type | Description |
|---|---|---|
plan_number |
str | Plan identifier ("01", "02", etc.) |
dest_folder |
str/Path | Directory for computation (None = in-place) |
ras_object |
RasPrj | Project object (default: global ras) |
clear_geompre |
bool | Clear geometry preprocessor files first |
num_cores |
int | Number of CPU cores to use |
overwrite_dest |
bool | Overwrite destination if exists |
max_runtime |
float/None | Optional positive finite execution limit in seconds |
Examples¶
# Execute with specific core count
success = RasCmdr.compute_plan("01", num_cores=4)
# Execute to separate folder (preserves original)
success = RasCmdr.compute_plan(
"01",
dest_folder="/results/run1",
overwrite_dest=True
)
# Force geometry preprocessing
success = RasCmdr.compute_plan("01", clear_geompre=True)
Bounded Execution and Launch Evidence¶
max_runtime is an additive execution-safety option for direct
RasCmdr.compute_plan() runs. Supply a positive finite number of seconds when
the caller needs a hard plan-specific engine deadline:
result = RasCmdr.compute_plan(
"01",
force_rerun=True,
verify=True,
max_runtime=900,
)
details = result.execution_details
if details["runtime_timed_out"]:
print(details["failure_stage"], details["cancellation_details"])
Boolean values, strings, zero, negative values, NaN, infinity, and values above
4,294,967.294 seconds are rejected before project access or execution begins.
The upper limit keeps the value representable by the Windows subprocess wait
API. An explicit value establishes one monotonic engine deadline immediately
before Ras.exe launch, after executable proof, process preflight, and
result-artifact preparation. The remaining budget is shared by compute-message
callback monitoring, the direct launcher wait, asynchronous solver completion,
and exact-plan quiescence confirmation; each phase does not receive a fresh
timeout.
max_runtime=None is the compatibility default. It does not add a deadline to
the direct Ras.exe launcher wait. The existing 7,200-second bound for an
asynchronous RasUnsteady.exe solver that outlives its launcher remains in
place.
When an explicit deadline expires, ras-commander records the timeout and calls
only RasCmdr.cancel_plan_exact() for that initialized project and plan. It
does not fall back to a process-name kill. Opposing result-family artifacts are
finalized only when the structured cancellation or final process inspection
positively confirms exact-plan quiescence. An uncertain cancellation therefore
returns a failed ComputeResult and leaves potentially active artifacts
untouched. Exact-plan cancellation and terminal evidence collection can extend
the method's wall-clock return time beyond max_runtime; the deadline limits
the engine attempt, not the safety work needed to prove what remains running.
Plan-specific cancellation currently has qualified command signatures for the core steady and unsteady launchers/solvers. Sediment, quasi-unsteady, and water-quality engines are inventoried and make quiescence fail closed when they can be linked to the plan, but they are not signalled until their exact native command signatures are qualified. A timeout involving one of these engines can therefore return an uncertain, failed cancellation while leaving the process running for manual review.
ComputeResult.execution_details provides JSON-safe audit evidence, including:
max_runtime_secondsandruntime_timed_out;launch_details, with the exact command, working directory, executable path and SHA-256, project and plan paths, and launcher PID/create-time identity;launcher_returncode, which can remain nonzero when a delegated modern solver nevertheless produces a verified final HDF;failure_stage,failure_type, andfailure_detail;- structured
cancellation_details, including tri-state quiescence evidence; and artifact_preparation_cleanupandartifact_finalization_cleanup, which report the exact removed and already-missing target paths before and after execution; andresult_artifacts_finalizedplusartifact_finalization_failure, which keep a cleanup defect separate from an earlier timeout or execution failure.
Within either cleanup record, result_format is the result family targeted for
deletion—the opposing family—not the run's selected output format. On a
successful cleanup, removed_paths and missing_paths are disjoint and their
union is the complete plan-scoped target set. Both cleanup fields are None
when calculation is skipped before cleanup.
completion_verified is independent of overall ComputeResult.success. For
example, a complete HDF may verify and then result-family finalization may fail;
that outcome is success=False, completion_verified=True, and remains safe to
inspect as a failed execution receipt when exact-plan quiescence is proven.
Advanced supervisors can implement the optional duck-typed
on_exec_launched(plan_number, launch_details) callback to persist the launch
identity immediately after it is captured. This additive hook does not change
the exported ExecutionCallback protocol.
Sequential Execution¶
Run multiple plans in order using RasCmdr.compute_test_mode(). Plans execute in a copy of the project.
results = RasCmdr.compute_test_mode(
plan_number=["01", "02", "03"],
dest_folder_suffix="[Test]"
)
for plan, success in results.items():
print(f"Plan {plan}: {'OK' if success else 'FAILED'}")
When to Use¶
- Plans have dependencies (e.g., plan 02 needs results from 01)
- Controlled resource usage is needed
- Debugging complex multi-plan workflows
Parameters¶
| Parameter | Type | Description |
|---|---|---|
plan_number |
list | Plans to run in order |
dest_folder_suffix |
str | Suffix for test folder name |
clear_geompre |
bool | Clear preprocessor before each plan |
num_cores |
int | Cores per plan |
overwrite_dest |
bool | Overwrite test folder |
Parallel Execution¶
Run multiple independent plans simultaneously using RasCmdr.compute_parallel(). Creates temporary worker folders.
results = RasCmdr.compute_parallel(
plan_number=["01", "02", "03"],
max_workers=3,
num_cores=2,
dest_folder="/results/parallel_run"
)
Resource Optimization¶
Balance workers and cores based on your system:
import psutil
# Calculate optimal configuration
physical_cores = psutil.cpu_count(logical=False)
cores_per_worker = 2
max_workers = physical_cores // cores_per_worker
# Also consider RAM (each HEC-RAS instance needs 2-4GB+)
available_ram_gb = psutil.virtual_memory().available / (1024**3)
ram_limited_workers = int(available_ram_gb // 4)
# Use the more restrictive limit
optimal_workers = min(max_workers, ram_limited_workers)
results = RasCmdr.compute_parallel(
plan_number=["01", "02", "03", "04"],
max_workers=optimal_workers,
num_cores=cores_per_worker
)
Parameters¶
| Parameter | Type | Description |
|---|---|---|
plan_number |
list | Plans to run concurrently |
max_workers |
int | Maximum parallel HEC-RAS instances |
num_cores |
int | Cores assigned to each worker |
dest_folder |
str/Path | Final results location |
clear_geompre |
bool | Clear preprocessor in worker folders |
overwrite_dest |
bool | Overwrite destination folder |
How It Works¶
- Creates temporary worker folders (copies of project)
- Assigns plans to workers
- Executes plans in parallel
- Consolidates results to destination folder
- Cleans up worker folders
Execution Mode Comparison¶
| Feature | Single | Sequential | Parallel |
|---|---|---|---|
| Speed | Fast (1 plan) | Moderate | Fastest (many plans) |
| Resource Usage | Low | Low | High |
| Dependencies | N/A | Supported | Not supported |
| Disk Space | Low | Medium | High (temp folders) |
| Use Case | Testing, debugging | Dependent plans | Batch processing |
Plan Modification Before Execution¶
Modify plan parameters programmatically before running:
from ras_commander import RasPlan, RasCmdr
# Clone and modify
new_plan = RasPlan.clone_plan("01", new_plan_shortid="Modified Run")
# Change parameters
RasPlan.set_num_cores(new_plan, 4)
RasPlan.set_computation_interval(new_plan, "5MIN")
RasPlan.set_description(new_plan, "Run with finer timestep")
# Execute modified plan
success = RasCmdr.compute_plan(new_plan)
Native Flow Hydrograph Optimization¶
HEC-RAS native automated flow optimization scales selected flow hydrographs until a stage or flow target is met at a reference point or line. RAS Commander exposes this through RasFlowOptimization while keeping execution inside RasCmdr.
from ras_commander import init_ras_project, RasFlowOptimization, RasCmdr
init_ras_project("/path/to/tutorial-13-project", "6.5")
# Copy a plan and enable native HEC-RAS flow optimization on the copy.
opt_plan = RasFlowOptimization.copy_plan_with_optimization(
"01",
new_plan_shortid="Native Flow Opt",
mode="stage",
reference_location="Yosemite Falls Vantage Point",
target_value=3963.5,
tolerance=0.1,
hydrographs=["BCLine: Inflow"],
min_ratio=0.5,
max_ratio=1.0,
max_iterations=10,
)
# Execute through RAS Commander, not a direct Ras.exe call.
success = RasCmdr.compute_plan(opt_plan)
# After compute, read native trial output from the plan HDF or compute messages.
trials = RasFlowOptimization.get_trial_results(opt_plan)
print(trials[["trial", "ratio", "difference", "target", "computed"]])
Use RasFlowOptimization.get_settings() to audit an existing plan and RasFlowOptimization.list_flow_hydrographs() to discover flow hydrographs that can be selected for scaling. RasFlowOptimization.compute_plan_and_get_trials() is a convenience wrapper around RasCmdr.compute_plan() followed by trial extraction. See examples/301_flow_hydrograph_optimization.ipynb for the Tutorial 13 workflow and fallback guidance.
RasCalibrate remains the ras-commander-native calibration/search API for broader parameter sweeps, arbitrary model edits, and custom objective metrics. Native flow optimization is narrower: it delegates HEC-RAS' built-in flow-ratio trial loop to HEC-RAS and reports the resulting trial table where available. See the official HEC-RAS flow hydrograph optimization tutorial for the corresponding GUI workflow.
Checking Results¶
After execution, verify results were generated and the run completed without errors. This is critical for determining if the simulation succeeded.
Structured Mechanical Evidence¶
Use inspect_execution_evidence() when automation must distinguish an
inspected false value from an unavailable, uninspected, or unreadable channel.
The method is read-only: it does not run HEC-RAS, preprocess the plan, or open
the legacy COM controller.
from ras_commander import RasCmdr
evidence = RasCmdr.inspect_execution_evidence(
"01",
ras_object=ras,
hash_files=True,
)
completion = evidence.mechanical_completion
print(completion.state, completion.value, completion.reason_code)
errors = evidence.observations["message_error_count"]
print(errors.state, errors.value, errors.source_locator)
The four observation states have distinct meanings:
availablemeans inspection produced a value, includingFalseor zero.not_available_in_versionis used only for a positively established producer-version limitation.not_inspectedmeans no trustworthy observation was possible or requested.failedmeans inspection was attempted but the source was unreadable, malformed, unstable, or contradictory.
If result_modified_after is supplied, pass a timezone-aware datetime so
the freshness comparison is reproducible across machines. Message-health
counts prefer embedded HDF messages and fall back to stored messages; the two
completion-message observations remain separate.
Result-family selection reads Program Version= from the current .p## bytes,
not from cached plan_df metadata. When only one result family exists, that
artifact is selected even if its family differs from the declaration; the
evidence records unexpected_result_format. This supports a copied legacy plan
that was cleanly rerun by a newer engine without rewriting its declaration.
When both .p##.hdf and .O## exist, the inspector applies stricter rules:
| Plan declaration | Selection |
|---|---|
HEC-RAS 5 or newer, .O## timestamp after HDF |
Raise ResultArtifactAmbiguityError. |
HEC-RAS 5 or newer, .O## timestamp equal to or before HDF |
Select HDF, warn, and record multiple_result_formats_present. Legacy output does not contribute to the selected evidence. |
HEC-RAS 4 or older, HDF timestamp after .O## |
Raise ResultArtifactAmbiguityError. The timestamp is only a conservative ambiguity trigger, not proof of run chronology. |
HEC-RAS 4 or older, HDF timestamp equal to or before .O## |
Select .O##, warn, and record multiple_result_formats_present. HDF completion and runtime do not contribute to the selected evidence. |
| Missing or unreadable declaration | Raise ResultArtifactAmbiguityError. |
flowchart TD
A["Inspect existing project"] --> B{"Both HDF and .O##?"}
B -- "No" --> C["Read sole existing format<br/>record a conflict if unexpected"]
B -- "Yes" --> D{"Declared plan family"}
D -- "HEC-RAS 5+" --> E{"HDF mtime >= .O## mtime?"}
E -- "Yes" --> F["Select HDF<br/>warn about ignored .O##"]
E -- "No" --> G["Raise ResultArtifactAmbiguityError"]
D -- "HEC-RAS 4 or older" --> H{".O## mtime >= HDF mtime?"}
H -- "Yes" --> I["Select .O##<br/>warn about ignored HDF"]
H -- "No" --> G
D -- "Unresolved" --> G
Copied-folder timestamps can be misleading. The error therefore asks the user to resolve the formats rather than claiming which computation is newest.
An actual ras-commander computation does not use those timestamps to decide
what to remove. It normalizes artifacts using the selected HEC-RAS executable
or controller, not the plan declaration: HEC-RAS 5+ runs preserve HDF and
remove .O##; legacy runs preserve .O## and remove the plan HDF. Cleanup is
coupled to the real launch after skip decisions, plan preparation, callbacks,
watchdog startup, and log creation have succeeded. It runs again after every
launched attempt once solver completion or termination is confirmed, because
modern HEC-RAS 1D engines recreate .O## during computation. If solver
quiescence cannot be confirmed, the run fails and leaves the opposing artifact
visible rather than racing an active writer. Remote PsExec staging is also
retained when completion is unconfirmed. Skipped runs do not change plan bytes
or delete result artifacts.
flowchart TD
A["Resolve selected executable or controller"] --> B{"Engine family reliable?"}
B -- "No or conflicting" --> C["Fail without deleting results"]
B -- "Yes" --> D{"Skip calculation?"}
D -- "Yes" --> E["Return without changing<br/>plan bytes or results"]
D -- "No" --> F["Finish plan preparation,<br/>callbacks, watchdog, and log setup"]
F --> G{"Selected engine family"}
G -- "HEC-RAS 5+" --> H["Remove exact .O##<br/>and stale messages"]
G -- "HEC-RAS 3-4" --> I["Remove exact plan HDF<br/>and stale messages"]
H --> J["Launch calculation"]
I --> J
J --> K["Wait for launcher, solver children,<br/>and temporary HDF state"]
K --> L{"Solver quiescence confirmed?"}
L -- "No" --> M["Fail and preserve visible conflict<br/>and remote staging"]
L -- "Yes" --> N["Remove any opposing result<br/>recreated during execution"]
N --> O["Return normalized result set"]
For command-line execution, a versioned ras_exe_path is authoritative and
ras_version is the fallback for an unversioned executable name. If both are
resolvable but imply different result families, automatic cleanup fails closed
without deleting either family. RasControl.run_plan() instead follows the
version of the selected COM controller. An unversioned Ras.exe path with no
resolvable version is not assumed to be modern, because that guess could
permanently remove a valid legacy result.
This automatic normalization belongs to full plan-calculation APIs.
RasPreprocess.preprocess_plan() and
GeomPreprocessor.run_geometry_preprocessor() are preflight/preprocessing
operations that attempt to stop before hydraulic computation; they do not
delete either family of final results and must not be used to normalize an
ambiguous project.
To resolve an existing project manually, explicitly select the family to remove:
from ras_commander import RasCmdr
# Permanent, exact plan-scoped removal. Geometry HDF, DSS, terrain, and
# .p##.tmp.hdf preprocessing files are never included.
cleanup = RasCmdr.remove_plan_execution_artifacts(
"01",
result_format="legacy", # or "hdf" / "both"
include_message_sidecars=False,
ras_object=ras,
)
print(cleanup.removed_paths)
The helper validates its complete, project-contained target list before the
first deletion. If an operating-system error occurs during deletion,
PlanExecutionCleanupError.cleanup records any paths already removed and
failed_path identifies the file that could not be removed.
Alternatively, rerun with the intended HEC-RAS version through
RasCmdr.compute_plan() or RasControl.run_plan(); the execution path removes
the opposing result family and stale compute-message sidecars.
Mechanical completion is deliberately independent from message errors, warnings, result freshness, volume accounting, convergence quality, and hydraulic acceptance. Review those observations separately.
For native Linux unsteady runs, an HDF Event Conditions completion attribute
can be inherited from the prepared .tmp.hdf. An available / True
observation from that attribute describes the file; it does not establish that
the subsequent native calculation finished. Native results may also omit the
Windows Complete Process message. Use the native execution API's solver-log
and populated-result checks, then verify the requested output time window,
array dimensions, and finite values. Keep the structured observations alongside
these checks rather than using the inherited attribute as the acceptance gate.
Quick Verification¶
from ras_commander import init_ras_project, ras, HdfResultsPlan
# Refresh project data to see new results
init_ras_project(ras.project_folder, "6.5")
# Check for HDF results
hdf_entries = ras.get_hdf_entries()
print(f"Found {len(hdf_entries)} HDF result files")
Check Compute Messages for Errors¶
The compute messages contain the HEC-RAS log output. Always check for errors:
from ras_commander import HdfResultsPlan
# Get computation messages (accepts plan number or HDF path)
msgs = HdfResultsPlan.get_compute_messages("01")
if msgs:
# Check for error keywords
if 'ERROR' in msgs.upper() or 'FAILED' in msgs.upper():
print("ERRORS DETECTED in computation!")
# Print error lines
for line in msgs.split('\n'):
if 'ERROR' in line.upper() or 'FAILED' in line.upper():
print(f" {line}")
else:
print("Computation completed without errors")
else:
print("No compute messages - run may not have completed")
Check Volume Accounting¶
Volume accounting verifies mass conservation. A large imbalance indicates problems:
from ras_commander import HdfResultsPlan
volume_df = HdfResultsPlan.get_volume_accounting("01")
if volume_df is not None:
print("Volume Accounting Data:")
# Display as transposed for readability
print(volume_df.T)
else:
print("No volume accounting - run may have failed")
Check Unsteady Results Exist¶
Verify unsteady results were written to the HDF:
from ras_commander import HdfResultsPlan
# Basic unsteady info
try:
info = HdfResultsPlan.get_unsteady_info("01")
print("Unsteady results present")
print(info.T)
except KeyError:
print("No unsteady results found")
# Detailed unsteady summary
try:
summary = HdfResultsPlan.get_unsteady_summary("01")
print("\nUnsteady Summary:")
print(summary.T)
except KeyError:
print("No unsteady summary - check if run completed")
Check Runtime Performance¶
Review computation timing and performance:
from ras_commander import HdfResultsPlan
runtime = HdfResultsPlan.get_runtime_data("01")
if runtime is not None:
print(f"Plan: {runtime['Plan Name'].iloc[0]}")
print(f"Simulation: {runtime['Simulation Time (hr)'].iloc[0]:.1f} hr")
print(f"Compute Time: {runtime['Complete Process (hr)'].iloc[0]:.3f} hr")
print(f"Speed: {runtime['Complete Process Speed (hr/hr)'].iloc[0]:.0f}x realtime")
Complete Verification Example¶
from ras_commander import init_ras_project, HdfResultsPlan
init_ras_project("/path/to/project", "6.5")
def check_run_success(plan_number):
"""Check if a plan run was successful."""
print(f"\n{'='*50}")
print(f"Verifying Plan {plan_number}")
print('='*50)
success = True
# 1. Check compute messages
msgs = HdfResultsPlan.get_compute_messages(plan_number)
if msgs:
has_errors = any(kw in msgs.upper()
for kw in ['ERROR', 'FAILED', 'UNSTABLE'])
if has_errors:
print("[FAIL] Errors found in compute messages")
success = False
else:
print("[OK] No errors in compute messages")
else:
print("[FAIL] No compute messages found")
success = False
# 2. Check volume accounting
volume = HdfResultsPlan.get_volume_accounting(plan_number)
if volume is not None:
print("[OK] Volume accounting data present")
else:
print("[WARN] No volume accounting data")
# 3. Check unsteady results
try:
HdfResultsPlan.get_unsteady_summary(plan_number)
print("[OK] Unsteady results present")
except:
print("[WARN] No unsteady summary")
# 4. Runtime data
runtime = HdfResultsPlan.get_runtime_data(plan_number)
if runtime is not None:
speed = runtime['Complete Process Speed (hr/hr)'].iloc[0]
print(f"[INFO] Compute speed: {speed:.0f}x realtime")
print(f"\nOverall: {'SUCCESS' if success else 'NEEDS REVIEW'}")
return success
# Usage
check_run_success("01")
See Workflows and Patterns for more detailed verification patterns including batch verification.
Detailed Compute Message Logging¶
Monitor HEC-RAS execution in real-time with stream callbacks. This provides live feedback during computation and enables automated error detection.
Stream Callbacks¶
The stream_callback parameter accepts callback objects for real-time monitoring:
from ras_commander import RasCmdr
from ras_commander.callbacks import ConsoleCallback
# Monitor execution with console output
success = RasCmdr.compute_plan(
"01",
stream_callback=ConsoleCallback(verbose=True)
)
Available Callbacks¶
from ras_commander.callbacks import (
ConsoleCallback, # Print to console
FileLoggerCallback, # Log to file
ProgressBarCallback, # Show progress bar (requires tqdm)
SynchronizedCallback # Thread-safe wrapper for parallel execution
)
# Console callback
RasCmdr.compute_plan("01", stream_callback=ConsoleCallback(verbose=True))
# File logger callback
RasCmdr.compute_plan("01", stream_callback=FileLoggerCallback("run.log"))
# Progress bar (requires: pip install tqdm)
RasCmdr.compute_plan("01", stream_callback=ProgressBarCallback())
Custom Callbacks¶
Create custom callbacks for specialized monitoring:
from ras_commander.callbacks import ExecutionCallback
class ErrorDetectionCallback(ExecutionCallback):
"""Callback that stops execution on first error."""
def on_exec_message(self, message):
# Check for error keywords
if any(kw in message.upper() for kw in ['ERROR', 'FAILED', 'UNSTABLE']):
print(f"❌ ERROR DETECTED: {message}")
# Could raise exception, send alert, etc.
elif 'warning' in message.lower():
print(f"⚠ WARNING: {message}")
else:
# Normal message
print(f"ℹ {message}")
# Use custom callback
RasCmdr.compute_plan("01", stream_callback=ErrorDetectionCallback())
Callback Methods¶
Custom callbacks can implement these methods:
class MyCallback(ExecutionCallback):
def on_start(self, plan_number):
"""Called when execution starts."""
print(f"Starting plan {plan_number}")
def on_exec_message(self, message):
"""Called for each HEC-RAS message during execution."""
print(f"HEC-RAS: {message}")
def on_complete(self, success):
"""Called when execution completes."""
if success:
print("✓ Execution completed successfully")
else:
print("✗ Execution failed")
def on_error(self, error):
"""Called if exception occurs."""
print(f"Exception: {error}")
Parallel Execution with Callbacks¶
Use SynchronizedCallback wrapper for thread-safe logging:
from ras_commander.callbacks import ConsoleCallback, SynchronizedCallback
# Wrap callback for thread safety
safe_callback = SynchronizedCallback(ConsoleCallback(verbose=True))
# Use with parallel execution
results = RasCmdr.compute_parallel(
plan_number=["01", "02", "03"],
max_workers=3,
stream_callback=safe_callback # Thread-safe logging
)
Post-Execution Message Review¶
Review compute messages after execution completes:
from ras_commander import HdfResultsPlan
# Get all compute messages
msgs = HdfResultsPlan.get_compute_messages("01")
# Parse for specific information
for line in msgs.split('\n'):
if 'time step' in line.lower():
print(line) # Timestep information
elif 'iterations' in line.lower():
print(line) # Iteration counts
elif 'error' in line.lower():
print(f"⚠ {line}") # Errors
Error Handling¶
from ras_commander import RasCmdr
import logging
# Enable debug logging
logging.getLogger('ras_commander').setLevel(logging.DEBUG)
try:
success = RasCmdr.compute_plan("01")
if not success:
print("Plan execution failed - check HEC-RAS logs")
except FileNotFoundError as e:
print(f"Plan file not found: {e}")
except ValueError as e:
print(f"Invalid parameter: {e}")
Best Practices¶
- Test first: Use
compute_plan()withdest_folderto test without modifying original - Monitor resources: Watch CPU and RAM during parallel execution
- Clear preprocessor: Use
clear_geompre=Trueafter geometry changes - Check return values: Always verify execution success
- Use logging: Enable DEBUG level for troubleshooting