Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
ff1a93d
Remove unused idx variable in _elastic_component
elc45 Jan 12, 2026
3d89a31
Use eigenvalues to impose standard Matérn prior for GP
maresb Jan 12, 2026
600ce7d
Simplify Matérn prior formula (remove top eigenvalue normalization)
elc45 Jan 12, 2026
3472226
Extract _matern_gp_prior_sigma_from_eigenvalues helper function
elc45 Jan 12, 2026
27ff49f
Add coupling_sigma and elastic_sigma config fields
elc45 Jan 12, 2026
fd5b392
Wire up sigma parameter from config
elc45 Jan 12, 2026
1071bb1
add notebook
elc45 Jan 12, 2026
41ac793
notebook fixes
elc45 Jan 12, 2026
dca0c4c
Refactor eigenmode prior variance computation in MCMC
maresb Jan 14, 2026
dfceb0f
Simplify run command
maresb Jan 15, 2026
135cd30
Remove scaling for elastic component
maresb Jan 29, 2026
47dfbac
Add gp_parameterization config for centered vs non-centered GP coeffi…
maresb Jan 29, 2026
93c0a9a
Add run directory aliases to gp_vis notebook
maresb Jan 21, 2026
cfab972
Fix unused vmin/vmax variables in gp_vis notebook
maresb Jan 21, 2026
0ba01e5
Fix unused center variable in gp_vis notebook
maresb Jan 21, 2026
7229a41
Make slip kind handling explicit with error for unknown values
maresb Jan 29, 2026
29a7bf3
Fix typing lints for nullable tde_to_velocities
maresb Jan 29, 2026
f1ac6d1
Rename has_coupling_limit to has_coupling_bound
maresb Jan 29, 2026
c19519f
Split coupling_sigma/elastic_sigma into ss/ds variants with defaults
maresb Jan 29, 2026
11e8622
Add prior mean parameters for coupling/elastic GP fields
maresb Jan 29, 2026
9bda576
Consolidate _ss/_ds suffixed parameters to single values
maresb Jan 29, 2026
7a4fdfa
Rename default_mcmc_* to mcmc_default_mesh_* in Config
maresb Jan 30, 2026
7362354
Update gp_vis notebook colorbar limits from [-5, 5] to [-1, 1]
maresb Jan 30, 2026
805e18b
Increase default mcmc mesh sigma values from 1.0 to 5.0
maresb Jan 30, 2026
9bddea7
Update runs for Mac Studio
maresb Jan 30, 2026
e733733
Add mcmc_default_mesh_matern_nu and mcmc_default_mesh_matern_length_s…
maresb Feb 1, 2026
8f3b33e
Add MCMC and mesh parameters to CLI
maresb Feb 1, 2026
59bf343
Set Matérn nu=5/2
maresb Feb 9, 2026
fed5c5b
Set coupling GP mean to 0.9
maresb Feb 9, 2026
24a994e
Set Matérn length scale to 0.2 diameters
maresb Feb 9, 2026
3b6bf3f
Eliminate duplicate MeshConfig defaults for GP kernel parameters
maresb Feb 9, 2026
703e7dd
Set default mesh coupling sigma to 1
maresb Feb 9, 2026
35a7dfc
Add doc_gp_params.ipynb
maresb Feb 9, 2026
dbcdaf8
Minimize gp_vis.ipynb
maresb Feb 9, 2026
5533de6
update reference arrays
elc45 Feb 9, 2026
5eff0b0
rtol
elc45 Feb 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions celeri/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,34 @@ def parse_args() -> argparse.Namespace:
required=False,
help="Effective area (m²) for station likelihood weighting",
)
parser.add_argument(
"--mcmc-default-mesh-coupling-mean",
type=float,
default=None,
required=False,
help="Default GP prior mean for mesh coupling",
)
parser.add_argument(
"--mcmc-default-mesh-coupling-sigma",
type=float,
default=None,
required=False,
help="Default GP prior sigma for mesh coupling",
)
parser.add_argument(
"--mcmc-default-mesh-elastic-mean",
type=float,
default=None,
required=False,
help="Default GP prior mean for mesh elastic rates",
)
parser.add_argument(
"--mcmc-default-mesh-elastic-sigma",
type=float,
default=None,
required=False,
help="Default GP prior sigma for mesh elastic rates",
)
parser.add_argument(
"--mesh-default-eigenvector-algorithm",
type=str,
Expand All @@ -211,6 +239,20 @@ def parse_args() -> argparse.Namespace:
choices=["eigh", "eigsh"],
help="Algorithm for mesh eigendecomposition (eigh | eigsh)",
)
parser.add_argument(
"--mcmc-default-mesh-matern-nu",
type=float,
default=None,
required=False,
help="Default Matérn smoothness parameter (nu) for mesh eigenfunctions",
)
parser.add_argument(
"--mcmc-default-mesh-matern-length-scale",
type=float,
default=None,
required=False,
help="Default Matérn length scale for mesh eigenfunctions",
)

