.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "auto_examples/figure_generation/plot_physics_sensitivity.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_auto_examples_figure_generation_plot_physics_sensitivity.py: Physics sensitivity: learning how lambda choices reshape the physics diagnostics ================================================================================= This example teaches you how to read the GeoPrior physics- sensitivity figure. This figure is not about maps. It is about *control knobs*. In GeoPrior, two of the most important physics weights are: - :math:`\lambda_{\mathrm{prior}}` - :math:`\lambda_{\mathrm{cons}}` The physics-sensitivity figure asks a very practical question: **If we move around in this lambda plane, how do the key physics diagnostics respond?** That is useful because many training workflows eventually reach a point where the user asks: - Should I increase the prior pressure? - Should I strengthen the consolidation term? - Is there a stable region in parameter space rather than a single lucky point? What this figure shows ---------------------- The plotting backend builds a two-row figure over the :math:`(\lambda_{\mathrm{prior}}, \lambda_{\mathrm{cons}})` plane. - Row 1 shows the chosen prior-side metric, usually :math:`\epsilon_{\mathrm{prior}}`. - Row 2 shows the chosen consolidation-side metric, usually :math:`\epsilon_{\mathrm{cons}}`. One column is drawn per city, and each row uses a shared colorbar across cities so comparisons remain fair. The script supports three rendering modes: - ``heatmap`` - ``tricontour`` - ``pcolormesh`` It can also: - filter rows by city, - filter by model name, - filter by ``pde_mode``, - mark the best point, - and draw a trend arrow showing the local improvement direction in parameter space. In this lesson, we will make a compact synthetic ablation table, run the real CLI backend on it, and then interpret the result. .. GENERATED FROM PYTHON SOURCE LINES 62-67 Imports ------- We use the real plotting entrypoint from the project code. This gallery page is therefore a teaching wrapper around the actual plotting command. .. GENERATED FROM PYTHON SOURCE LINES 67-82 .. code-block:: Python from __future__ import annotations import tempfile from pathlib import Path import matplotlib.image as mpimg import matplotlib.pyplot as plt import numpy as np import pandas as pd from geoprior.scripts.plot_physics_sensitivity import ( plot_physics_sensitivity_main, ) .. GENERATED FROM PYTHON SOURCE LINES 83-100 Step 1 - Build a synthetic lambda grid -------------------------------------- We create a small parameter grid over two physics weights: - lambda_prior - lambda_cons The synthetic data are not meant to be "the true model". They are meant to teach the user how to read the figure. We will create two cities: - Nansha - Zhongshan and let each city have a slightly different optimum region in the lambda plane. .. GENERATED FROM PYTHON SOURCE LINES 100-104 .. code-block:: Python lambda_prior_vals = np.array([0.0, 0.1, 0.3, 1.0, 3.0]) lambda_cons_vals = np.array([0.0, 0.1, 0.3, 1.0, 3.0]) .. GENERATED FROM PYTHON SOURCE LINES 105-130 Step 2 - Create a compact synthetic ablation table -------------------------------------------------- The real script accepts explicit input files in .csv / .json / .jsonl format. We therefore create a tidy CSV that looks like a lightweight ablation table. Important columns for this figure are: - city - model - pde_mode - lambda_prior - lambda_cons - epsilon_prior - epsilon_cons The script can also work with other metrics such as coverage80, sharpness80, r2, mae, mse, rmse, and pss. Here we shape the synthetic surfaces so that: - Nansha prefers a moderate prior weight and moderate consolidation weight, - Zhongshan prefers a slightly stronger prior weight, - and both metrics are lower-is-better. .. GENERATED FROM PYTHON SOURCE LINES 130-203 .. code-block:: Python rows: list[dict[str, float | str]] = [] for city in ("Nansha", "Zhongshan"): for lp in lambda_prior_vals: for lc in lambda_cons_vals: if city == "Nansha": eps_prior = ( 0.28 + 0.12 * (np.log10(lp + 0.12) + 0.45) ** 2 + 0.05 * (np.log10(lc + 0.12) + 0.20) ** 2 + 0.02 * np.sin(1.8 * lp + 0.8 * lc) ) eps_cons = ( 0.22 + 0.05 * (np.log10(lp + 0.12) + 0.10) ** 2 + 0.11 * (np.log10(lc + 0.12) + 0.40) ** 2 + 0.02 * np.cos(1.4 * lc + 0.5 * lp) ) else: eps_prior = ( 0.32 + 0.13 * (np.log10(lp + 0.12) + 0.10) ** 2 + 0.06 * (np.log10(lc + 0.12) + 0.30) ** 2 + 0.03 * np.sin(1.2 * lp + 0.4 * lc) ) eps_cons = ( 0.26 + 0.04 * (np.log10(lp + 0.12) + 0.30) ** 2 + 0.13 * (np.log10(lc + 0.12) + 0.05) ** 2 + 0.02 * np.cos(1.7 * lc + 0.6 * lp) ) coverage80 = np.clip( 0.92 - 0.10 * eps_prior - 0.08 * eps_cons, 0.65, 0.98, ) sharpness80 = 18.0 + 14.0 * eps_cons r2 = np.clip( 0.94 - 0.55 * eps_prior - 0.35 * eps_cons, 0.15, 0.95, ) mae = 4.0 + 8.5 * eps_cons mse = 20.0 + 18.0 * eps_prior + 10.0 * eps_cons rmse = np.sqrt(mse) pss = 0.04 + 0.20 * eps_cons rows.append( { "city": city, "model": "GeoPriorSubsNet", "pde_mode": "both", "lambda_prior": float(lp), "lambda_cons": float(lc), "epsilon_prior": float(eps_prior), "epsilon_cons": float(eps_cons), "coverage80": float(coverage80), "sharpness80": float(sharpness80), "r2": float(r2), "mae": float(mae), "mse": float(mse), "rmse": float(rmse), "pss": float(pss), } ) df = pd.DataFrame(rows) df.head() .. raw:: html
city model pde_mode lambda_prior lambda_cons epsilon_prior epsilon_cons coverage80 sharpness80 r2 mae mse rmse pss
0 Nansha GeoPriorSubsNet both 0.0000 0.0000 0.3326 0.3035 0.8625 22.2493 0.6508 6.5800 29.0217 5.3872 0.1007
1 Nansha GeoPriorSubsNet both 0.0000 0.1000 0.3187 0.2808 0.8657 21.9311 0.6665 6.3867 28.5439 5.3427 0.0962
2 Nansha GeoPriorSubsNet both 0.0000 0.3000 0.3129 0.2720 0.8669 21.8081 0.6727 6.3121 28.3526 5.3247 0.0944
3 Nansha GeoPriorSubsNet both 0.0000 1.0000 0.3241 0.2793 0.8653 21.9100 0.6640 6.3739 28.6258 5.3503 0.0959
4 Nansha GeoPriorSubsNet both 0.0000 3.0000 0.3442 0.3318 0.8590 22.6456 0.6345 6.8205 29.5139 5.4327 0.1064


