integrate_query¶
Functions related to querying posterior realizations in the INTEGRATE module.
Query posterior realizations based on geophysical constraints.
This module provides tools to compute probabilities that posterior realizations from Bayesian inversion satisfy user-defined constraints (e.g., thickness of lithology classes, resistivity thresholds).
- integrate.integrate_query.cells_to_polygon(cells, mask)¶
Union of the masked Voronoi cells -> a single shapely Polygon (largest part).
- integrate.integrate_query.find_coherent_area(X, Y, P, p_min, X_center=None, Y_center=None, max_area_m2=None, seed_index=None, xy=None, hull_ratio=0.1, edge_buffer=None, cell_area_k=6.0, elong_max=4.0)¶
Grow one coherent high-probability area and return its indices + polygon.
Full geometry pipeline in one call. From the sounding coordinates a Voronoi tessellation defines the adjacency graph and the per-sounding cell areas; the cells are clipped to a concave hull of the survey so their areas are meaningful. Edge-affected soundings (huge, badly constrained cells on the sparse survey rim) are dropped – they can neither seed a region nor be reached by growth. A connected region is then grown with
grow_connected_region()and its boundary returned as a polygon.- Parameters:
X (ndarray (N,) or (N, 1)) – Sounding coordinates [m]; column vectors (raw HDF5) are flattened.
Y (ndarray (N,) or (N, 1)) – Sounding coordinates [m]; column vectors (raw HDF5) are flattened.
P (ndarray (N,) or (N, 1)) – Per-sounding probability/score, aligned with
X/Y. Soundings failing the edge filter are made unreachable internally.p_min (float) – Inclusion probability cutoff (the region “width”/area knob).
X_center (float, optional) – Seed the region near this (x, y) – the nearest kept sounding is used as the center. If both are None (and no
seed_index/xy), the sounding with the maximumPis used. The resolved center is returned in the output asX_center/Y_center.Y_center (float, optional) – Seed the region near this (x, y) – the nearest kept sounding is used as the center. If both are None (and no
seed_index/xy), the sounding with the maximumPis used. The resolved center is returned in the output asX_center/Y_center.max_area_m2 (float, optional) – Hard cap on the region area [m^2]; None = no cap.
seed_index (int, optional) – Seed at this exact sounding index (bypasses
X_center/Y_center).xy ((float, float), optional) – Deprecated alias for
(X_center, Y_center). At most one seed selector (X_center/Y_center/xy,seed_index) may be set.hull_ratio (float, optional) – Concave-hull tightness for the survey outline (0 = tight, 1 = convex).
edge_buffer (optional) – Edge-filter parameters; see
flag_edge_cells().cell_area_k (optional) – Edge-filter parameters; see
flag_edge_cells().elong_max (optional) – Edge-filter parameters; see
flag_edge_cells().
- Returns:
region –
idx(int (M,) region sounding indices),polygon(shapely Polygon outline),seed(int),area(float [m^2]),order(list[int] growth path),X_center/Y_center(float – the sounding coordinates actually used as the region center/seed: the requestedX_center/Y_centersnapped to its nearest sounding, or the argmax-Psounding if no center was given), plus the reusable Voronoi scaffold:vor(scipy Voronoi),neighbors(list[list[int]] adjacency),cells(per-sounding clipped cell polygons),cell_area((N,) float),boundary(concave survey outline Polygon),good((N,) bool edge-filter keep mask) andmask((N,) bool) –mask[i] = i in idx. For several regions of interest share this scaffold across calls, or passvor/neighbors/cells/cell_area/boundary/goodintogrow_connected_region()to grow extra regions without recomputing the geometry.- Return type:
dict
Examples
>>> region = find_coherent_area(X, Y, P_raw, p_min=0.5) >>> region['idx'], region['polygon'].area, region['area'] >>> # extra region on the same scaffold, no recompute: >>> idx2, area2, _ = grow_connected_region( ... np.where(region['good'], P_raw, -np.inf), ... region['neighbors'], region['cell_area'], p_min=0.5, ... seed=region['seed'])
- integrate.integrate_query.flag_edge_cells(X, Y, vor, cells, boundary, edge_buffer=None, k=6.0, elong_max=None)¶
Boolean good mask – False for edge-affected soundings on the sparse survey rim.
A sounding is dropped if its Voronoi cell is unbounded (a convex-hull vertex), or if it lies within edge_buffer of the survey outline AND its cell is either much larger than the interior-median cell (> k x) or very elongated (perimeter^2 / (4 pi area) > elong_max). Large cells that are NOT near the rim (interior data gaps) are kept.
Returns (good, edge_affected, info).
- integrate.integrate_query.get_prior_model_info(f_prior_h5, im)¶
Return metadata for prior model im.
- Parameters:
f_prior_h5 (str) – Path to the prior HDF5 file.
im (int) – Model index.
- Returns:
info – Keys: ‘name’, ‘is_discrete’, ‘z’, ‘class_id’, ‘class_name’.
- Return type:
dict
- integrate.integrate_query.grow_connected_region(P, neighbors, cell_area, p_min, max_area_m2=None, seed=None)¶
Grow one connected high-probability region outward from a seed sounding.
Starting at
seed, repeatedly add the adjacent sounding with the highestP, keeping only those withP >= p_min. Growth stops on its own when no untried adjacent sounding is likely enough, or when the accumulated area reachesmax_area_m2(if given).This is the pure graph step – it needs a pre-built adjacency graph and per-sounding areas but no Voronoi/shapely. Use
find_coherent_area()for the full geometry pipeline (Voronoi + edge filter + boundary polygon).- Parameters:
P (ndarray (N,)) – Per-sounding score/probability;
NaN/-infsoundings are never added (use this to make edge-dropped soundings unreachable).neighbors (list of iterable of int) – Adjacency graph;
neighbors[i]= indices of soundings adjacent to i.cell_area (ndarray (N,)) – Per-sounding representative area [m^2]; summed into
area.p_min (float) – Inclusion cutoff – the region grows while the best neighbour has
P >= p_min.max_area_m2 (float, optional) – Hard cap on the accumulated area [m^2]; None = no cap.
seed (int, optional) – Starting sounding index; None -> argmax of
P.
- Returns:
indices (ndarray (M,) int) – Indices of the soundings in the grown region (includes the seed).
area (float) – Accumulated area [m^2], sum of
cell_areaoverindices.order (list of int) –
indicesin the order they were added (growth path; order[0] = seed).
- integrate.integrate_query.load_query(path)¶
Load a query dict from a JSON file.
- Parameters:
path (str) – Input JSON file path.
- Returns:
query – Query definition dictionary.
- Return type:
dict
- integrate.integrate_query.prior_describe(f_prior_h5)¶
Print a human-readable summary of all models in a prior HDF5 file.
- Parameters:
f_prior_h5 (str) – Path to the prior HDF5 file.
Examples
>>> ig.prior_describe('prior.h5') Prior file: prior.h5 N realizations: 1000000 im=1 Resistivity CONTINUOUS depth 0–89 m (89 layers) im=2 Lithology DISCRETE depth 0–89 m (89 layers) class 1 = Sand class 2 = Grus im=3 Waterlevel SCALAR
- integrate.integrate_query.query(f_post_h5, query_dict)¶
Dispatcher: route to query_probability() or query_percentile() based on query_dict.
If
query_dictcontains a"metric"key, callsquery_percentile(). Otherwise callsquery_probability()(backward compatible with all existing"constraints"-based dicts).- Parameters:
f_post_h5 (str) – Path to the posterior HDF5 file.
query_dict (str or dict) – Query definition. See
query_probability()andquery_percentile()for the respective schemas.
- Returns:
See the delegated function for details.
- Return type:
result, meta
- integrate.integrate_query.query_from_text(text, f_prior_h5, model='anthropic/claude-sonnet-4-6', api_key=None, max_tokens=4096, verbose=False, system_prompt=None)¶
Translate a natural-language query into a query dict using an LLM.
Uses LiteLLM to interpret the user’s text query in the context of the available prior models and the integrate query schema, returning a query dict and a plain-English interpretation of what the LLM understood.
- Parameters:
text (str) – Natural language description of the query, e.g. “What is the probability that cumulative clay thickness exceeds 10 m?”.
f_prior_h5 (str) – Path to the prior HDF5 file. Model metadata (class names, depth ranges, discrete/continuous type) is read automatically and included in the LLM prompt so the model knows what constraints are valid.
model (str, optional) – LiteLLM model string (default: ‘anthropic/claude-sonnet-4-6’). Any LiteLLM-supported model works, e.g. ‘openai/gpt-4o’.
api_key (str, optional) – Provider API key. If None, the relevant environment variable (e.g. ANTHROPIC_API_KEY) is used.
verbose (bool, optional) – If True, print the system prompt and LLM response for inspection.
system_prompt (str, optional) – Custom system prompt overriding the default built from the prior file. Use to hand-tune the translation instructions (model list and schema context). The effective prompt is still returned for inspection.
- Returns:
query_dict (dict) – Query dict ready to pass to ig.query(f_post_h5, query_dict).
interpretation (str) – Plain English confirmation of what the LLM understood the query to mean. Check this before running ig.query() to catch misunderstandings cheaply.
system_prompt (str) – The full system prompt sent to the LLM. Useful for inspection and debugging.
- Raises:
ImportError – If the litellm package is not installed.
ValueError – If the LLM reports the query is unsupported, or if the response cannot be parsed as valid JSON.
Notes
Requires either the api_key parameter or the relevant provider environment variable to be set. Install the dependency with: pip install litellm
Examples
>>> import integrate as ig >>> query_dict, interpretation, system_prompt = ig.query_from_text( ... "Probability that cumulative clay thickness > 10 m within 0-30 m", ... f_prior_h5='prior.h5', ... api_key='sk-ant-...', ... ) >>> print(interpretation) >>> P, meta = ig.query('posterior.h5', query_dict) >>> ig.query_plot(P, meta)
- integrate.integrate_query.query_percentile(f_post_h5, query_dict)¶
Compute per-data-point percentiles of a metric over posterior realizations.
Rather than asking “what fraction of realizations satisfy condition X?”, this asks “what is the p5/p50/p95 of metric X across realizations?”. The metric is defined by the same fields as a probability constraint, minus the comparison fields (
thickness_comparison,thickness_threshold,negate).- Parameters:
f_post_h5 (str) – Path to the posterior HDF5 file.
query_dict (str or dict) – Path to a JSON file, or a dict with a
"metric"key and an optional"percentiles"key (default[5, 50, 95]).
- Returns:
percentile_values (ndarray (N_data, n_percentiles)) – Requested percentile values for each data location.
meta (dict) – Keys: ‘X’, ‘Y’, ‘N_data’, ‘N_post’, ‘i_use’, ‘percentiles’.
Examples
>>> query_def = { ... "metric": { ... "im": 2, "classes": [1, 2], ... "thickness_mode": "cumulative", ... "depth_max": 30.0 ... }, ... "percentiles": [5, 50, 95] ... } >>> pct_values, meta = query_percentile('f_post.h5', query_def) >>> # pct_values shape: (N_data, 3) — p5, p50, p95 per location
- integrate.integrate_query.query_percentile_plot(percentile_values, meta, query_text=None, interpretation=None, text_panel=False, hardcopy=False, **kwargs)¶
Plot one probability map per requested percentile as side-by-side subplots.
- Parameters:
percentile_values (ndarray (N_data, n_percentiles)) – Output of query_percentile().
meta (dict) – Metadata dict from query_percentile() containing ‘X’, ‘Y’, ‘percentiles’.
query_text (str, optional) – Original query string — shown as figure suptitle.
interpretation (str, optional) – LLM interpretation string — shown below query_text if provided.
text_panel (bool, optional) – If True, add a narrow text column to the right of the maps.
hardcopy (bool or str, optional) – Save figure to disk. True → ‘query_percentile_plot.png’; a string is used as the filename (.png appended if no extension).
**kwargs – All remaining keyword arguments are forwarded to
plot_xy(), giving full control overcmap,clim,uselog,colorbar,colorbar_label,plotPoints,plotPoints_color,plotPoints_marker,s, etc.climdefaults to[percentile_values.min(), percentile_values.max()]so that all subplots share the same colour scale.cmapdefaults to'viridis'.
- Returns:
fig
- Return type:
matplotlib Figure
- integrate.integrate_query.query_plot(P, meta, ip=None, query_dict=None, f_prior_h5=None, f_post_h5=None, title=None, query_text=None, interpretation=None, text_panel=False, hardcopy=False, **kwargs)¶
Plot query results and optionally detailed model visualization for a data point.
If ip is None, displays the XY probability map showing P(x, y). If ip is provided (together with query_dict and f_prior_h5/f_post_h5), skips the probability map and shows only the detailed single-point visualization of all posterior realizations and the query-matching subset.
- Parameters:
P (ndarray (N_data,)) – Probability array from query().
meta (dict) – Metadata dict from query() containing ‘X’, ‘Y’, ‘i_use’, ‘i_use_query’.
ip (int, optional) – Data point index to visualize in detail. If None, only shows probability map.
query_dict (dict, optional) – Query dict used in query(). Required for detailed visualization.
f_prior_h5 (str, optional) – Path to prior HDF5 file. If not provided, will be extracted from f_post_h5.
f_post_h5 (str, optional) – Path to posterior HDF5 file. Used to automatically extract prior file path if f_prior_h5 is not provided.
title (str, optional) – Custom title for the probability map. If None, a title is built from query_text and interpretation (if provided), or ‘Query Probability Map’.
query_text (str, optional) – The original natural-language query string. Shown in the figure title, or in the text panel if text_panel=True.
interpretation (str, optional) – The LLM interpretation string returned by query_from_text(). Shown as a second line in the figure title, or in the text panel if text_panel=True.
text_panel (bool, optional) – If True and query_text or interpretation is provided, adds a narrow text column to the right of the probability map. The query text appears at the top and the interpretation below. Default False.
hardcopy (bool or str, optional) – Save the probability map figure. If True, saves as ‘query_plot.png’. If a string, uses that as the filename (a ‘.png’ extension is appended if the string has no extension). Default False.
**kwargs – All remaining keyword arguments are forwarded to
plot_xy(), giving full control overcmap,clim,uselog,colorbar,colorbar_label,plotPoints,plotPoints_color,plotPoints_marker,s, etc.cmapdefaults to'hot_r'andclimdefaults to[0, 1].
Examples
>>> P, meta = query(f_post_h5, query_def) >>> query_plot(P, meta) # Just probability map >>> query_plot(P, meta, title='Custom Query Title') # Custom title >>> query_plot(P, meta, ip=1000, query_dict=query_def, f_post_h5='posterior.h5') >>> query_plot(P, meta, ip=1000, query_dict=query_def, f_prior_h5='prior.h5') >>> # With LLM query text and interpretation: >>> query_dict, interp = ig.query_from_text(text, f_prior_h5) >>> P, meta = ig.query(f_post_h5, query_dict) >>> ig.query_plot(P, meta, query_text=text, interpretation=interp)
- integrate.integrate_query.query_probability(f_post_h5, query_dict)¶
Compute per-data-point probability that posterior realizations satisfy a query.
- Parameters:
f_post_h5 (str) – Path to the posterior HDF5 file.
query_dict (str or dict) – Path to a JSON file, or a dict with a
"constraints"key.
- Returns:
P (ndarray (N_data,)) – Probability [0, 1] for each data location.
meta (dict) – Keys: ‘X’, ‘Y’, ‘N_data’, ‘N_post’, ‘i_use’, ‘i_use_query’.
Examples
>>> query_def = { ... "constraints": [{ ... "im": 2, "classes": [2], ... "thickness_mode": "cumulative", ... "thickness_comparison": ">", ... "thickness_threshold": 10.0, ... "depth_min": 0.0, "depth_max": 30.0 ... }] ... } >>> P, meta = query_probability('f_post.h5', query_def)
- integrate.integrate_query.query_test_llm(model='anthropic/claude-sonnet-4-6', api_key=None, verbose=1)¶
Test whether a given LLM model and API key are working correctly.
Sends a minimal JSON-generation prompt and checks that the response is valid JSON. Prints a summary and returns a status dict.
- Parameters:
model (str, optional) – LiteLLM model string (default: ‘anthropic/claude-sonnet-4-6’).
api_key (str, optional) – Provider API key. If None, the relevant environment variable is used.
verbose (int, optional) – 0 = silent, 1 = summary only (default), 2 = full response included.
- Returns:
result – Keys: ‘ok’ (bool), ‘model’, ‘response’ (str or None), ‘error’ (str or None).
- Return type:
dict
- integrate.integrate_query.region_volumes(pct, area)¶
Volume at each percentile, summed over an area’s soundings.
- Parameters:
pct (ndarray (N_sounding, N_pct)) – Per-sounding posterior thickness percentiles [m] (e.g. the array returned by
ig.query(f_post_h5, {"metric": ..., "percentiles": ...})).area (dict) – Must provide
mask((N,) bool – which soundings to sum over) andcell_area((N,) float [m^2] – the representative area of each sounding). A dict returned byfind_coherent_area()works directly; so does any{'mask': ..., 'cell_area': ...}(e.g. one built from the soundings inside a hand-drawn polygon).
- Returns:
sum(pct[mask] * cell_area[mask])– one volume [m^3] per percentile.- Return type:
ndarray (N_pct,)
- integrate.integrate_query.save_query(query, path)¶
Save a query dict to a JSON file.
- Parameters:
query (dict) – Query definition dictionary.
path (str) – Output JSON file path.
- integrate.integrate_query.title_from_json(file_json, f_prior_h5=None, model='anthropic/claude-sonnet-4-6', api_key=None, showInfo=1)¶
Return a plain-language description of what a query JSON dict will do.
Uses an LLM to produce a short human-readable summary suitable for a figure title or log message. If the LLM is unavailable (missing package, no API key, network error), returns an empty string.
- Parameters:
file_json (str or dict) – Path to a query JSON file, or a query dict directly (e.g. from
ig.load_query()).f_prior_h5 (str, optional) – Path to the prior HDF5 file. When provided, real model names, depth ranges, and class labels are included in the prompt so the description uses geological names instead of numeric model/class IDs.
model (str, optional) – LiteLLM model string (default: ‘anthropic/claude-sonnet-4-6’).
api_key (str, optional) – Provider API key. If None, the relevant environment variable is used.
showInfo (int, optional) – 0 = silent; 1 = print a message when the LLM cannot be reached (default); 2 = also print the exception detail.
- Returns:
description – One-sentence plain-English summary of the query, or an empty string if the LLM could not be reached.
- Return type:
str
Examples
>>> description = ig.title_from_json('my_query.json') >>> description = ig.title_from_json('my_query.json', f_prior_h5='prior.h5') >>> query = ig.load_query('query_ex1.json') >>> title = ig.title_from_json(query, f_prior_h5='prior.h5') >>> title = ig.title_from_json(query, showInfo=0) # silent on failure
- integrate.integrate_query.voronoi_cells_ordered(X, Y, boundary)¶
Per-sounding Voronoi cell polygons, in input order, clipped to boundary.
Always returns exactly
len(X)entries withcells[i]belonging to soundingi(each cell is matched to the sounding point it contains, or the nearest sounding as a fallback), socells/cell_areastay aligned withX/Y/P.
- integrate.integrate_query.voronoi_graph(X, Y)¶
(vor, neighbors): scipy Voronoi object + neighbour-index lists (cells sharing an edge).
Fails on exactly-duplicated coordinates – deduplicate X, Y first if that happens.