executing-plans
executing-plans
Use when executing an implementation plan in the current session as the implementer yourself — your human partner chose inline execution, or no subagent tool is available
serialization-and-advancedPersist GemPy GeoModel objects, validate and round-trip JSON or .gempy files, extract meshes, configure centered-grid gravity, and use optional topology, property, Subsurface, and legacy integrations safely.
Independent GitHub snapshot · Automated classification · Original documentation governs
Use this route after a GeoModel has been constructed and, where needed,
computed. It owns persistence, schema validation, mesh extraction, geophysics,
topology/property plugins, Subsurface conversion, and GemPy 2/3 compatibility.
It does not own initial model construction or ordinary grids/plots.
.gempy output, but JSON helpers do not promise that.| Need | Entry point | Result / gate |
|---|---|---|
| Native binary model archive | gp.save_model(model, path, validate_serialization=True) | .gempy ZIP archive; load and compare counts/structure |
| Native binary restore | gp.load_model("model.gempy") | GeoModel; requires exact .gempy suffix |
| Pydantic JSON snapshot | model.model_dump_json(by_alias=True, indent=2) | JSON text; restore with binary context, see reference |
| Tutorial JSON interchange | JsonIO.save_model_to_json / load_model_from_json | portable data/grid/series JSON; not the same as .gempy |
| Dense-grid mesh | set_meshes_with_marching_cubes(model) | vertices/faces on each element; needs dense computed output + scikit-image |
| Gravity forward setup | gp.set_centered_grid + gp.calculate_gravity_gradient | centered kernel and GeophysicsInput; compute owns final solution |
| Topology/property | external gempy_plugins modules | optional, version-matched plugin only |
| Subsurface mesh/data | model.solutions...meshes_to_subsurface() or gp.compute_model(..., to_subsurface=True) where supported | optional subsurface, usually PyVista for display |
| GemPy 3 to legacy | gempy.API.gp2_gp3_compatibility.gp3_to_gp2_input.gempy3_to_gempy2 | requires gempy_legacy; compatibility surface is narrow |
Keep the model object in memory and ensure its input data, structural group order, fault relation matrix, grid, and interpolation options are in the intended state. If it will be computed after restore, compute it after loading; solutions are runtime/cache state, not a replacement for source inputs.
Save a binary archive:
import gempy as gp
archive = gp.save_model(model, "artifacts/model.gempy",
validate_serialization=True)
restored = gp.load_model(archive)
assert len(restored.surface_points_copy.xyz) == len(model.surface_points_copy.xyz)
assert len(restored.orientations_copy.xyz) == len(model.orientations_copy.xyz)
save_model defaults a missing path to <model.meta.name>.gempy; a path
without a suffix gets .gempy appended. Any present non-.gempy suffix
raises ValueError. load_model raises ValueError for a missing/wrong
suffix and FileNotFoundError for a missing file. Both currently emit a
development warning; do not treat that warning as a successful validation.
The default validation compares input-table bytes and the model string.
Recompute explicitly with gp.compute_model(restored) when downstream
solutions, meshes, or gravity are required. A .gempy archive stores a
JSON header plus input.bin and grid.bin; do not hand-edit the archive.
For faulted models, compare group names/order and
np.array_equal(original.structural_frame.fault_relations, restored.structural_frame.fault_relations). The loader temporarily removes
serialized fault names, validates the Pydantic model, then restores relations;
this is why a fault round-trip must be tested rather than inferred from a
surface-point count.
Detailed archive and Pydantic patterns are in references/persistence-reference.md.
GeoModel.model_dump_json(by_alias=True, indent=2) serializes the Pydantic
model/header. GeoModel.model_validate_json(text) needs the binary payload
context for binary-backed input/grid fields. For a native binary round-trip,
prefer gp.save_model/gp.load_model; if inspecting the JSON header directly,
use the context manager with the original input_tables_binary and grid_binary:
from gempy.core.data.encoders.converters import loading_model_from_binary
text = model.model_dump_json(by_alias=True, indent=2)
with loading_model_from_binary(model.structural_frame.input_tables_binary,
model.grid.grid_binary):
restored = gp.data.GeoModel.model_validate_json(text)
restored.validate()
GeoModel.validate() is semantic validation, not a generic JSON-schema
validator. gp.compute_model calls it by default; skip_validation=True is an
explicit escape hatch only for a known, intentional diagnostic case. It raises
ModelValidationError for empty input, empty groups, underdetermined input, or
an out-of-order BASEMENT group. Pydantic errors can still arise from malformed
field types/enums. See gempy/VALIDATION_SPEC.md facts summarized in
references/troubleshooting.md.
gempy.modules.json_io.JsonIOThis is a separate, file-oriented interchange format:
from gempy.modules.json_io import JsonIO
JsonIO.save_model_to_json(model, "artifacts/model.json")
portable = JsonIO.load_model_from_json("artifacts/model.json")
portable.validate()
Required top-level JSON keys are surface_points, orientations, and
grid_settings. The validator mutates the loaded dictionary with defaults:
point/orientation nugget, IDs, orientation polarity, default metadata, and
series structural relation. Coordinates and gradients must be numeric; polarity
must be 1 or -1; grid resolution/extent and optional interpolation options
must have the expected container/types. Missing required keys or fields raise
ValueError with a field-specific message. A JSON load may synthesize a default
Strat_Series and default grid/interpolation settings. JSON save/load is not a
promise to preserve computed solution caches byte-for-byte.
For deterministic smoke testing without source data, run:
python scripts/json_roundtrip_smoke.py
The helper generates a tiny model, writes only temporary .json/.gempy
artifacts, checks both round-trips, and reports missing core prerequisites
instead of reading checkout files.
from gempy.modules.mesh_extranction.marching_cubes import set_meshes_with_marching_cubes. It requires model.solutions to have
DENSE_GRID, interpolation outputs, a dense regular grid, and scikit-image.
It stores vertices and triangular edges on each structural element. A
missing dense solution or output raises ValueError; a missing skimage is
an environment failure. Do not use this as a plotting route; hand display to
grids/visualization.N x 3 NumPy
array, then call gp.set_centered_grid(grid=model.grid, centers=centers, resolution=[nx, ny, nz], radius=scalar_or_3_vector). Compute
tz = gp.calculate_gravity_gradient(model.grid.centered_grid) and set
model.geophysics_input = gp.data.GeophysicsInput(tz=tz, densities=np.asarray(densities)). Set mesh_extraction=False when gravity
is the only result needed, then compute with a compatible CPU NumPy config.
The kernel is centered-grid geometry; densities must match the model's
expected units/order. Gravity computation is numerical and can be expensive;
validate shapes and one small center before scaling up.from gempy_plugins.topology_analysis import topology as tp, after a computed model. tp.compute_topology(model)
returns (edges, centroids); adjacency and label helpers operate on those
values. Missing gempy_plugins is not a GemPy core failure. Do not silently
substitute an invented internal topology API.gempy_plugins.property_estimation uses GSTools and
conditioning data to assign domains and run configured kriging/simulation.
It is optional and can be expensive, stochastic, and visualization-heavy;
keep seeds and domain decisions explicit. Missing GSTools/plugin is not a
persistence error.subsurface is required by
gp.set_topography_from_subsurface_structured_grid,
gp.set_topography_from_file, borehole conversion, and the
meshes_to_subsurface() solution helpers. Mesh display through Subsurface
normally also needs its PyVista stack. require_subsurface() raises a
targeted ImportError; install/diagnose it through the environment route.
Network-backed example datasets are not part of this workflow. Export arrays
or unstructured data to a caller-owned path/object and check vertex/cell
lengths before passing to a simulator.gempy3_to_gempy2(model) is a narrow adapter that requires
gempy_legacy; it creates a legacy model, transfers tabular inputs, extent,
resolution, and structural mapping. The reverse output adapter consumes
engine Solutions and a legacy Project. Treat this as migration glue, not
a general .gempy loader, and verify the legacy package/API version first.For signatures, shape checks, and integration snippets see references/advanced-integrations.md.
json.load, report the first missing/type-invalid field, then rerun
JsonIO._validate_json_schema on a copy. For .gempy, do not unzip/edit;
rerun save with validation and compare the original/loaded input bytes.restored.validate() and inspect
group names, structural relations, fault matrix shape, grid active types,
and interpolation options before using skip_validation=True.scikit-image; if gravity fails, verify
centered-grid centers/resolution/radius, tz.shape, densities length, and
that the chosen backend supports the requested dtype/device.See references/troubleshooting.md for the failure matrix and links back to modeling and environment routes.