return parser.parse_args()

Expand Down
95 changes: 83 additions & 12 deletions celeri/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@

McmcStationWeighting = Literal["voronoi",]

McmcMeanParameterization = Literal["constrained", "unconstrained"]


class Config(BaseModel):
# Forbid extra fields when reading from JSON
Expand Down Expand Up @@ -196,8 +198,8 @@ class Config(BaseModel):
mesh_default_eigenvector_algorithm: EigenvectorAlgorithm = "eigh"
"""Default algorithm for computing eigenvectors in mesh processing.

This value is used as a fallback when a mesh configuration does not
specify its own `eigenvector_algorithm`.
Propagated to each mesh's ``eigenvector_algorithm`` unless overridden
in the per-mesh configuration.

Options:
- "eigh": Dense eigenvalue decomposition (scipy.linalg.eigh). Faster for many modes.
Expand All @@ -206,6 +208,39 @@ class Config(BaseModel):
Both have equivalent accuracy, but eigenvector signs may differ between algorithms.
"""

mcmc_default_mesh_matern_nu: float = 2.5
"""Default Matérn kernel smoothness parameter (nu) for mesh eigenfunction computation.

Propagated to each mesh's ``matern_nu`` unless overridden in the per-mesh
configuration.

Common values:
- 0.5: Exponential covariance (rough, continuous but not differentiable)
- 1.5: Once-differentiable (moderate smoothness)
- 2.5: Twice-differentiable (smooth)

Higher values produce smoother eigenfunctions.
"""

mcmc_default_mesh_matern_length_scale: float = 0.2
"""Default Matérn kernel length scale for mesh eigenfunction computation.

Propagated to each mesh's ``matern_length_scale`` unless overridden in the
per-mesh configuration.

The interpretation depends on ``mesh_default_matern_length_units``.
"""

mesh_default_matern_length_units: Literal["absolute", "diameters"] = "diameters"
"""Default units for the Matérn kernel length scale.

Propagated to each mesh's ``matern_length_units`` unless overridden in the
per-mesh configuration.

- "diameters": Scales by mesh diameter (max pairwise centroid distance)
- "absolute": Uses the value directly in mesh coordinate units (meters)
"""

mcmc_station_velocity_method: McmcStationVelocityMethod = "project_to_eigen"
"""Method for computing station velocities from slip rates in MCMC.

Expand All @@ -231,6 +266,23 @@ class Config(BaseModel):
standard unweighted likelihood or if your network has uniform spatial coverage.
"""

# Default mean and sigma for coupling/elastic GP priors in MCMC.
# These are defaults; mesh-specific values can override.
# The "parameterization" determines whether the mean is in constrained or
# unconstrained space. For coupling with bounds [0, 1], constrained mean 0.5
# is at the center. For elastic with one-sided bounds, unconstrained mean 0
# places the constrained mean at ±softplus_lengthscale.
mcmc_default_mesh_coupling_mean: float = 0.9
mcmc_default_mesh_coupling_mean_parameterization: McmcMeanParameterization = (
"constrained"
)
mcmc_default_mesh_coupling_sigma: float = 1.0
mcmc_default_mesh_elastic_mean: float = 0.0
mcmc_default_mesh_elastic_mean_parameterization: McmcMeanParameterization = (
"unconstrained"
)
mcmc_default_mesh_elastic_sigma: float = 5.0

mcmc_station_effective_area: float = 10_000**2
"""Effective area (in m²) for station likelihood weighting in MCMC.

Expand Down Expand Up @@ -369,6 +421,34 @@ def validate_no_mixed_constraints(self) -> Self:
raise ValueError(error)
return self

@model_validator(mode="after")
def apply_mesh_defaults(self) -> Self:
"""Propagate top-level defaults to mesh configs that don't set their own.