.. GENERATED FROM PYTHON SOURCE LINES 204-210 Step 3 - Inspect the best points directly ----------------------------------------- Before plotting, it is useful to compute the best points in the table itself. Since epsilon_prior and epsilon_cons are both lower-is-better diagnostics, we locate their minima for each city. The plotting backend will also highlight the best point. .. GENERATED FROM PYTHON SOURCE LINES 210-239 .. code-block:: Python best_rows: list[dict[str, float | str]] = [] for city, sub in df.groupby("city", sort=True): i1 = int(sub["epsilon_prior"].idxmin()) i2 = int(sub["epsilon_cons"].idxmin()) best_rows.append( { "city": city, "best_for": "epsilon_prior", "lambda_prior": float(df.loc[i1, "lambda_prior"]), "lambda_cons": float(df.loc[i1, "lambda_cons"]), "metric_value": float(df.loc[i1, "epsilon_prior"]), } ) best_rows.append( { "city": city, "best_for": "epsilon_cons", "lambda_prior": float(df.loc[i2, "lambda_prior"]), "lambda_cons": float(df.loc[i2, "lambda_cons"]), "metric_value": float(df.loc[i2, "epsilon_cons"]), } ) best_df = pd.DataFrame(best_rows) print(best_df.to_string(index=False)) .. rst-class:: sphx-glr-script-out .. code-block:: none city best_for lambda_prior lambda_cons metric_value Nansha epsilon_prior 0.1000 0.3000 0.2949 Nansha epsilon_cons 3.0000 0.3000 0.2309 Zhongshan epsilon_prior 0.3000 0.3000 0.3442 Zhongshan epsilon_cons 1.0000 1.0000 0.2528 .. GENERATED FROM PYTHON SOURCE LINES 240-245 Step 4 - Save the synthetic table --------------------------------- The real script supports explicit input files, so we write the table to CSV and then call the true plotting entrypoint exactly as a user would. .. GENERATED FROM PYTHON SOURCE LINES 245-254 .. code-block:: Python tmp_dir = Path( tempfile.mkdtemp(prefix="gp_sg_phys_sens_") ) csv_path = tmp_dir / "ablation_sensitivity.csv" df.to_csv(csv_path, index=False) print(f"Input table written to: {csv_path}") .. rst-class:: sphx-glr-script-out .. code-block:: none Input table written to: /tmp/gp_sg_phys_sens_jpmn99jx/ablation_sensitivity.csv .. GENERATED FROM PYTHON SOURCE LINES 255-268 Step 5 - Run the real plotting command -------------------------------------- We call the real command entrypoint with: - one explicit CSV input, - epsilon_prior for row 1, - epsilon_cons for row 2, - pcolormesh rendering, - visible sample points, - and trend arrows turned on. The script also writes a copy of the used tidy table next to the figure outputs. .. GENERATED FROM PYTHON SOURCE LINES 268-299 .. code-block:: Python plot_physics_sensitivity_main( [ "--input", str(csv_path), "--metric-prior", "epsilon_prior", "--metric-cons", "epsilon_cons", "--render", "pcolormesh", "--models", "GeoPriorSubsNet", "--pde-modes", "both", "--trend-arrow", "true", "--show-points", "true", "--show-title", "true", "--show-legend", "true", "--out-dir", str(tmp_dir), "--out", "physics_sensitivity_gallery", ], prog="plot-physics-sensitivity", ) .. image-sg:: /auto_examples/figure_generation/images/sphx_glr_plot_physics_sensitivity_001.png :alt: Supplement S7 • Physics sensitivity ($\epsilon_{\mathrm{prior}}$ and $\epsilon_{\mathrm{cons}}$ vs. $\lambda_{\mathrm{prior}}, \lambda_{\mathrm{cons}}$), Nansha — $\epsilon_{\mathrm{prior}}$, Zhongshan — $\epsilon_{\mathrm{prior}}$, Nansha — $\epsilon_{\mathrm{cons}}$, Zhongshan — $\epsilon_{\mathrm{cons}}$ :srcset: /auto_examples/figure_generation/images/sphx_glr_plot_physics_sensitivity_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none [OK] table -> /tmp/gp_sg_phys_sens_jpmn99jx/tableS7_physics_used.csv [OK] figs -> /tmp/gp_sg_phys_sens_jpmn99jx/physics_sensitivity_gallery.png | /tmp/gp_sg_phys_sens_jpmn99jx/physics_sensitivity_gallery.pdf .. GENERATED FROM PYTHON SOURCE LINES 300-305 Step 6 - Display the saved figure in the gallery page ----------------------------------------------------- The plotting script writes PNG and PDF outputs. To ensure the figure is visible directly inside Sphinx-Gallery, we reload the PNG and display it here. .. GENERATED FROM PYTHON SOURCE LINES 305-314 .. code-block:: Python img = mpimg.imread( tmp_dir / "physics_sensitivity_gallery.png" ) fig, ax = plt.subplots(figsize=(7.2, 5.8)) ax.imshow(img) ax.axis("off") .. image-sg:: /auto_examples/figure_generation/images/sphx_glr_plot_physics_sensitivity_002.png :alt: plot physics sensitivity :srcset: /auto_examples/figure_generation/images/sphx_glr_plot_physics_sensitivity_002.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none (np.float64(-0.5), np.float64(4999.5), np.float64(4049.5), np.float64(-0.5)) .. GENERATED FROM PYTHON SOURCE LINES 315-323 Step 7 - Read the table that the script actually used ----------------------------------------------------- The backend writes a table named: tableS7_physics_used.csv That is helpful because it shows the exact rows that survived canonicalization, filtering, unit harmonization, and deduping. .. GENERATED FROM PYTHON SOURCE LINES 323-336 .. code-block:: Python used_df = pd.read_csv( tmp_dir / "tableS7_physics_used.csv" ) print("") print("Used-table summary") print( used_df.groupby("city")[["epsilon_prior", "epsilon_cons"]] .agg(["min", "max", "mean"]) .round(4) ) .. rst-class:: sphx-glr-script-out .. code-block:: none Used-table summary epsilon_prior epsilon_cons min max mean min max mean city Nansha 0.2949 0.4310 0.3334 0.2309 0.3423 0.2711 Zhongshan 0.3442 0.4734 0.3818 0.2528 0.3940 0.3181 .. GENERATED FROM PYTHON SOURCE LINES 337-395 Step 8 - How to read the figure ------------------------------- Let us now read the page like a real user. Row 1: epsilon_prior ~~~~~~~~~~~~~~~~~~~~ This row tells you how the prior-side physics mismatch changes as you move through the lambda plane. Darker / lighter regions matter depending on the metric and the colormap direction, but in the default scientific reading the "best" region is the one with the most favorable value, and the script can highlight that best point directly. Row 2: epsilon_cons ~~~~~~~~~~~~~~~~~~~ This row tells you how the consolidation-side mismatch responds to the same lambda changes. The most important lesson is that the two rows do not always prefer the exact same region. That is normal. Physics tuning is often about finding a stable compromise rather than forcing one metric to its absolute minimum. Columns: one city per column ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Because each row uses a shared color scale across cities, you can compare Nansha and Zhongshan fairly within the same metric. That is important: without shared scaling, one city could look artificially better only because its panel was rescaled. The best-point marker ~~~~~~~~~~~~~~~~~~~~~ The script marks the best point in parameter space for each panel. That gives the user an immediate visual answer to: "Where is the local optimum for this metric?" The trend arrow ~~~~~~~~~~~~~~~ The trend arrow is subtle but very useful. It is based on a fitted plane in parameter space and points toward the estimated improvement direction. For lower-is-better metrics such as epsilon_prior and epsilon_cons, that arrow is flipped so it points toward decreasing error. What this synthetic example teaches ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In this lesson, the two cities were designed to have related but not identical preferred regions. That is a realistic situation: a lambda pair that is comfortable for one city may not be equally comfortable for another. In practice, that means: - look for broad stable basins, not only one isolated point, - compare row 1 and row 2 together, - and prefer regions where both cities behave reasonably well. .. GENERATED FROM PYTHON SOURCE LINES 397-412 Step 9 - Practical takeaway --------------------------- This figure is best used when you are deciding how strongly to weight the physics terms in training. It does not replace final model evaluation. It helps you choose a *sensible regime* before committing to a longer experiment campaign. A good region in this figure often has three properties: - it is not an isolated pixel, - it keeps both prior and consolidation diagnostics under control, - and it behaves acceptably across cities. .. GENERATED FROM PYTHON SOURCE LINES 414-469 Command-line version -------------------- The same figure can be produced from the command line. The real script supports: - explicit inputs with --input (repeatable), - root scanning via --root / --results-root, - filtering by model names, - filtering by pde_mode, - metric choices such as epsilon_prior, epsilon_cons, r2, coverage80, sharpness80, mae, mse, rmse, and pss, - three render styles: heatmap | tricontour | pcolormesh - and output of the figure plus the used tidy table. Legacy dispatcher: .. code-block:: bash python -m scripts plot-physics-sensitivity \ --root results \ --metric-prior epsilon_prior \ --metric-cons epsilon_cons \ --render heatmap \ --models GeoPriorSubsNet \ --pde-modes both \ --trend-arrow true \ --out supp_fig_S7_physics_sensitivity Explicit input file: .. code-block:: bash python -m scripts plot-physics-sensitivity \ --input results/ablation_table.csv \ --metric-prior epsilon_prior \ --metric-cons epsilon_cons \ --render pcolormesh \ --show-points true \ --trend-arrow true \ --out supp_fig_S7_physics_sensitivity Modern CLI: .. code-block:: bash geoprior plot physics-sensitivity \ --root results \ --metric-prior epsilon_prior \ --metric-cons epsilon_cons \ --render tricontour \ --out supp_fig_S7_physics_sensitivity The gallery page teaches the figure. The command line reproduces it in a real workflow. .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 5.203 seconds) .. _sphx_glr_download_auto_examples_figure_generation_plot_physics_sensitivity.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_physics_sensitivity.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_physics_sensitivity.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_physics_sensitivity.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_