# Using Commercial Solvers (CPLEX / Gurobi) By default, EMHASS uses the high-performance open-source solver **HiGHS**. It is bundled with the default Docker image and requires no configuration. If you possess a license for a commercial solver like **Gurobi** or **CPLEX**, you can enable them by building a custom Docker image. ```{note} This functionality is intended for advanced users. This will assume using the Docker Standalone installation method. See [Installation methods](installation_methods) section for more details. ``` ## 1. Create a Custom Dockerfile Create a file named `Dockerfile.custom` that extends the official EMHASS image. **For Gurobi:** ```dockerfile FROM davidusb-geek/emhass:latest # Install the Gurobi Python interface RUN uv pip install gurobipy # Set the solver environment variable ENV LP_SOLVER=GUROBI ``` **For CPLEX:** ```dockerfile FROM davidusb-geek/emhass:latest # Install the CPLEX Python interface RUN uv pip install cplex # Set the solver environment variable ENV LP_SOLVER=CPLEX ``` ## 2. Build the Image Build your custom image locally: ```bash docker build -t emhass-custom -f Dockerfile.custom . ``` ## 3. Run with License Mounting When running the container, you must mount your license file to the location expected by the solver. **Example for Gurobi:** Assuming your license file is at `/home/user/gurobi.lic`: ```bash docker run -d \ --name emhass \ -p 5000:5000 \ -v /home/user/config_emhass.json:/app/config_emhass.json \ -v /home/user/gurobi.lic:/opt/gurobi/gurobi.lic \ <-- Mount License -e GRB_LICENSE_FILE=/opt/gurobi/gurobi.lic \ <-- Tell Gurobi where it is emhass-custom ``` **Note:** You do not need to change any configuration in `config.json` or the web UI. The `LP_SOLVER` environment variable handles the switch automatically. ## Optimization Performance Tuning EMHASS includes several features to improve optimization performance, especially for complex configurations with many deferrable loads and battery systems. ### Object Caching & Warm Start EMHASS automatically caches the optimization problem structure between runs. When the system configuration hasn't changed, subsequent optimizations reuse the cached problem structure and warm-start from the previous solution. This can significantly speed up repeated optimizations (e.g., during MPC operation). The cache is automatically invalidated when: - Number of deferrable loads changes - Battery configuration changes - Thermal load configuration changes - Prediction horizon changes No configuration is required—caching works automatically. ### MIP Gap Tolerance For Mixed-Integer Programming problems (which occur when using semi-continuous loads, single-constant loads, or startup penalties), the solver can spend significant time proving a solution is exactly optimal. The `lp_solver_mip_rel_gap` parameter allows the solver to stop earlier when a "good enough" solution is found. **Configuration:** ```yaml # In config.yaml or config.json lp_solver_mip_rel_gap: 0.05 # Stop when within 5% of optimal ``` **Valid range:** 0 to 1 (representing 0% to 100% gap tolerance). Values outside this range are clamped automatically. **Recommended values:** | Value | Description | Use Case | |-------|-------------|----------| | 0 | Exact optimal (default) | When precision is critical | | 0.05 | Within 5% of optimal | **Recommended** for most users - ~2x speedup | | 0.10 | Within 10% of optimal | Fast solving, good for testing | | 0.20 | Within 20% of optimal | Very fast, adequate for simple decisions | **Benchmarks show:** - 5% gap: ~1.75x speedup - 10% gap: ~1.86x speedup - 20% gap: ~2.89x speedup For home energy optimization, a 5% gap is typically imperceptible in practice—the difference between "optimal" and "within 5% of optimal" is usually smaller than forecast uncertainty. **Note:** This parameter only affects problems with binary variables. Pure linear problems (continuous loads only, no battery) are unaffected. ## Tuning for Low-Power Hardware (Raspberry Pi, low-end NUC, etc.) When EMHASS runs on a Raspberry Pi 4 / 5, an Intel NUC, or similar low-power hardware, several defaults can be tuned to keep the MPC cycle responsive without changing what gets optimised. All settings below are **quality-neutral** unless explicitly noted otherwise. The advice below assumes the default HiGHS solver (`lp_solver: HiGHS`) and a home-sized problem (handful of deferrable loads, 24–48 h horizon). With a different solver backend or a much larger problem, the specific numeric recommendations (e.g. `num_threads: 2`) may not apply directly — the principle of "leave headroom for HA and the OS" still does. ### Quick preset In `config.yaml` (or via the web UI): ```yaml # Low-power deployment preset lp_solver_timeout: 60 # plenty of headroom; never time out on real workloads num_threads: 2 # leave one core free for HA / publishing ``` Then, depending on whether your problem has binary variables (semi-continuous loads, single-constant loads, startup penalties): - **Pure-LP setups (no binaries)** — leave defaults; HiGHS' simplex is already fast on a Pi. - **MILP setups** — consider `lp_solver_mip_rel_gap: 0.05` (see the MIP-gap section above for the quality trade-off). ### Why the threads setting matters HiGHS' default behaviour (`num_threads: 0`) runs the simplex single-threaded and uses HiGHS' internal parallel scheduler for the MIP branch-and-bound. On a 4-core Pi 4 with no other heavy workloads, `num_threads: 2` is typically the sweet spot: - More than half the cores helps the B&B but doesn't help simplex. - Fewer than all cores leaves room for Home Assistant, EMHASS' own publish path, and the OS so optimisation doesn't preempt other realtime work. On boxes with 8+ cores, multi-threading can actually **hurt** small MILPs because of coordination overhead — measure on your specific shape before turning it up. ### Other settings worth checking - `prediction_horizon`: on a Pi, 48 h × 30 min steps (96 steps) is roughly the comfort threshold. 24 h × 5 min (288) is also reasonable. 48 h × 5 min (576) is workable but pay close attention to MPC cycle wall-clock — start with `lp_solver_timeout: 120` and decrease once you've measured. - `weather_forecast_method: open-meteo` with the built-in 30-min cache is cheap. Solcast adds an HTTP roundtrip per cycle; the cache helps but a slow WAN link from a Pi can still cost a second or two each tick. - `historic_days_to_retrieve`: each day fetched is one HA recorder call per sensor. Default 2 days is fine; bumping to 5+ for a long-window forecast model can add real time on a Pi behind cellular/Tailscale.