MeshConfig fields that default to None inherit from the corresponding
Config-level default. Per-mesh overrides (explicitly set in the mesh
JSON) are preserved.
"""
defaults: dict[str, object] = {
# GP kernel hyperparameters
"matern_nu": self.mcmc_default_mesh_matern_nu,
"matern_length_scale": self.mcmc_default_mesh_matern_length_scale,
"matern_length_units": self.mesh_default_matern_length_units,
"eigenvector_algorithm": self.mesh_default_eigenvector_algorithm,
# MCMC prior parameters
"coupling_mean": self.mcmc_default_mesh_coupling_mean,
"coupling_mean_parameterization": self.mcmc_default_mesh_coupling_mean_parameterization,
"coupling_sigma": self.mcmc_default_mesh_coupling_sigma,
"elastic_mean": self.mcmc_default_mesh_elastic_mean,
"elastic_mean_parameterization": self.mcmc_default_mesh_elastic_mean_parameterization,
"elastic_sigma": self.mcmc_default_mesh_elastic_sigma,
}
for mesh_field, default_value in defaults.items():
for mesh_param in self.mesh_params:
if mesh_field not in mesh_param.model_fields_set:
setattr(mesh_param, mesh_field, default_value)
return self


def _get_output_path(base: Path) -> Path:
"""Generate a unique numbered output path within the base directory.
Expand Down Expand Up @@ -428,16 +508,7 @@ def get_config(file_name: Path | str) -> Config:
else:
mesh_params = MeshConfig.from_file(file_path.parent / mesh_parameters_file_name)

# Apply the top-level default eigenvector algorithm to mesh configs
# that don't explicitly set their own
mesh_default_eigenvector_algorithm = config_data.get(
"mesh_default_eigenvector_algorithm", None
)
if mesh_default_eigenvector_algorithm is not None:
for mesh_param in mesh_params:
if "eigenvector_algorithm" not in mesh_param.model_fields_set:
mesh_param.eigenvector_algorithm = mesh_default_eigenvector_algorithm

# Top-level defaults are propagated to mesh configs by Config.apply_mesh_defaults
config_data["mesh_params"] = mesh_params
config_data["file_name"] = file_path.resolve()

Expand Down
85 changes: 67 additions & 18 deletions celeri/mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,18 +84,21 @@ class MeshConfig(BaseModel):
Tuple containing the constrained upper and lower bounds for the elastic rates on the mesh for strike-slip.
elastic_constraints_ds : ScalarBound
Tuple containing the constrained upper and lower bounds for the elastic rates on the mesh for dip-slip.
matern_nu : float
Matérn kernel smoothness parameter (default 1/2). Common values: 1/2 (exponential),
matern_nu : float | None
Matérn kernel smoothness parameter. When None, inherits from
Config.mcmc_default_mesh_matern_nu. Common values: 1/2 (exponential),
3/2 (once-differentiable), 5/2 (twice-differentiable).
matern_length_scale : float
Matérn kernel length scale (default 1.0). Interpretation depends on matern_length_units.
matern_length_units : Literal["absolute", "diameters"]
Units for matern_length_scale: 'diameters' scales by mesh diameter (default),
'absolute' uses the value directly in the same units as mesh coordinates.
eigenvector_algorithm : Literal["eigh", "eigsh"]
Algorithm for eigendecomposition (default "eigh"). 'eigh' (dense LAPACK) is faster for
many modes, 'eigsh' (sparse ARPACK) is faster for few modes. Both have equivalent accuracy,
but eigenvector signs may differ between algorithms.
matern_length_scale : float | None
Matérn kernel length scale. When None, inherits from
Config.mcmc_default_mesh_matern_length_scale.
matern_length_units : Literal["absolute", "diameters"] | None
Units for matern_length_scale. When None, inherits from
Config.mesh_default_matern_length_units. 'diameters' scales by mesh
diameter, 'absolute' uses the value directly in mesh coordinate units.
eigenvector_algorithm : Literal["eigh", "eigsh"] | None
Algorithm for eigendecomposition. When None, inherits from
Config.mesh_default_eigenvector_algorithm. 'eigh' (dense LAPACK) is
faster for many modes, 'eigsh' (sparse ARPACK) is faster for few modes.
softplus_lengthscale : float
Length scale for the softplus operations for sign constraints when only one bound (upper or lower) is present.
Automatically set to 1.0 mm/yr if one bound is present.
Expand Down Expand Up @@ -157,11 +160,31 @@ class MeshConfig(BaseModel):
elastic_constraints_ss: ScalarBound = ScalarBound(lower=None, upper=None)
elastic_constraints_ds: ScalarBound = ScalarBound(lower=None, upper=None)

coupling_sigma: float = 0.25
elastic_sigma: float = 1.0
# GP prior mean and sigma for coupling/elastic fields in MCMC.
# When None, these use defaults from the top-level Config.
# The mean_parameterization determines whether the mean is specified in
# the constrained (bounded) or unconstrained (transformed) space.
coupling_mean: float | None = None
coupling_mean_parameterization: Literal["constrained", "unconstrained"] | None = (
None
)
coupling_sigma: float | None = None
elastic_mean: float | None = None
elastic_mean_parameterization: Literal["constrained", "unconstrained"] | None = None
elastic_sigma: float | None = None

softplus_lengthscale: float = 1.0

gp_parameterization: Literal["centered", "non_centered"] = "non_centered"
"""Parameterization for GP coefficients in MCMC.

Both parameterizations are mathematically equivalent but change the
geometry of the sampling space, which can affect HMC performance.

- "non_centered": Sample white noise and mollify via eigenvalue scaling.
- "centered": Sample directly with heterogeneous variances.
"""

# Hint for the new sqp solver about the likely range of kinematic slip rates.
sqp_kinematic_slip_rate_hint_ss: ScalarBound = ScalarBound(
lower=-100.0, upper=100.0
Expand All @@ -175,11 +198,13 @@ class MeshConfig(BaseModel):
iterative_coupling_smoothing_length_scale: float | None = None
iterative_coupling_kinematic_slip_regularization_scale: float = 1.0

# GP kernel hyperparameters for eigenmode computation
matern_nu: float = 0.5
matern_length_scale: float = 1.0
matern_length_units: Literal["absolute", "diameters"] = "diameters"
eigenvector_algorithm: Literal["eigh", "eigsh"] = "eigh"
# GP kernel hyperparameters for eigenmode computation.
# When None, the top-level Config defaults are used
# (propagated by Config.apply_mesh_defaults).
matern_nu: float | None = None
matern_length_scale: float | None = None
matern_length_units: Literal["absolute", "diameters"] | None = None
eigenvector_algorithm: Literal["eigh", "eigsh"] | None = None

@classmethod
def from_file(cls, filename: str | Path) -> list[MeshConfig]:
Expand Down Expand Up @@ -806,6 +831,18 @@ def from_params(cls, config: MeshConfig):
mesh["side_slip_idx"],
)

assert config.matern_nu is not None, (
"MeshConfig.matern_nu must be set (propagated by Config.apply_mesh_defaults)"
)
assert config.matern_length_scale is not None, (
"MeshConfig.matern_length_scale must be set (propagated by Config.apply_mesh_defaults)"
)
assert config.matern_length_units is not None, (
"MeshConfig.matern_length_units must be set (propagated by Config.apply_mesh_defaults)"
)
assert config.eigenvector_algorithm is not None, (
"MeshConfig.eigenvector_algorithm must be set (propagated by Config.apply_mesh_defaults)"
)
mesh["eigenvalues"], mesh["eigenvectors"] = _get_eigenvalues_and_eigenvectors(
mesh["n_modes"],
mesh["x_centroid"],
Expand Down Expand Up @@ -895,6 +932,18 @@ def from_disk(cls, input_dir: str | Path):
)

if mesh.eigenvalues is None or mesh.eigenvectors is None:
assert config.matern_nu is not None, (
"MeshConfig.matern_nu must be set (propagated by Config.apply_mesh_defaults)"
)
assert config.matern_length_scale is not None, (
"MeshConfig.matern_length_scale must be set (propagated by Config.apply_mesh_defaults)"
)
assert config.matern_length_units is not None, (
"MeshConfig.matern_length_units must be set (propagated by Config.apply_mesh_defaults)"
)
assert config.eigenvector_algorithm is not None, (
"MeshConfig.eigenvector_algorithm must be set (propagated by Config.apply_mesh_defaults)"
)
mesh.eigenvalues, mesh.eigenvectors = _get_eigenvalues_and_eigenvectors(
mesh.n_modes,
mesh.x_centroid,
Expand Down
6 changes: 6 additions & 0 deletions celeri/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -829,8 +829,14 @@ def _hash_elastic_operator_input(
"a_priori_slip_filename",
"coupling_constraints_ss",
"coupling_constraints_ds",
"coupling_mean",
"coupling_mean_parameterization",
"coupling_sigma",
"elastic_constraints_ss",
"elastic_constraints_ds",
"elastic_mean",
"elastic_mean_parameterization",
"elastic_sigma",
"smoothing_weight",
"softplus_lengthscale",
}
Expand Down
6 changes: 5 additions & 1 deletion celeri/scripts/segmesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,11 @@ def main():
for j in range(n_new_meshes):
filename = mesh_dir / f"{seg_file_stem}_segmesh{j}.msh"
new_entry = celeri.MeshConfig(
file_name=model.config.mesh_parameters_file_name
file_name=model.config.mesh_parameters_file_name,
matern_nu=model.config.mcmc_default_mesh_matern_nu,
matern_length_scale=model.config.mcmc_default_mesh_matern_length_scale,
matern_length_units=model.config.mesh_default_matern_length_units,
eigenvector_algorithm=model.config.mesh_default_eigenvector_algorithm,
)
new_entry.mesh_filename = filename
model.config.mesh_params.append(new_entry)
Expand Down
Loading