API Reference

class kdellipspy.AxitraForwardModel(input_ctl_path: str, axitra_dir: str | None = None)[source]

Bases: object

Forward model using axitra python wrapper.

Main workflow: 1) Build invariant geometry from input.ctl 2) Apply ellipse model (a1,a2,theta,np,tp,dmax,vr) 3) Build Axitra instance with model/stations/sources 4) Compute Green functions: moment.green(ap) 5) Convolve: moment.conv(ap, hist, …)

apply_ellipse_model_to_geometry(geometry: FaultGeometry, model: numpy.ndarray, keep_all_sources: bool = False) → FaultGeometry[source]

Apply 7-parameter ellipse model to an existing geometry instance.

keep_all_sources=True keeps the full source set (zero moment outside the ellipse) so the Green’s functions can be cached for the fixed mesh.

build_axitra(geometry: FaultGeometry, fmax: float | None = None, duration: float | None = None, xl: float = 0.0, latlon: bool = True, freesurface: bool = True, ikmax: int = 100000, id: int | None = None, aw: float | None = 0.5)[source]
build_geometry(slip_geom: float = 1.0) → FaultGeometry[source]
build_geometry_with_ellipse_slip(model: numpy.ndarray) → FaultGeometry[source]

Build fault geometry with ellipse-to-subfault slip mapping.

Model parameters (7):

model[0]: a1 - semi-axis 1 (km) model[1]: a2 - semi-axis 2 (km) model[2]: theta - rotation angle (fraction of pi) model[3]: np - position fraction [0,1] model[4]: tp - position angle (fraction of 2pi) model[5]: dmax - maximum slip (m) model[6]: vr - rupture velocity (km/s)

Returns:

FaultGeometry with slip distribution applied from ellipse parameters.

conv(ap, geometry: FaultGeometry, source_type: int | None = None, t0: float | None = 3, t1: float = 0.0, unit: int | None = None, sfunc=None, quiet: bool = True)[source]
estimate_total_moment_and_mw(model: numpy.ndarray, geometry: FaultGeometry | None = None) → Tuple[float, float][source]

Estimate total scalar moment (N.m) and Mw for a 7-parameter ellipse model.

classmethod from_config(cfg: ConfigParser, axitra_dir: str | None = None) → AxitraForwardModel[source]

Create AxitraForwardModel directly from a ConfigParser object. (Crea AxitraForwardModel directamente desde un objeto ConfigParser.)

classmethod from_params(params: dict, axitra_dir: str | None = None) → AxitraForwardModel[source]

Create AxitraForwardModel from a dictionary of parameters instead of an input.ctl file.

This allows building the forward model programmatically without file I/O.

Parameters:

paramsdict

Dictionary containing all configuration sections: - ‘observed_data’: Dictionary with time window and sampling parameters - ‘source_position’: Dictionary with event location and focal mechanism - ‘fault_plane’: Dictionary with fault geometry discretization - ‘ellipse’: Dictionary with ellipse model and frequency band - ‘inversion_params’: Dictionary with inversion parameter ranges - ‘inversion_process’: Dictionary with algorithm settings - ‘moment_tensor’: Dictionary with moment tensor components - ‘stations’: List of station dictionaries (lat, lon, height, name, use_n, use_e, use_z) - ‘velocity_model’: List of velocity layer dictionaries

axitra_dirstr, optional

Path to axitra directory. If not provided, assumes it’s a sibling folder.

Returns:

AxitraForwardModel

Initialized forward model instance with all configuration loaded from params.

Example:

>>> params = {
...     'observed_data': {'Time window start (t1)': 0.0, ...},
...     'source_position': {'Latitude': -20.0, 'Longitude': -70.0, ...},
...     'fault_plane': {'Length along strike (Lx)': 100000.0, ...},
...     'ellipse': {'Number of ellipses': 1, ...},
...     'stations': [
...         {'latitude': -19.5, 'longitude': -69.5, 'height': 0.0, 'name': 'SL01'},
...         ...
...     ],
...     'velocity_model': [
...         {'thickness': 10000.0, 'vp': 5500.0, 'vs': 3200.0, 'rho': 2700.0, 'qp': 100.0, 'qs': 50.0},
...         ...
...     ],
...     'inversion_params': {...},
...     'inversion_process': {...},
...     'moment_tensor': {...},
... }
>>> fm = AxitraForwardModel.from_params(params, axitra_dir='/path/to/axitra')
green(ap, quiet: bool = True)[source]
model_array() → numpy.ndarray[source]
plot(synthetic: numpy.ndarray, time: numpy.ndarray | None = None, show: bool = True, save_path: str | Path | None = None, dpi: int = 300)[source]

Plot synthetic seismograms generated by this forward model. (Grafica los sismogramas sintéticos generados por este modelo forward.)

select_source_function() → dict[source]

Interactive prompt to select source time function, rise time and output type, mirroring the axitra Fortran convms behavior.

Source types (passed directly to moment.conv as source_type):

0 : Dirac 1 : Ricker -> requires rise time (t0) 2 : Smooth step -> requires rise time (t0) 3 : From file <axi_id.sou> -> sfunc array must be supplied externally 4 : Triangle -> requires rise time (t0) 5 : Ramp -> requires rise time (t0) 7 : True step -> requires rise time (t0) 8 : Trapezoid -> requires rise time (t0) AND flat time (t1) +10: compute only the source time function, no convolution

Returns a dict with keys:

source_type : int (may include +10 flag) t0 : float (rise time, or delta for Dirac) t1 : float (flat time for Trapezoid, 0.0 otherwise) unit : int (1=disp, 2=vel, 3=acc) sfunc : None (caller must set this for source_type=3)

stations_array(latlon: bool = True) → numpy.ndarray[source]
suggested_fmax_duration() → Tuple[float, float][source]
class kdellipspy.BaseInversionModel(input_ctl_path: str | Path | None = None, axitra_dir: str | Path | None = None, observed_waveforms: numpy.ndarray | None = None, time_array: numpy.ndarray | None = None, azi_times_array: numpy.ndarray | None = None, axitra_aw: float = 0.5, axitra_ikmax: int = 100000, config: ConfigParser | None = None)[source]

Bases: object

Abstract base class for kinematic inversion drivers (NA and MCMC). (Clase base abstracta para los motores de inversión cinemática NA y MCMC.)

Sub-classes must call super().__init__(...) and then implement either run_na_search or run_mcmc_search.

Parameters:
  • input_ctl_path (Path to input.ctl configuration file)

  • axitra_dir (Path to axitra binary directory (optional))

  • observed_waveforms (Observed 3-component seismograms, shape (nsta, 3, npts))

  • time_array (Time vector, shape (npts,))

  • azi_times_array (Pre-computed P/S arrival time table, shape (nsta, 3))

best_synthetics
Type:

np.ndarray | None — Updated whenever a new best misfit is found

clear_green_cache() → None[source]

Clean up the cached Green’s-function axitra files (call at end of run).

classmethod from_config(config: ConfigParser, axitra_dir: str | Path | None = None, observed_waveforms: numpy.ndarray | None = None, time_array: numpy.ndarray | None = None, azi_times_array: numpy.ndarray | None = None, **kwargs) → BaseInversionModel[source]

Create an inversion model directly from a ConfigParser object.

classmethod from_params(params: Dict[str, Any], axitra_dir: str | Path | None = None, observed_waveforms: numpy.ndarray | None = None, time_array: numpy.ndarray | None = None, azi_times_array: numpy.ndarray | None = None, **kwargs) → BaseInversionModel[source]

Create an inversion model from a dictionary of parameters.

objective_function(model: numpy.ndarray) → float[source]

Evaluate the forward model and return L2 misfit for one parameter vector. (Evalúa el modelo forward y retorna el desajuste L2 para un vector de parámetros.)

Side-effects

  • Updates self._best_misfit_seen and self.best_synthetics when improved.

  • Cleans axitra temporary files unless the active config sets keep_axitra_files=True.

plot_fit(show: bool = True, save_path: str | None = None) → Tuple[Any, Any][source]

Grafica el mejor ajuste de formas de onda encontrado hasta ahora.

class kdellipspy.ConfigParser(filepath: str | Path | None = None)[source]

Bases: object

Parses and stores the inversion configuration from the ‘input.ctl’ file.

classmethod from_dict(params: Dict[str, Any]) → ConfigParser[source]

Create a ConfigParser instance from a dictionary of parameters.

static get_base_template_path() → Path[source]
parse() → None[source]
plot_stations(show: bool = True, save_path: str | None = None, use_cartopy: bool = True) → Tuple[Any, Any][source]

Grafica la ubicación de las estaciones e hipocentro.

save(output_path: str | Path | None = None) → None[source]
update_stations(keep_station_names: list[str]) → None[source]
update_stations_from_sac(raw_dir: str | Path, station_names: list[str]) → None[source]
kdellipspy.ConfigStation

alias of Station

class kdellipspy.DataPreprocessor(cfg: ConfigParser)[source]

Bases: object

Utility to prepare raw seismic data for axitra inversion. (Utilidad para preparar datos sísmicos crudos para la inversión con axitra.)

plot_record_section(raw_dir: Path, t_start: Any | None = None, t_end: Any | None = None, freqmin: float = 0.05, freqmax: float = 0.5, scale: float = 2.0, components: List[str] = ['Z'], station_names: List[str] | None = None)[source]

Visualizes waveforms from SAC files in a record section, similar to the provided notebook. (Visualiza formas de onda de archivos SAC en una sección de registro, similar al notebook proporcionado.)

process_raw_files(raw_dir: Path, output_dir: Path, freq1: float, freq2: float, t_start: Any = 0.0, t_end: Any | None = None, data_start_time: Any | None = None, units: int = 2, station_indices: List[int] | None = None, station_names: List[str] | None = None) → Dict[str, numpy.ndarray][source]

Loads concatenated raw velocity files, filters, trims with zero-padding, and saves Axitra-ready files. Supports UTCDateTime or float for timing.

If station_names is provided, it filters the stations by name. (Si se proporciona station_names, filtra las estaciones por nombre.)

class kdellipspy.DynamicInversionConfig(dynamic_root: 'Optional[str | Path]' = None, compile_sources: 'bool' = False, input_file: 'str' = 'input_dynamic_ellipse.txt', run_script: 'str' = 'run_dyn_inversion.sh', compile_script: 'str' = 'compile_dyn_src.sh', event_dir: 'str' = 'Evento')[source]

Bases: object

compile_script: str = 'compile_dyn_src.sh'
compile_sources: bool = False
dynamic_root: str | Path | None = None
event_dir: str = 'Evento'
input_file: str = 'input_dynamic_ellipse.txt'
run_script: str = 'run_dyn_inversion.sh'
class kdellipspy.DynamicInversionModel(dynamic_root: str | Path | None = None)[source]

Bases: object

class kdellipspy.EllipseParams(num_ellipses: int, initial_slip: int, slip_shape: int, freq1: float, freq2: float, t0: float, source_type: int, zerophase: bool = True)[source]

Bases: object

Section 4: Ellipse Parameters & Frequency Band

freq1: float
freq2: float
classmethod from_dict(params: Dict) → EllipseParams[source]
initial_slip: int
num_ellipses: int
slip_shape: int
source_type: int
t0: float
zerophase: bool = True
class kdellipspy.EllipticalSlipMapper(config: ConfigParser)[source]

Bases: object

Apply ellipse-based slip factors to fault geometry source points.

apply_to_geometry(geom: FaultGeometry, model: numpy.ndarray, keep_all_sources: bool = False) → FaultGeometry[source]

Apply the ellipse slip model to geom.

When keep_all_sources is True the source-point set is NOT pruned to the subfaults inside the ellipse: every source point is kept with zero moment/slip outside the ellipse. This keeps the source set (positions and indices) constant across models, which lets the caller compute the Green’s functions once for the full mesh and reuse them via conv.

class kdellipspy.FaultGeometry(length_strike_m: float, length_dip_m: float, hypo_strike_m: float, hypo_dip_m: float, nx: int, ny: int, strike_deg: float, dip_deg: float, rake_deg: float, source_depth_m: float, source_lat: float, source_lon: float, rupture_velocity_km_s: float, mt_enabled: bool, subfaults: List[kdellipspy.core.geometry.Subfault], source_points: List[kdellipspy.core.geometry.SourcePoint])[source]

Bases: object

dip_deg: float
hypo_dip_m: float
hypo_strike_m: float
length_dip_m: float
length_strike_m: float
moment_magnitude_mw() → float[source]

Return moment magnitude Mw from total moment (Hanks & Kanamori, M0 in N.m).

mt_enabled: bool
property nsources: int
property nsubfaults: int
nx: int
ny: int
plot(title: str = '2D Slip Distribution', show: bool = True, save_path: str | None = None) → Tuple[Any, Any][source]

Visualización 2D de la distribución de slip interpolada.

rake_deg: float
rupture_velocity_km_s: float
source_depth_m: float
source_lat: float
source_lon: float
source_points: List[SourcePoint]
strike_deg: float
subfaults: List[Subfault]
to_axitra_hist() → numpy.ndarray[source]
to_axitra_sources(latlon: bool = True) → numpy.ndarray[source]
total_moment_nm() → float[source]

Return total scalar seismic moment in N.m for current source set.

class kdellipspy.FaultPlaneParams(lx: float, ly: float, hx: float, hy: float, nx: int, ny: int)[source]

Bases: object

Section 3: Fault Plane Parameters

classmethod from_dict(params: Dict) → FaultPlaneParams[source]
hx: float
hy: float
lx: float
ly: float
nx: int
ny: int
class kdellipspy.GeometryBuilder(config: ConfigParser)[source]

Bases: object

Build fault geometry from input.ctl through ConfigParser.

build(slip_geom: float = 1.0) → FaultGeometry[source]
classmethod from_config(config: ConfigParser) → GeometryBuilder[source]
classmethod from_input_ctl(input_ctl_path: str) → GeometryBuilder[source]
classmethod from_params(params: dict) → GeometryBuilder[source]
kdellipspy.GeometryStation

alias of Station

class kdellipspy.InversionParam(name: str, min_val: float, max_val: float, flag: int)[source]

Bases: object

Single parameter for inversion

flag: int
max_val: float
min_val: float
name: str
class kdellipspy.InversionParams(parameters: List[InversionParam])[source]

Bases: object

Section 5: Parameters to Invert

classmethod from_dict(params: Dict, param_lines: List[str]) → InversionParams[source]
parameters: List[InversionParam]
class kdellipspy.InversionProcessParams(algorithm_type: int, num_iterations: int, ss1: int, ss_other: int, cells_resample: int, misfit_time_window: float = 0.0, n_jobs: int = 1, mcmc_total_steps: int = 500, mcmc_burn_in: int = 0, mcmc_proposal_scale: float = 0.08, mcmc_thin: int = 1, mcmc_chains: int = 1)[source]

Bases: object

Section 6: Inversion Process Parameters

algorithm_type: int
cells_resample: int
classmethod from_dict(params: Dict) → InversionProcessParams[source]
mcmc_burn_in: int = 0
mcmc_chains: int = 1
mcmc_proposal_scale: float = 0.08
mcmc_thin: int = 1
mcmc_total_steps: int = 500
misfit_time_window: float = 0.0
n_jobs: int = 1
num_iterations: int
ss1: int
ss_other: int
class kdellipspy.MCMCConfig(total_steps: int = 500, burn_in: int = 0, proposal_scale: float = 0.08, thin: int = 1, random_seed: int | None = None, keep_axitra_files: bool = False, chains: int = 1)[source]

Bases: object

Hyperparameters for the MCMC search with PyMC + ArviZ. (Hiperparámetros para la búsqueda MCMC con PyMC + ArviZ.)

PyMC runs a Metropolis sampler with a uniform prior on the parameter box. ArviZ is used for posterior summary statistics.

total_steps
Type:

Total number of MCMC steps per chain (tune + draws)

burn_in
Type:

Number of tuning steps (excluded from posterior)

proposal_scale

each parameter’s range, e.g. 0.08 → 8 % of width per param

Type:

Metropolis proposal standard deviation as a fraction of

thin
Type:

Thinning factor applied to the posterior draws

random_seed
Type:

Optional seed for reproducibility

keep_axitra_files
Type:

Keep temporary axitra files (useful for debugging)

chains

Warning: chains > 1 may fail when axitra uses shared temp files.

Type:

Number of parallel MCMC chains.

burn_in: int = 0
chains: int = 1
keep_axitra_files: bool = False
proposal_scale: float = 0.08
random_seed: int | None = None
thin: int = 1
total_steps: int = 500
class kdellipspy.MCMCInversionModel(input_ctl_path: str | Path | None = None, axitra_dir: str | Path | None = None, observed_waveforms: numpy.ndarray | None = None, time_array: numpy.ndarray | None = None, azi_times_array: numpy.ndarray | None = None, axitra_aw: float = 0.5, axitra_ikmax: int = 100000, config: ConfigParser | None = None)[source]

Bases: BaseInversionModel

Kinematic Inversion Model using MCMC (Metropolis, PyMC + ArviZ). (Modelo de Inversión Cinemática usando MCMC — PyMC + ArviZ.)

Integrates observed data, arrival times, and event configuration to sample the posterior distribution of kinematic rupture model parameters. (Integra los datos observados, tiempos de llegada y configuración del evento para muestrear la distribución posterior de los parámetros del modelo cinemático.)

Parameters:
  • input_ctl_path (Path to input.ctl configuration file (optional if config is provided))

  • axitra_dir (Path to axitra binary directory (optional))

  • observed_waveforms (3-component seismograms, shape (nsta, 3, npts))

  • time_array (Time vector, shape (npts,))

  • azi_times_array (P/S arrival time table, shape (nsta, 3))

  • config (ConfigParser object (optional if input_ctl_path is provided))

best_synthetics

Synthetic seismograms of the best model found so far (nsta, 3, npts).

Type:

np.ndarray | None

Example

>>> # Using a config file path:
>>> model = MCMCInversionModel(
...     "path/to/run/input.ctl",
...     axitra_dir="path/to/axitra",
...     observed_waveforms=obs,
...     time_array=t,
...     azi_times_array=azi,
... )
>>> # Or using a ConfigParser object:
>>> cfg = ConfigParser("path/to/input.ctl")
>>> model = MCMCInversionModel(
...     config=cfg,
...     observed_waveforms=obs,
...     time_array=t,
...     azi_times_array=azi,
... )
>>> result = model.run_mcmc_search()
>>> print(result.best_model.misfit)

Run the PyMC Metropolis sampler and return posterior models. (Ejecuta el muestreador Metropolis de PyMC y retorna los modelos posteriores.)

Parameters:

mc_config (MCMCConfig, optional) – MCMC hyperparameters. If None, values are read from self.cfg.inversion_process (input.ctl).

Returns:

Container with posterior samples, the best model (minimum misfit), ArviZ summary in extra_metadata, and JSON/CSV export helpers.

Return type:

NAResult

Raises:
  • ImportError – If pymc (or arviz / pytensor) is not installed.

  • ValueError – If burn_in >= total_steps.

class kdellipspy.MisfitCalculator(observed_waveforms: numpy.ndarray, time_array: numpy.ndarray, azi_times_path: Path | None = None, azi_times_array: numpy.ndarray | None = None, time_window_s: float = 20.0, station_flags: numpy.ndarray | None = None)[source]

Bases: object

L2 waveform misfit calculator with P/S arrival-time windows. (Calculador de desajuste L2 de formas de onda con ventanas de llegada P/S.)

Parameters:
  • observed_waveforms (np.ndarray, shape (nsta, 3, npts))

  • time_array (np.ndarray, shape (npts,))

  • azi_times_path (Path to ASCII azi_times.txt (3 columns: azi, tP, tS))

  • azi_times_array (Pre-loaded azi_times as np.ndarray (takes priority))

  • time_window_s (Duration (s) of the P and S analysis windows)

diagnostics_summary(synthetic: numpy.ndarray, max_stations: int = 3) → str[source]

Compact per-station diagnostics string for one model evaluation. (Resumen diagnóstico compacto por estación para una evaluación de modelo.)

l2_misfit(synthetic: numpy.ndarray, use_full_signal: bool = False) → float[source]

Compute normalised L2 misfit in P (radial+vertical) and S (transverse) windows. (Calcula el desajuste L2 normalizado en ventanas P (radial+vertical) y S (transversal).)

class kdellipspy.MomentTensor(flag: int, mrr: float, mtt: float, mpp: float, mrt: float, mrp: float, mtp: float, exponent: float, scaling_mode: str = 'no_mt')[source]

Bases: object

Section 7: Moment Tensor (Full MT)

exponent: float
flag: int
classmethod from_dict(params: Dict) → MomentTensor[source]
mpp: float
mrp: float
mrr: float
mrt: float
mtp: float
mtt: float
scaling_mode: str = 'no_mt'
class kdellipspy.NAConfig(n_samples_initial: int = 30, n_samples_iteration: int = 30, n_iterations: int = 10, n_cells_resample: int = 7, n_jobs: int = 1, random_seed: int | None = None, keep_axitra_files: bool = False)[source]

Bases: object

Hyperparameters for the Neighbourhood Algorithm search. (Hiperparámetros para la búsqueda con el Algoritmo Neighbourhood.)

n_samples_initial
Type:

Number of random samples drawn in the first iteration (ni)

n_samples_iteration
Type:

Samples drawn around the best Voronoi cells per iteration (ns)

n_iterations
Type:

Number of NA iterations after the initial random stage (n)

n_cells_resample
Type:

Number of best Voronoi cells to resample from (nr)

n_jobs
Type:

Number of parallel workers for objective function evaluation (-1 for all)

random_seed
Type:

Optional seed for reproducibility

keep_axitra_files
Type:

Keep temporary axitra files (useful for debugging)

keep_axitra_files: bool = False
n_cells_resample: int = 7
n_iterations: int = 10
n_jobs: int = 1
n_samples_initial: int = 30
n_samples_iteration: int = 30
random_seed: int | None = None
class kdellipspy.NAInversionModel(input_ctl_path: str | Path | None = None, axitra_dir: str | Path | None = None, observed_waveforms: numpy.ndarray | None = None, time_array: numpy.ndarray | None = None, azi_times_array: numpy.ndarray | None = None, axitra_aw: float = 0.5, axitra_ikmax: int = 100000, config: ConfigParser | None = None)[source]

Bases: BaseInversionModel

Kinematic Inversion Model using the Neighbourhood Algorithm (NA). (Modelo de Inversión Cinemática utilizando el Algoritmo Neighbourhood — NA.)

Integrates observed data, arrival times, and event configuration to evaluate kinematic rupture models. Communicates with axitra to simulate synthetic seismograms and compute misfit against real data. (Integra los datos observados, tiempos de llegada y configuración del evento para evaluar distintos modelos de ruptura cinemática. Se comunica con axitra para simular sismogramas sintéticos y calcular el desajuste con los datos reales.)

Parameters:
  • input_ctl_path (Path to input.ctl configuration file (optional if config is provided))

  • axitra_dir (Path to axitra binary directory (optional))

  • observed_waveforms (3-component seismograms, shape (nsta, 3, npts))

  • time_array (Time vector, shape (npts,))

  • azi_times_array (P/S arrival time table, shape (nsta, 3))

  • config (ConfigParser object (optional if input_ctl_path is provided))

best_synthetics

Synthetic seismograms of the best model found so far (nsta, 3, npts). Set to None until the first objective function evaluation.

Type:

np.ndarray | None

param_names
Type:

List[str] — Human-readable parameter labels

param_ranges
Type:

np.ndarray, shape (n_params, 2) — [min, max] per parameter

Example

>>> # Using a config file path:
>>> model = NAInversionModel(
...     "path/to/run/input.ctl",
...     axitra_dir="path/to/axitra",
...     observed_waveforms=obs,
...     time_array=t,
...     azi_times_array=azi,
... )
>>> # Or using a ConfigParser object:
>>> cfg = ConfigParser("path/to/input.ctl")
>>> model = NAInversionModel(
...     config=cfg,
...     observed_waveforms=obs,
...     time_array=t,
...     azi_times_array=azi,
... )
>>> result = model.run_na_search()
>>> print(result.best_model.misfit)

Run the Neighbourhood Algorithm search and return all sampled models. (Ejecuta la búsqueda con el Algoritmo Neighbourhood y retorna todos los modelos.)

Parameters:

na_config (NAConfig, optional) – Search hyperparameters. If None, values are read from self.cfg.inversion_process (input.ctl).

Returns:

Container with every evaluated model, the best model, and JSON/CSV export helpers.

Return type:

NAResult

Raises:

ImportError – If neighpy is not installed.

class kdellipspy.NAModel(model: numpy.ndarray, misfit: float, iteration: int)[source]

Bases: object

Single sampled model and its objective value. (Modelo muestreado individual y su valor objetivo.)

model
Type:

Parameter vector (np.ndarray)

misfit
Type:

Objective/misfit value (float)

iteration
Type:

Iteration index at which this model was sampled (int)

iteration: int
misfit: float
model: numpy.ndarray
class kdellipspy.NAResult(all_models: List[NAModel], param_names: Sequence[str] | None = None, extra_metadata: Dict[str, Any] | None = None, best_synthetics: numpy.ndarray | None = None, observed: numpy.ndarray | None = None, time: numpy.ndarray | None = None, config: ConfigParser | None = None, azi_times_array: numpy.ndarray | None = None)[source]

Bases: object

Container for all sampled models with export helpers. (Contenedor de todos los modelos muestreados con exportadores.)

Parameters:
  • all_models (List of NAModel instances)

  • param_names (Parameter name labels (optional, defaults to 7-param names))

  • extra_metadata (Algorithm-specific metadata written to JSON export)

  • best_synthetics (Synthetic waveforms of the best model (optional))

  • observed (Observed waveforms used for inversion (optional))

  • time (Time array used for inversion (optional))

  • config (ConfigParser instance (optional))

export_csv(filepath: Path) → None[source]

Export all models as CSV (iteration, misfit, param columns). (Exporta todos los modelos como CSV con columnas de iteración, misfit y parámetros.)

export_results(filepath: Path) → None[source]

Export all models + metadata as JSON. (Exporta todos los modelos y metadatos como JSON.)

classmethod load(filepath: str | Path) → NAResult[source]

Load a NAResult object from a saved file.

plot(show: bool = True, save_path: str | None = None) → Tuple[Any, Any][source]

Grafica el resumen de resultados de la búsqueda NA.

plot_appraisal(samples: np.ndarray | None = None, show: bool = True, save_path: str | None = None, bins: int = 40, title: str | None = None, **run_kwargs: Any) → Tuple[Any, Any][source]

Corner plot of the posterior uncertainty (1D marginals + 2D trade-offs). (Gráfico ‘corner’ de la incertidumbre: marginales 1D y trade-offs 2D.)

If samples is not given and the appraisal has not been run yet, it is executed via run_appraisal() (extra kwargs are forwarded there, e.g. n_resample, temperature, seed).

plot_azimuthal(show: bool = True, save_path: str | None = None, ax: Any | None = None) → Tuple[Any, Any][source]

Diagrama polar (radar) de la cobertura azimutal de las estaciones respecto al epicentro, con el mayor hueco azimutal sombreado. ax (eje polar) permite componerlo dentro de plot_yolo.

plot_convergence(show: bool = True, save_path: str | None = None, fig: Any | None = None) → Tuple[Any, Any][source]

Grafica la convergencia detallada por cada parámetro.

plot_elipse(show: bool = True, save_path: str | None = None, title: str | None = None) → Tuple[Any, Any][source]

Alias por compatibilidad con el nombre en español.

plot_ellipse(show: bool = True, save_path: str | None = None, title: str | None = None, fig: Any | None = None) → Tuple[Any, Any][source]

Grafica la distribución de slip de la elipse para el mejor misfit. Requiere que config y best_model estén presentes en el objeto.

plot_ellipse_depth(show: bool = True, save_path: str | None = None) → Tuple[Any, Any][source]

Secciones transversales de la elipse de slip en profundidad (estilo legacy plot_geometry): corte N–S y corte E–O, coloreados por slip, con hipocentro.

plot_ellipse_map(show: bool = True, save_path: str | None = None, pad: float = 0.25, fig: Any | None = None) → Tuple[Any, Any][source]

Mapa cartopy de la elipse de slip proyectada a la superficie (footprint coloreado por slip + borde + hipocentro + estaciones).

plot_fit(show: bool = True, save_path: str | None = None, rotate: bool = True, mark_windows: bool = True, fig: Any | None = None) → Tuple[Any, Any][source]

Grafica el mejor ajuste de formas de onda encontrado. Requiere que best_synthetics, observed y time estén presentes en el objeto.

Por defecto rota a R/T/Z y sombrea la ventana del misfit (P→R,Z ; S→T), consistente con cómo se calcula el misfit. Si no hay datos de azimut/ tiempos disponibles, cae a las componentes N/E/Z sin ventana.

plot_misfit_breakdown(show: bool = True, save_path: str | None = None, window_s='auto', fig: Any | None = None) → Tuple[Any, Any][source]

Visualiza la contribución al misfit por estación/componente (heatmaps).

plot_yolo(save_path: str | Path = 'dashboard.pdf', show: bool = False, dpi: int = 200) → Path | None[source]

🎲 YOLO: arma un DASHBOARD de una sola página con todos los paneles encajados (GridSpec):

┌───────────────── título (evento · misfit · Mw) ─────────────────┐ │ mapa elipse (cartopy) │ ajuste R/T/Z (7 est, ventanas)│ │ azimutal (radar) │ heatmap mf │ (panel alto) │ │ convergencia de parámetros │ appraisal (corner) │ └─────────────────────────────────────────────────────────────────┘

Cada panel se renderiza con su método y se compone como imagen, así se reaprovecha todo el código existente (cartopy, corner, etc.). El appraisal se corre automáticamente si no estaba.

run_appraisal(n_resample: int = 20000, n_walkers: int = 1, temperature: float | None = None, bounds: numpy.ndarray | None = None, seed: int | None = None, verbose: bool = True, save: bool = True) → numpy.ndarray | None[source]

Run the NA appraisal stage to approximate the posterior. (Ejecuta la etapa de ‘appraisal’ del NA para aproximar la posterior.)

Reuses the models already evaluated during the NA search (all_models) and resamples their Voronoi cells with neighpy.NAAppraiser — it does NOT evaluate the forward model again. Populates appraisal_samples, appraisal_mean and appraisal_covariance.

Parameters:
  • n_resample (length of the resampling random walk (more = smoother PDFs).)

  • n_walkers (parallel walkers (>1 uses neighpy's multiprocessing).)

  • temperature (misfit-to-log-posterior scale, log_ppd = -misfit / T.) – If None, T = 2 * best_misfit (auto-scale). SMALLER T → narrower posterior (more trust in the data). NOTE: the misfit here is the normalized L2 misfit, not a noise-calibrated chi-square, so temperature implicitly sets the assumed noise level. Tune it (or pass an explicit value) if you need calibrated uncertainties.

  • bounds ((n_params, 2) optional; derived from the config if None.)

  • seed (forwarded to NAAppraiser / NAAppraiser.run.)

  • verbose (forwarded to NAAppraiser / NAAppraiser.run.)

  • save (forwarded to NAAppraiser / NAAppraiser.run.)

Returns:

Posterior ensemble, shape (n_samples, n_params), or None if save=False.

Return type:

np.ndarray | None

save(filepath: str | Path) → None[source]

Save the entire NAResult object to a file for later reloading. Uses joblib if available (efficient for numpy arrays), otherwise uses pickle.

class kdellipspy.ObservedDataParams(t1: float, t2: float, npts: int, delta: float, units: int)[source]

Bases: object

Section 1: Observed Data Parameters

delta: float
classmethod from_dict(params: Dict) → ObservedDataParams[source]
npts: int
t1: float
t2: float
units: int
class kdellipspy.SourcePoint(index: int, subfault_index: int, x_m: float, y_m: float, z_m: float, rupture_time_s: float, moment: float = 0.0, displacement: float = 0.0, strike_deg: float = 0.0, dip_deg: float = 0.0, rake_deg: float = 0.0, width: float = 0.0, length: float = 0.0, mu_pa: float = 0.0, basis_slot: int = 0)[source]

Bases: object

basis_slot: int = 0
dip_deg: float = 0.0
displacement: float = 0.0
index: int
length: float = 0.0
moment: float = 0.0
mu_pa: float = 0.0
rake_deg: float = 0.0
rupture_time_s: float
strike_deg: float = 0.0
subfault_index: int
width: float = 0.0
x_m: float
y_m: float
z_m: float
class kdellipspy.SourcePosition(event_name: str, origin_time: str | None, latitude: float, longitude: float, depth: float, strike: float, dip: float, rake: float)[source]

Bases: object

Section 2: Source Position & Focal Mechanism

depth: float
dip: float
event_name: str
classmethod from_dict(params: Dict) → SourcePosition[source]
latitude: float
longitude: float
origin_time: str | None
rake: float
strike: float
class kdellipspy.StationGeometry(ref_lat: float, ref_lon: float, stations: List[kdellipspy.core.geometry.Station])[source]

Bases: object

property nstations: int
ref_lat: float
ref_lon: float
stations: List[Station]
to_axitra_stations(latlon: bool = False) → numpy.ndarray[source]
class kdellipspy.StationParams(stations: List[Station] = <factory>)[source]

Bases: object

Section 8: Station Parameters

classmethod from_lines(station_lines: List[str]) → StationParams[source]
stations: List[Station]
class kdellipspy.Subfault(index: int, x_m: float, y_m: float, z_m: float, rupture_time_s: float, mu_pa: float = 0.0, area_m2: float = 0.0, slip_m: float = 0.0, lat: float = 0.0, lon: float = 0.0)[source]

Bases: object

area_m2: float = 0.0
index: int
lat: float = 0.0
lon: float = 0.0
mu_pa: float = 0.0
rupture_time_s: float
slip_m: float = 0.0
x_m: float
y_m: float
z_m: float
class kdellipspy.UTMProjection(lat0_deg: float, lon0_deg: float)[source]

Bases: object

UTM (Universal Transverse Mercator) projection using pyproj.

Automatically detects the UTM zone based on the center coordinates. Uses WGS84 ellipsoid for accurate geodetic transformations. This replaces the legacy Lambert conformal projection with a faster (C-native) and more standard approach.

latlon_to_xy(lat_deg: float, lon_deg: float) → Tuple[float, float][source]

Convert lat/lon (degrees) to UTM Easting/Northing (meters).

xy_to_latlon(easting_m: float, northing_m: float) → Tuple[float, float][source]

Convert UTM Easting/Northing (meters) to lat/lon (degrees).

class kdellipspy.VelocityLayer(thickness: float, vp: float, vs: float, rho: float, qp: float, qs: float)[source]

Bases: object

Single velocity layer

qp: float
qs: float
rho: float
thickness: float
vp: float
vs: float
class kdellipspy.VelocityModel(layers: List[VelocityLayer] = <factory>)[source]

Bases: object

Section 9: Velocity Model 1D

classmethod from_lines(layer_lines: List[str]) → VelocityModel[source]
layers: List[VelocityLayer]
to_numpy() → numpy.ndarray[source]
kdellipspy.bandpass_filter_waveforms(synthetic: numpy.ndarray, time: numpy.ndarray, freq1: float, freq2: float, corners: int = 4, zerophase: bool = True) → numpy.ndarray[source]

Apply Butterworth bandpass filter to synthetic waveforms.

This ensures synthetic waveforms match the frequency band of observed data, maintaining consistency in the misfit calculation.

Parameters:
  • synthetic – waveform array with shape (n_stations, 3, npts)

  • time – time array with shape (npts,)

  • freq1 – low frequency cutoff (Hz)

  • freq2 – high frequency cutoff (Hz)

  • corners – filter order (default 4 = 4th order)

  • zerophase – if True, apply filter forward-backward for zero phase distortion

Returns:

filtered waveforms with same shape as input

Return type:

filtered_synthetic

Raises:

ValueError – if time array has < 2 points or freq2 >= Nyquist frequency

kdellipspy.build_azi_times_array(input_ctl_path: str | Path | None = None, model_name: str = 'iasp91', p_shift_s: float = -1.0, s_shift_s: float = -1.0, use_iris: bool = False, config: ConfigParser | None = None) → numpy.ndarray[source]

Build legacy-compatible azi_times values as an array with columns: [azimuth_rad, tP, tS].

This reproduces the old script behavior while avoiding IRIS network requirements by default (use_iris=False).

kdellipspy.build_geometry_from_input_ctl(input_ctl_path: str, slip_geom: float = 1.0) → FaultGeometry[source]
kdellipspy.build_station_geometry(ref_lat: float, ref_lon: float, station_data: List[Tuple[str, float, float, float]]) → StationGeometry[source]
kdellipspy.integrate_waveforms(waveforms: numpy.ndarray, delta: float, baseline_samples: int | None = None, steps: int = 1) → numpy.ndarray[source]

Integrate waveforms along the last axis using a digital filter (Matlab integraf.m logic). Cleanest results for seismic signals.

Parameters:
  • waveforms – array with shape (…, npts)

  • delta – sampling interval in seconds

  • baseline_samples – Number of samples at the start for pre-event baseline correction.

  • steps – Number of integration steps (1 for velocity, 2 for displacement).

Returns:

Integrated waveforms with the same shape as the input.

kdellipspy.load_and_filter_observed_data(freq1: float | None = None, freq2: float | None = None, input_ctl_path: str | Path | None = None, data_dir: str | Path = 'DATA', prefer_raw: bool = False, config: ConfigParser | None = None) → Tuple[numpy.ndarray, numpy.ndarray][source]

Load observed data with two supported user workflows:

  1. Flat files in DATA: - real_disp_x/y/z (if input.ctl units=1) - real_vel_x/y/z (if input.ctl units=2) Each file must contain 2 columns: time and data, and total rows must be npts * nstations.

  2. RAW files in DATA/RAW: - .sac or .mseed files - station name extracted from waveform metadata - bandpass filtering using input.ctl frequency band - response removal attempted when inventory files are present in RAW

Returns:

ndarray with shape (n_stations, 3, npts) time_array: ndarray with shape (npts,)

Return type:

observed_waveforms

kdellipspy.parse_velocity_model(filepath: str) → List[Dict[str, float]][source]

Compatibility helper for existing code path.

kdellipspy.precompute_greens_functions(params, v_model)[source]

Compatibility shim for legacy calls.

kdellipspy.read_input_ctl(filepath: str) → Dict[str, Any][source]

Compatibility helper returning a flat dictionary used by the pipeline.

kdellipspy.validate_input_ctl(filepath: str) → Dict[str, Any][source]

Return a concise parse summary for smoke testing.

kdellipspy.write_azi_times_file(input_ctl_path: str | Path | None = None, output_path: str | Path | None = None, model_name: str = 'iasp91', p_shift_s: float = -1.0, s_shift_s: float = -1.0, use_iris: bool = False, config: ConfigParser | None = None) → Path[source]

Write Event/azi_times.txt with legacy-compatible 3-column format.

Core Configuration & Parsing

class kdellipspy.core.config_parser.ConfigParser(filepath: str | Path | None = None)[source]

Bases: object

Parses and stores the inversion configuration from the ‘input.ctl’ file.

classmethod from_dict(params: Dict[str, Any]) → ConfigParser[source]

Create a ConfigParser instance from a dictionary of parameters.

static get_base_template_path() → Path[source]
parse() → None[source]
plot_stations(show: bool = True, save_path: str | None = None, use_cartopy: bool = True) → Tuple[Any, Any][source]

Grafica la ubicación de las estaciones e hipocentro.

save(output_path: str | Path | None = None) → None[source]
update_stations(keep_station_names: list[str]) → None[source]
update_stations_from_sac(raw_dir: str | Path, station_names: list[str]) → None[source]
class kdellipspy.core.config_parser.EllipseParams(num_ellipses: int, initial_slip: int, slip_shape: int, freq1: float, freq2: float, t0: float, source_type: int, zerophase: bool = True)[source]

Bases: object

Section 4: Ellipse Parameters & Frequency Band

freq1: float
freq2: float
classmethod from_dict(params: Dict) → EllipseParams[source]
initial_slip: int
num_ellipses: int
slip_shape: int
source_type: int
t0: float
zerophase: bool = True
class kdellipspy.core.config_parser.FaultPlaneParams(lx: float, ly: float, hx: float, hy: float, nx: int, ny: int)[source]

Bases: object

Section 3: Fault Plane Parameters

classmethod from_dict(params: Dict) → FaultPlaneParams[source]
hx: float
hy: float
lx: float
ly: float
nx: int
ny: int
class kdellipspy.core.config_parser.InversionParam(name: str, min_val: float, max_val: float, flag: int)[source]

Bases: object

Single parameter for inversion

flag: int
max_val: float
min_val: float
name: str
class kdellipspy.core.config_parser.InversionParams(parameters: List[InversionParam])[source]

Bases: object

Section 5: Parameters to Invert

classmethod from_dict(params: Dict, param_lines: List[str]) → InversionParams[source]
parameters: List[InversionParam]
class kdellipspy.core.config_parser.InversionProcessParams(algorithm_type: int, num_iterations: int, ss1: int, ss_other: int, cells_resample: int, misfit_time_window: float = 0.0, n_jobs: int = 1, mcmc_total_steps: int = 500, mcmc_burn_in: int = 0, mcmc_proposal_scale: float = 0.08, mcmc_thin: int = 1, mcmc_chains: int = 1)[source]

Bases: object

Section 6: Inversion Process Parameters

algorithm_type: int
cells_resample: int
classmethod from_dict(params: Dict) → InversionProcessParams[source]
mcmc_burn_in: int = 0
mcmc_chains: int = 1
mcmc_proposal_scale: float = 0.08
mcmc_thin: int = 1
mcmc_total_steps: int = 500
misfit_time_window: float = 0.0
n_jobs: int = 1
num_iterations: int
ss1: int
ss_other: int
class kdellipspy.core.config_parser.MomentTensor(flag: int, mrr: float, mtt: float, mpp: float, mrt: float, mrp: float, mtp: float, exponent: float, scaling_mode: str = 'no_mt')[source]

Bases: object

Section 7: Moment Tensor (Full MT)

exponent: float
flag: int
classmethod from_dict(params: Dict) → MomentTensor[source]
mpp: float
mrp: float
mrr: float
mrt: float
mtp: float
mtt: float
scaling_mode: str = 'no_mt'
class kdellipspy.core.config_parser.ObservedDataParams(t1: float, t2: float, npts: int, delta: float, units: int)[source]

Bases: object

Section 1: Observed Data Parameters

delta: float
classmethod from_dict(params: Dict) → ObservedDataParams[source]
npts: int
t1: float
t2: float
units: int
class kdellipspy.core.config_parser.SourcePosition(event_name: str, origin_time: str | None, latitude: float, longitude: float, depth: float, strike: float, dip: float, rake: float)[source]

Bases: object

Section 2: Source Position & Focal Mechanism

depth: float
dip: float
event_name: str
classmethod from_dict(params: Dict) → SourcePosition[source]
latitude: float
longitude: float
origin_time: str | None
rake: float
strike: float
class kdellipspy.core.config_parser.Station(latitude: float, longitude: float, height: float, name: str, use_n: bool = True, use_e: bool = True, use_z: bool = True)[source]

Bases: object

Individual station parameters

height: float
latitude: float
longitude: float
name: str
use_e: bool = True
use_n: bool = True
use_z: bool = True
class kdellipspy.core.config_parser.StationParams(stations: List[Station] = <factory>)[source]

Bases: object

Section 8: Station Parameters

classmethod from_lines(station_lines: List[str]) → StationParams[source]
stations: List[Station]
class kdellipspy.core.config_parser.VelocityLayer(thickness: float, vp: float, vs: float, rho: float, qp: float, qs: float)[source]

Bases: object

Single velocity layer

qp: float
qs: float
rho: float
thickness: float
vp: float
vs: float
class kdellipspy.core.config_parser.VelocityModel(layers: List[VelocityLayer] = <factory>)[source]

Bases: object

Section 9: Velocity Model 1D

classmethod from_lines(layer_lines: List[str]) → VelocityModel[source]
layers: List[VelocityLayer]
to_numpy() → numpy.ndarray[source]
kdellipspy.core.config_parser.parse_velocity_model(filepath: str) → List[Dict[str, float]][source]

Compatibility helper for existing code path.

kdellipspy.core.config_parser.read_input_ctl(filepath: str) → Dict[str, Any][source]

Compatibility helper returning a flat dictionary used by the pipeline.

kdellipspy.core.config_parser.validate_input_ctl(filepath: str) → Dict[str, Any][source]

Return a concise parse summary for smoke testing.

Forward Modeling

class kdellipspy.core.forward_model.AxitraForwardModel(input_ctl_path: str, axitra_dir: str | None = None)[source]

Bases: object

Forward model using axitra python wrapper.

Main workflow: 1) Build invariant geometry from input.ctl 2) Apply ellipse model (a1,a2,theta,np,tp,dmax,vr) 3) Build Axitra instance with model/stations/sources 4) Compute Green functions: moment.green(ap) 5) Convolve: moment.conv(ap, hist, …)

apply_ellipse_model_to_geometry(geometry: FaultGeometry, model: numpy.ndarray, keep_all_sources: bool = False) → FaultGeometry[source]

Apply 7-parameter ellipse model to an existing geometry instance.

keep_all_sources=True keeps the full source set (zero moment outside the ellipse) so the Green’s functions can be cached for the fixed mesh.

build_axitra(geometry: FaultGeometry, fmax: float | None = None, duration: float | None = None, xl: float = 0.0, latlon: bool = True, freesurface: bool = True, ikmax: int = 100000, id: int | None = None, aw: float | None = 0.5)[source]
build_geometry(slip_geom: float = 1.0) → FaultGeometry[source]
build_geometry_with_ellipse_slip(model: numpy.ndarray) → FaultGeometry[source]

Build fault geometry with ellipse-to-subfault slip mapping.

Model parameters (7):

model[0]: a1 - semi-axis 1 (km) model[1]: a2 - semi-axis 2 (km) model[2]: theta - rotation angle (fraction of pi) model[3]: np - position fraction [0,1] model[4]: tp - position angle (fraction of 2pi) model[5]: dmax - maximum slip (m) model[6]: vr - rupture velocity (km/s)

Returns:

FaultGeometry with slip distribution applied from ellipse parameters.

conv(ap, geometry: FaultGeometry, source_type: int | None = None, t0: float | None = 3, t1: float = 0.0, unit: int | None = None, sfunc=None, quiet: bool = True)[source]
estimate_total_moment_and_mw(model: numpy.ndarray, geometry: FaultGeometry | None = None) → Tuple[float, float][source]

Estimate total scalar moment (N.m) and Mw for a 7-parameter ellipse model.

classmethod from_config(cfg: ConfigParser, axitra_dir: str | None = None) → AxitraForwardModel[source]

Create AxitraForwardModel directly from a ConfigParser object. (Crea AxitraForwardModel directamente desde un objeto ConfigParser.)

classmethod from_params(params: dict, axitra_dir: str | None = None) → AxitraForwardModel[source]

Create AxitraForwardModel from a dictionary of parameters instead of an input.ctl file.

This allows building the forward model programmatically without file I/O.

Parameters:

paramsdict

Dictionary containing all configuration sections: - ‘observed_data’: Dictionary with time window and sampling parameters - ‘source_position’: Dictionary with event location and focal mechanism - ‘fault_plane’: Dictionary with fault geometry discretization - ‘ellipse’: Dictionary with ellipse model and frequency band - ‘inversion_params’: Dictionary with inversion parameter ranges - ‘inversion_process’: Dictionary with algorithm settings - ‘moment_tensor’: Dictionary with moment tensor components - ‘stations’: List of station dictionaries (lat, lon, height, name, use_n, use_e, use_z) - ‘velocity_model’: List of velocity layer dictionaries

axitra_dirstr, optional

Path to axitra directory. If not provided, assumes it’s a sibling folder.

Returns:

AxitraForwardModel

Initialized forward model instance with all configuration loaded from params.

Example:

>>> params = {
...     'observed_data': {'Time window start (t1)': 0.0, ...},
...     'source_position': {'Latitude': -20.0, 'Longitude': -70.0, ...},
...     'fault_plane': {'Length along strike (Lx)': 100000.0, ...},
...     'ellipse': {'Number of ellipses': 1, ...},
...     'stations': [
...         {'latitude': -19.5, 'longitude': -69.5, 'height': 0.0, 'name': 'SL01'},
...         ...
...     ],
...     'velocity_model': [
...         {'thickness': 10000.0, 'vp': 5500.0, 'vs': 3200.0, 'rho': 2700.0, 'qp': 100.0, 'qs': 50.0},
...         ...
...     ],
...     'inversion_params': {...},
...     'inversion_process': {...},
...     'moment_tensor': {...},
... }
>>> fm = AxitraForwardModel.from_params(params, axitra_dir='/path/to/axitra')
green(ap, quiet: bool = True)[source]
model_array() → numpy.ndarray[source]
plot(synthetic: numpy.ndarray, time: numpy.ndarray | None = None, show: bool = True, save_path: str | Path | None = None, dpi: int = 300)[source]

Plot synthetic seismograms generated by this forward model. (Grafica los sismogramas sintéticos generados por este modelo forward.)

select_source_function() → dict[source]

Interactive prompt to select source time function, rise time and output type, mirroring the axitra Fortran convms behavior.

Source types (passed directly to moment.conv as source_type):

0 : Dirac 1 : Ricker -> requires rise time (t0) 2 : Smooth step -> requires rise time (t0) 3 : From file <axi_id.sou> -> sfunc array must be supplied externally 4 : Triangle -> requires rise time (t0) 5 : Ramp -> requires rise time (t0) 7 : True step -> requires rise time (t0) 8 : Trapezoid -> requires rise time (t0) AND flat time (t1) +10: compute only the source time function, no convolution

Returns a dict with keys:

source_type : int (may include +10 flag) t0 : float (rise time, or delta for Dirac) t1 : float (flat time for Trapezoid, 0.0 otherwise) unit : int (1=disp, 2=vel, 3=acc) sfunc : None (caller must set this for source_type=3)

stations_array(latlon: bool = True) → numpy.ndarray[source]
suggested_fmax_duration() → Tuple[float, float][source]
kdellipspy.core.forward_model.precompute_greens_functions(params, v_model)[source]

Compatibility shim for legacy calls.

Fault Geometry & Kinematics

class kdellipspy.core.geometry.EllipticalSlipMapper(config: ConfigParser)[source]

Bases: object

Apply ellipse-based slip factors to fault geometry source points.

apply_to_geometry(geom: FaultGeometry, model: numpy.ndarray, keep_all_sources: bool = False) → FaultGeometry[source]

Apply the ellipse slip model to geom.

When keep_all_sources is True the source-point set is NOT pruned to the subfaults inside the ellipse: every source point is kept with zero moment/slip outside the ellipse. This keeps the source set (positions and indices) constant across models, which lets the caller compute the Green’s functions once for the full mesh and reuse them via conv.

class kdellipspy.core.geometry.FaultGeometry(length_strike_m: float, length_dip_m: float, hypo_strike_m: float, hypo_dip_m: float, nx: int, ny: int, strike_deg: float, dip_deg: float, rake_deg: float, source_depth_m: float, source_lat: float, source_lon: float, rupture_velocity_km_s: float, mt_enabled: bool, subfaults: List[kdellipspy.core.geometry.Subfault], source_points: List[kdellipspy.core.geometry.SourcePoint])[source]

Bases: object

dip_deg: float
hypo_dip_m: float
hypo_strike_m: float
length_dip_m: float
length_strike_m: float
moment_magnitude_mw() → float[source]

Return moment magnitude Mw from total moment (Hanks & Kanamori, M0 in N.m).

mt_enabled: bool
property nsources: int
property nsubfaults: int
nx: int
ny: int
plot(title: str = '2D Slip Distribution', show: bool = True, save_path: str | None = None) → Tuple[Any, Any][source]

Visualización 2D de la distribución de slip interpolada.

rake_deg: float
rupture_velocity_km_s: float
source_depth_m: float
source_lat: float
source_lon: float
source_points: List[SourcePoint]
strike_deg: float
subfaults: List[Subfault]
to_axitra_hist() → numpy.ndarray[source]
to_axitra_sources(latlon: bool = True) → numpy.ndarray[source]
total_moment_nm() → float[source]

Return total scalar seismic moment in N.m for current source set.

class kdellipspy.core.geometry.GeometryBuilder(config: ConfigParser)[source]

Bases: object

Build fault geometry from input.ctl through ConfigParser.

build(slip_geom: float = 1.0) → FaultGeometry[source]
classmethod from_config(config: ConfigParser) → GeometryBuilder[source]
classmethod from_input_ctl(input_ctl_path: str) → GeometryBuilder[source]
classmethod from_params(params: dict) → GeometryBuilder[source]
class kdellipspy.core.geometry.SourcePoint(index: int, subfault_index: int, x_m: float, y_m: float, z_m: float, rupture_time_s: float, moment: float = 0.0, displacement: float = 0.0, strike_deg: float = 0.0, dip_deg: float = 0.0, rake_deg: float = 0.0, width: float = 0.0, length: float = 0.0, mu_pa: float = 0.0, basis_slot: int = 0)[source]

Bases: object

basis_slot: int = 0
dip_deg: float = 0.0
displacement: float = 0.0
index: int
length: float = 0.0
moment: float = 0.0
mu_pa: float = 0.0
rake_deg: float = 0.0
rupture_time_s: float
strike_deg: float = 0.0
subfault_index: int
width: float = 0.0
x_m: float
y_m: float
z_m: float
class kdellipspy.core.geometry.Station(index: int, name: str, x_m: float, y_m: float, z_m: float, lat: float, lon: float)[source]

Bases: object

index: int
lat: float
lon: float
name: str
x_m: float
y_m: float
z_m: float
class kdellipspy.core.geometry.StationGeometry(ref_lat: float, ref_lon: float, stations: List[kdellipspy.core.geometry.Station])[source]

Bases: object

property nstations: int
ref_lat: float
ref_lon: float
stations: List[Station]
to_axitra_stations(latlon: bool = False) → numpy.ndarray[source]
class kdellipspy.core.geometry.Subfault(index: int, x_m: float, y_m: float, z_m: float, rupture_time_s: float, mu_pa: float = 0.0, area_m2: float = 0.0, slip_m: float = 0.0, lat: float = 0.0, lon: float = 0.0)[source]

Bases: object

area_m2: float = 0.0
index: int
lat: float = 0.0
lon: float = 0.0
mu_pa: float = 0.0
rupture_time_s: float
slip_m: float = 0.0
x_m: float
y_m: float
z_m: float
class kdellipspy.core.geometry.UTMProjection(lat0_deg: float, lon0_deg: float)[source]

Bases: object

UTM (Universal Transverse Mercator) projection using pyproj.

Automatically detects the UTM zone based on the center coordinates. Uses WGS84 ellipsoid for accurate geodetic transformations. This replaces the legacy Lambert conformal projection with a faster (C-native) and more standard approach.

latlon_to_xy(lat_deg: float, lon_deg: float) → Tuple[float, float][source]

Convert lat/lon (degrees) to UTM Easting/Northing (meters).

xy_to_latlon(easting_m: float, northing_m: float) → Tuple[float, float][source]

Convert UTM Easting/Northing (meters) to lat/lon (degrees).

kdellipspy.core.geometry.build_geometry_from_input_ctl(input_ctl_path: str, slip_geom: float = 1.0) → FaultGeometry[source]
kdellipspy.core.geometry.build_station_geometry(ref_lat: float, ref_lon: float, station_data: List[Tuple[str, float, float, float]]) → StationGeometry[source]

Plotting Utilities (Object-Oriented)

Unified plotting functions for kdellipspy.

kdellipspy.core.plotting.plot_azimuthal_coverage(station_lats, station_lons, station_names, ev_lat: float, ev_lon: float, show: bool = True, save_path: str | Path | None = None, dpi: int = 200, ax: Any | None = None) → Tuple[Any, Any][source]

Diagrama polar (radar) de la cobertura azimutal de las estaciones respecto al epicentro. Ángulo = azimut evento→estación (N arriba, horario), radio = distancia epicentral (km). Sombrea el mayor hueco azimutal (gap).

Si se pasa ax (debe ser un eje polar) dibuja ahí y no guarda/abre figura (modo composición, p. ej. desde plot_yolo).

kdellipspy.core.plotting.plot_ellipse_map(lats, lons, slip, ev_lat: float, ev_lon: float, station_lats=None, station_lons=None, station_names=None, pad: float = 0.25, show: bool = True, save_path: str | Path | None = None, dpi: int = 200, fig: Any | None = None) → Tuple[Any, Any][source]

Mapa cartopy de la elipse de slip proyectada a la superficie: footprint (subfallas coloreadas por slip) + contorno del borde a 0.15*max_slip, hipocentro/epicentro y estaciones dentro del recuadro.

kdellipspy.core.plotting.plot_misfit_contribution(num: numpy.ndarray, den: numpy.ndarray, station_names: List[str], total_E: float, mode_label: str = '', show: bool = True, save_path: str | Path | None = None, dpi: int = 200, fig: Any | None = None) → Tuple[Any, Any][source]

Visualiza la ‘tabla’ de contribución al misfit como dos heatmaps estaciones×(R,T,Z): (izq) %% del residuo total que aporta cada componente; (der) misfit local num/den (calidad de ajuste). Anotados.

kdellipspy.core.plotting.plot_na_results(source_data: list[dict[str, float]], param_names: list[str], show: bool = True, save_path: str | Path | None = None, dpi: int = 300) → Tuple[Any, Any][source]

Plot summary of NA results.

kdellipspy.core.plotting.plot_parameter_convergence(rows: list[dict], param_names: list[str], misfits: numpy.ndarray, iterations: numpy.ndarray, show: bool = True, save_path: str | Path | None = None, dpi: int = 300, fig: Any | None = None) → Tuple[Any, Any][source]

Plot convergence for each parameter.

kdellipspy.core.plotting.plot_record_section(waveforms: numpy.ndarray, time: numpy.ndarray, station_names: List[str], distances: numpy.ndarray, comp_name: str = 'Z', scale: float = 1.0, show: bool = True, save_path: str | Path | None = None, dpi: int = 300) → Tuple[Any, Any][source]

Plot waveforms as a record section.

kdellipspy.core.plotting.plot_slip_distribution(geometry: FaultGeometry, title: str = '2D Slip Distribution', show: bool = True, save_path: str | Path | None = None, dpi: int = 300, fig: Any | None = None) → Tuple[Any, Any][source]

Plot 2D slip distribution on the fault-plane subfault grid (no interpolation).

kdellipspy.core.plotting.plot_stations_map(cfg: ConfigParser, show: bool = True, save_path: str | Path | None = None, dpi: int = 300, use_cartopy: bool = True) → Tuple[Any, Any][source]

Plot station locations on a map using Cartopy for geographic features if available.

kdellipspy.core.plotting.plot_synthetic_components(time: numpy.ndarray, synthetic: numpy.ndarray, station_names: List[str], show: bool = True, save_path: str | Path | None = None, dpi: int = 300) → Tuple[Any, Any][source]

Plot purely synthetic seismograms.

kdellipspy.core.plotting.plot_uncertainty_corner(samples: numpy.ndarray, param_names: List[str], bounds: numpy.ndarray | None = None, truths: numpy.ndarray | None = None, mean: numpy.ndarray | None = None, bins: int = 40, show: bool = True, save_path: str | Path | None = None, dpi: int = 300, title: str | None = None) → Tuple[Any, Any][source]

Corner plot of the NA appraisal posterior (1D marginals + 2D trade-offs). (Gráfico ‘corner’ de la posterior del appraisal NA: marginales 1D en la diagonal y densidades 2D —trade-offs entre parámetros— en el triángulo inferior.)

Parameters:
  • samples (np.ndarray, shape (n_samples, n_params)) – Posterior ensemble, e.g. NAAppraiser.samples.

  • param_names (list of str — axis labels (one per parameter).)

  • bounds (np.ndarray, shape (n_params, 2), optional) – [min, max] per parameter, used to fix the axis ranges.

  • truths (np.ndarray, shape (n_params,), optional) – Reference values marked in red (e.g. the best-fit model).

  • mean (np.ndarray, shape (n_params,), optional) – Posterior mean, marked with a green cross on the 2D panels.

  • bins (int — histogram bins for 1D and 2D panels.)

Return type:

(fig, axes)

kdellipspy.core.plotting.plot_waveform_fit(observed: numpy.ndarray, synthetic: numpy.ndarray, time: numpy.ndarray, station_names: List[str], misfit: float | None = None, show: bool = True, save_path: str | Path | None = None, dpi: int = 300, azimuths: numpy.ndarray | None = None, tp_s: numpy.ndarray | None = None, ts_s: numpy.ndarray | None = None, window_s: float | None = None, station_flags: numpy.ndarray | None = None, rotate: bool = False, mark_windows: bool = False, fig: Any | None = None) → Tuple[Any, Any][source]

Plot observed vs synthetic waveforms.

Parameters:
  • rotate (if True and azimuths (rad, one per station) is given, rotate the) – N/E components to Radial/Transverse and label columns R / T / Z, matching how the L2 misfit is computed (P -> R+Z, S -> T).

  • mark_windows (if True and tp_s, ts_s (s) and window_s (>0) are) – given, shade the misfit window per component: P window [tP, tP+window_s] on R and Z, S window [tS, tS+window_s] on T.

  • station_flags ((nsta, 3) booleans (use_n, use_e, use_z). Components that are) – NOT used in the misfit are drawn translucent and tagged “no usada”.

Inversion Base & Results Persistence

Base module for kinematic inversion: shared dataclasses, misfit logic and abstract model. (Módulo base para inversión cinemática: dataclasses compartidos, lógica de misfit y modelo base.)

Exports

  • NAModel : Single sampled model + misfit

  • MisfitCalculator : L2 waveform misfit with P/S time windows

  • NAResult : Container for all sampled models with export helpers

  • BaseInversionModel : Abstract base class for NA and MCMC inversion drivers

Dependencies (solo stdlib + numpy)

numpy, pathlib, csv, json, datetime, logging, os, time, copy

class kdellipspy.inversion.base.BaseInversionModel(input_ctl_path: str | Path | None = None, axitra_dir: str | Path | None = None, observed_waveforms: numpy.ndarray | None = None, time_array: numpy.ndarray | None = None, azi_times_array: numpy.ndarray | None = None, axitra_aw: float = 0.5, axitra_ikmax: int = 100000, config: ConfigParser | None = None)[source]

Bases: object

Abstract base class for kinematic inversion drivers (NA and MCMC). (Clase base abstracta para los motores de inversión cinemática NA y MCMC.)

Sub-classes must call super().__init__(...) and then implement either run_na_search or run_mcmc_search.

Parameters:
  • input_ctl_path (Path to input.ctl configuration file)

  • axitra_dir (Path to axitra binary directory (optional))

  • observed_waveforms (Observed 3-component seismograms, shape (nsta, 3, npts))

  • time_array (Time vector, shape (npts,))

  • azi_times_array (Pre-computed P/S arrival time table, shape (nsta, 3))

best_synthetics
Type:

np.ndarray | None — Updated whenever a new best misfit is found

azi_times_array: numpy.ndarray | None
best_synthetics: numpy.ndarray | None
checkpoint_path: str | Path | None
clear_green_cache() → None[source]

Clean up the cached Green’s-function axitra files (call at end of run).

classmethod from_config(config: ConfigParser, axitra_dir: str | Path | None = None, observed_waveforms: numpy.ndarray | None = None, time_array: numpy.ndarray | None = None, azi_times_array: numpy.ndarray | None = None, **kwargs) → BaseInversionModel[source]

Create an inversion model directly from a ConfigParser object.

classmethod from_params(params: Dict[str, Any], axitra_dir: str | Path | None = None, observed_waveforms: numpy.ndarray | None = None, time_array: numpy.ndarray | None = None, azi_times_array: numpy.ndarray | None = None, **kwargs) → BaseInversionModel[source]

Create an inversion model from a dictionary of parameters.

misfit_calc: MisfitCalculator | None
objective_function(model: numpy.ndarray) → float[source]

Evaluate the forward model and return L2 misfit for one parameter vector. (Evalúa el modelo forward y retorna el desajuste L2 para un vector de parámetros.)

Side-effects

  • Updates self._best_misfit_seen and self.best_synthetics when improved.

  • Cleans axitra temporary files unless the active config sets keep_axitra_files=True.

param_names: List[str]
plot_fit(show: bool = True, save_path: str | None = None) → Tuple[Any, Any][source]

Grafica el mejor ajuste de formas de onda encontrado hasta ahora.

use_full_signal: bool
use_green_cache: bool
class kdellipspy.inversion.base.MisfitCalculator(observed_waveforms: numpy.ndarray, time_array: numpy.ndarray, azi_times_path: Path | None = None, azi_times_array: numpy.ndarray | None = None, time_window_s: float = 20.0, station_flags: numpy.ndarray | None = None)[source]

Bases: object

L2 waveform misfit calculator with P/S arrival-time windows. (Calculador de desajuste L2 de formas de onda con ventanas de llegada P/S.)

Parameters:
  • observed_waveforms (np.ndarray, shape (nsta, 3, npts))

  • time_array (np.ndarray, shape (npts,))

  • azi_times_path (Path to ASCII azi_times.txt (3 columns: azi, tP, tS))

  • azi_times_array (Pre-loaded azi_times as np.ndarray (takes priority))

  • time_window_s (Duration (s) of the P and S analysis windows)

diagnostics_summary(synthetic: numpy.ndarray, max_stations: int = 3) → str[source]

Compact per-station diagnostics string for one model evaluation. (Resumen diagnóstico compacto por estación para una evaluación de modelo.)

l2_misfit(synthetic: numpy.ndarray, use_full_signal: bool = False) → float[source]

Compute normalised L2 misfit in P (radial+vertical) and S (transverse) windows. (Calcula el desajuste L2 normalizado en ventanas P (radial+vertical) y S (transversal).)

class kdellipspy.inversion.base.NAModel(model: numpy.ndarray, misfit: float, iteration: int)[source]

Bases: object

Single sampled model and its objective value. (Modelo muestreado individual y su valor objetivo.)

model
Type:

Parameter vector (np.ndarray)

misfit
Type:

Objective/misfit value (float)

iteration
Type:

Iteration index at which this model was sampled (int)

iteration: int
misfit: float
model: numpy.ndarray
class kdellipspy.inversion.base.NAResult(all_models: List[NAModel], param_names: Sequence[str] | None = None, extra_metadata: Dict[str, Any] | None = None, best_synthetics: numpy.ndarray | None = None, observed: numpy.ndarray | None = None, time: numpy.ndarray | None = None, config: ConfigParser | None = None, azi_times_array: numpy.ndarray | None = None)[source]

Bases: object

Container for all sampled models with export helpers. (Contenedor de todos los modelos muestreados con exportadores.)

Parameters:
  • all_models (List of NAModel instances)

  • param_names (Parameter name labels (optional, defaults to 7-param names))

  • extra_metadata (Algorithm-specific metadata written to JSON export)

  • best_synthetics (Synthetic waveforms of the best model (optional))

  • observed (Observed waveforms used for inversion (optional))

  • time (Time array used for inversion (optional))

  • config (ConfigParser instance (optional))

export_csv(filepath: Path) → None[source]

Export all models as CSV (iteration, misfit, param columns). (Exporta todos los modelos como CSV con columnas de iteración, misfit y parámetros.)

export_results(filepath: Path) → None[source]

Export all models + metadata as JSON. (Exporta todos los modelos y metadatos como JSON.)

classmethod load(filepath: str | Path) → NAResult[source]

Load a NAResult object from a saved file.

plot(show: bool = True, save_path: str | None = None) → Tuple[Any, Any][source]

Grafica el resumen de resultados de la búsqueda NA.

plot_appraisal(samples: np.ndarray | None = None, show: bool = True, save_path: str | None = None, bins: int = 40, title: str | None = None, **run_kwargs: Any) → Tuple[Any, Any][source]

Corner plot of the posterior uncertainty (1D marginals + 2D trade-offs). (Gráfico ‘corner’ de la incertidumbre: marginales 1D y trade-offs 2D.)

If samples is not given and the appraisal has not been run yet, it is executed via run_appraisal() (extra kwargs are forwarded there, e.g. n_resample, temperature, seed).

plot_azimuthal(show: bool = True, save_path: str | None = None, ax: Any | None = None) → Tuple[Any, Any][source]

Diagrama polar (radar) de la cobertura azimutal de las estaciones respecto al epicentro, con el mayor hueco azimutal sombreado. ax (eje polar) permite componerlo dentro de plot_yolo.

plot_convergence(show: bool = True, save_path: str | None = None, fig: Any | None = None) → Tuple[Any, Any][source]

Grafica la convergencia detallada por cada parámetro.

plot_elipse(show: bool = True, save_path: str | None = None, title: str | None = None) → Tuple[Any, Any][source]

Alias por compatibilidad con el nombre en español.

plot_ellipse(show: bool = True, save_path: str | None = None, title: str | None = None, fig: Any | None = None) → Tuple[Any, Any][source]

Grafica la distribución de slip de la elipse para el mejor misfit. Requiere que config y best_model estén presentes en el objeto.

plot_ellipse_depth(show: bool = True, save_path: str | None = None) → Tuple[Any, Any][source]

Secciones transversales de la elipse de slip en profundidad (estilo legacy plot_geometry): corte N–S y corte E–O, coloreados por slip, con hipocentro.

plot_ellipse_map(show: bool = True, save_path: str | None = None, pad: float = 0.25, fig: Any | None = None) → Tuple[Any, Any][source]

Mapa cartopy de la elipse de slip proyectada a la superficie (footprint coloreado por slip + borde + hipocentro + estaciones).

plot_fit(show: bool = True, save_path: str | None = None, rotate: bool = True, mark_windows: bool = True, fig: Any | None = None) → Tuple[Any, Any][source]

Grafica el mejor ajuste de formas de onda encontrado. Requiere que best_synthetics, observed y time estén presentes en el objeto.

Por defecto rota a R/T/Z y sombrea la ventana del misfit (P→R,Z ; S→T), consistente con cómo se calcula el misfit. Si no hay datos de azimut/ tiempos disponibles, cae a las componentes N/E/Z sin ventana.

plot_misfit_breakdown(show: bool = True, save_path: str | None = None, window_s='auto', fig: Any | None = None) → Tuple[Any, Any][source]

Visualiza la contribución al misfit por estación/componente (heatmaps).

plot_yolo(save_path: str | Path = 'dashboard.pdf', show: bool = False, dpi: int = 200) → Path | None[source]

🎲 YOLO: arma un DASHBOARD de una sola página con todos los paneles encajados (GridSpec):

┌───────────────── título (evento · misfit · Mw) ─────────────────┐ │ mapa elipse (cartopy) │ ajuste R/T/Z (7 est, ventanas)│ │ azimutal (radar) │ heatmap mf │ (panel alto) │ │ convergencia de parámetros │ appraisal (corner) │ └─────────────────────────────────────────────────────────────────┘

Cada panel se renderiza con su método y se compone como imagen, así se reaprovecha todo el código existente (cartopy, corner, etc.). El appraisal se corre automáticamente si no estaba.

run_appraisal(n_resample: int = 20000, n_walkers: int = 1, temperature: float | None = None, bounds: numpy.ndarray | None = None, seed: int | None = None, verbose: bool = True, save: bool = True) → numpy.ndarray | None[source]

Run the NA appraisal stage to approximate the posterior. (Ejecuta la etapa de ‘appraisal’ del NA para aproximar la posterior.)

Reuses the models already evaluated during the NA search (all_models) and resamples their Voronoi cells with neighpy.NAAppraiser — it does NOT evaluate the forward model again. Populates appraisal_samples, appraisal_mean and appraisal_covariance.

Parameters:
  • n_resample (length of the resampling random walk (more = smoother PDFs).)

  • n_walkers (parallel walkers (>1 uses neighpy's multiprocessing).)

  • temperature (misfit-to-log-posterior scale, log_ppd = -misfit / T.) – If None, T = 2 * best_misfit (auto-scale). SMALLER T → narrower posterior (more trust in the data). NOTE: the misfit here is the normalized L2 misfit, not a noise-calibrated chi-square, so temperature implicitly sets the assumed noise level. Tune it (or pass an explicit value) if you need calibrated uncertainties.

  • bounds ((n_params, 2) optional; derived from the config if None.)

  • seed (forwarded to NAAppraiser / NAAppraiser.run.)

  • verbose (forwarded to NAAppraiser / NAAppraiser.run.)

  • save (forwarded to NAAppraiser / NAAppraiser.run.)

Returns:

Posterior ensemble, shape (n_samples, n_params), or None if save=False.

Return type:

np.ndarray | None

save(filepath: str | Path) → None[source]

Save the entire NAResult object to a file for later reloading. Uses joblib if available (efficient for numpy arrays), otherwise uses pickle.

Kinematic Inversion (NA & MCMC)

Neighbourhood Algorithm (NA) inversion driver. (Motor de inversión con el Algoritmo Neighbourhood — NA.)

This module contains ONLY the NA search implementation. For MCMC inversion, see inversion_mcmc.py.

Exports

  • NAConfig : NA search hyperparameters

  • NAInversionModel : Full inversion driver; call run_na_search()

Dependencies

inversion_base (NAModel, NAResult, BaseInversionModel) neighpy (pip install neighpy) config_parser, forward_model (from this package)

class kdellipspy.inversion.kinematic.model_na.NAConfig(n_samples_initial: int = 30, n_samples_iteration: int = 30, n_iterations: int = 10, n_cells_resample: int = 7, n_jobs: int = 1, random_seed: int | None = None, keep_axitra_files: bool = False)[source]

Bases: object

Hyperparameters for the Neighbourhood Algorithm search. (Hiperparámetros para la búsqueda con el Algoritmo Neighbourhood.)

n_samples_initial
Type:

Number of random samples drawn in the first iteration (ni)

n_samples_iteration
Type:

Samples drawn around the best Voronoi cells per iteration (ns)

n_iterations
Type:

Number of NA iterations after the initial random stage (n)

n_cells_resample
Type:

Number of best Voronoi cells to resample from (nr)

n_jobs
Type:

Number of parallel workers for objective function evaluation (-1 for all)

random_seed
Type:

Optional seed for reproducibility

keep_axitra_files
Type:

Keep temporary axitra files (useful for debugging)

keep_axitra_files: bool = False
n_cells_resample: int = 7
n_iterations: int = 10
n_jobs: int = 1
n_samples_initial: int = 30
n_samples_iteration: int = 30
random_seed: int | None = None
class kdellipspy.inversion.kinematic.model_na.NAInversionModel(input_ctl_path: str | Path | None = None, axitra_dir: str | Path | None = None, observed_waveforms: numpy.ndarray | None = None, time_array: numpy.ndarray | None = None, azi_times_array: numpy.ndarray | None = None, axitra_aw: float = 0.5, axitra_ikmax: int = 100000, config: ConfigParser | None = None)[source]

Bases: BaseInversionModel

Kinematic Inversion Model using the Neighbourhood Algorithm (NA). (Modelo de Inversión Cinemática utilizando el Algoritmo Neighbourhood — NA.)

Integrates observed data, arrival times, and event configuration to evaluate kinematic rupture models. Communicates with axitra to simulate synthetic seismograms and compute misfit against real data. (Integra los datos observados, tiempos de llegada y configuración del evento para evaluar distintos modelos de ruptura cinemática. Se comunica con axitra para simular sismogramas sintéticos y calcular el desajuste con los datos reales.)

Parameters:
  • input_ctl_path (Path to input.ctl configuration file (optional if config is provided))

  • axitra_dir (Path to axitra binary directory (optional))

  • observed_waveforms (3-component seismograms, shape (nsta, 3, npts))

  • time_array (Time vector, shape (npts,))

  • azi_times_array (P/S arrival time table, shape (nsta, 3))

  • config (ConfigParser object (optional if input_ctl_path is provided))

best_synthetics

Synthetic seismograms of the best model found so far (nsta, 3, npts). Set to None until the first objective function evaluation.

Type:

np.ndarray | None

param_names
Type:

List[str] — Human-readable parameter labels

param_ranges
Type:

np.ndarray, shape (n_params, 2) — [min, max] per parameter

Example

>>> # Using a config file path:
>>> model = NAInversionModel(
...     "path/to/run/input.ctl",
...     axitra_dir="path/to/axitra",
...     observed_waveforms=obs,
...     time_array=t,
...     azi_times_array=azi,
... )
>>> # Or using a ConfigParser object:
>>> cfg = ConfigParser("path/to/input.ctl")
>>> model = NAInversionModel(
...     config=cfg,
...     observed_waveforms=obs,
...     time_array=t,
...     azi_times_array=azi,
... )
>>> result = model.run_na_search()
>>> print(result.best_model.misfit)

Run the Neighbourhood Algorithm search and return all sampled models. (Ejecuta la búsqueda con el Algoritmo Neighbourhood y retorna todos los modelos.)

Parameters:

na_config (NAConfig, optional) – Search hyperparameters. If None, values are read from self.cfg.inversion_process (input.ctl).

Returns:

Container with every evaluated model, the best model, and JSON/CSV export helpers.

Return type:

NAResult

Raises:

ImportError – If neighpy is not installed.

class kdellipspy.inversion.kinematic.model_na.NAModel(model: numpy.ndarray, misfit: float, iteration: int)[source]

Bases: object

Single sampled model and its objective value. (Modelo muestreado individual y su valor objetivo.)

model
Type:

Parameter vector (np.ndarray)

misfit
Type:

Objective/misfit value (float)

iteration
Type:

Iteration index at which this model was sampled (int)

iteration: int
misfit: float
model: numpy.ndarray
class kdellipspy.inversion.kinematic.model_na.NAResult(all_models: List[NAModel], param_names: Sequence[str] | None = None, extra_metadata: Dict[str, Any] | None = None, best_synthetics: numpy.ndarray | None = None, observed: numpy.ndarray | None = None, time: numpy.ndarray | None = None, config: ConfigParser | None = None, azi_times_array: numpy.ndarray | None = None)[source]

Bases: object

Container for all sampled models with export helpers. (Contenedor de todos los modelos muestreados con exportadores.)

Parameters:
  • all_models (List of NAModel instances)

  • param_names (Parameter name labels (optional, defaults to 7-param names))

  • extra_metadata (Algorithm-specific metadata written to JSON export)

  • best_synthetics (Synthetic waveforms of the best model (optional))

  • observed (Observed waveforms used for inversion (optional))

  • time (Time array used for inversion (optional))

  • config (ConfigParser instance (optional))

export_csv(filepath: Path) → None[source]

Export all models as CSV (iteration, misfit, param columns). (Exporta todos los modelos como CSV con columnas de iteración, misfit y parámetros.)

export_results(filepath: Path) → None[source]

Export all models + metadata as JSON. (Exporta todos los modelos y metadatos como JSON.)

classmethod load(filepath: str | Path) → NAResult[source]

Load a NAResult object from a saved file.

plot(show: bool = True, save_path: str | None = None) → Tuple[Any, Any][source]

Grafica el resumen de resultados de la búsqueda NA.

plot_appraisal(samples: np.ndarray | None = None, show: bool = True, save_path: str | None = None, bins: int = 40, title: str | None = None, **run_kwargs: Any) → Tuple[Any, Any][source]

Corner plot of the posterior uncertainty (1D marginals + 2D trade-offs). (Gráfico ‘corner’ de la incertidumbre: marginales 1D y trade-offs 2D.)

If samples is not given and the appraisal has not been run yet, it is executed via run_appraisal() (extra kwargs are forwarded there, e.g. n_resample, temperature, seed).

plot_azimuthal(show: bool = True, save_path: str | None = None, ax: Any | None = None) → Tuple[Any, Any][source]

Diagrama polar (radar) de la cobertura azimutal de las estaciones respecto al epicentro, con el mayor hueco azimutal sombreado. ax (eje polar) permite componerlo dentro de plot_yolo.

plot_convergence(show: bool = True, save_path: str | None = None, fig: Any | None = None) → Tuple[Any, Any][source]

Grafica la convergencia detallada por cada parámetro.

plot_elipse(show: bool = True, save_path: str | None = None, title: str | None = None) → Tuple[Any, Any][source]

Alias por compatibilidad con el nombre en español.

plot_ellipse(show: bool = True, save_path: str | None = None, title: str | None = None, fig: Any | None = None) → Tuple[Any, Any][source]

Grafica la distribución de slip de la elipse para el mejor misfit. Requiere que config y best_model estén presentes en el objeto.

plot_ellipse_depth(show: bool = True, save_path: str | None = None) → Tuple[Any, Any][source]

Secciones transversales de la elipse de slip en profundidad (estilo legacy plot_geometry): corte N–S y corte E–O, coloreados por slip, con hipocentro.

plot_ellipse_map(show: bool = True, save_path: str | None = None, pad: float = 0.25, fig: Any | None = None) → Tuple[Any, Any][source]

Mapa cartopy de la elipse de slip proyectada a la superficie (footprint coloreado por slip + borde + hipocentro + estaciones).

plot_fit(show: bool = True, save_path: str | None = None, rotate: bool = True, mark_windows: bool = True, fig: Any | None = None) → Tuple[Any, Any][source]

Grafica el mejor ajuste de formas de onda encontrado. Requiere que best_synthetics, observed y time estén presentes en el objeto.

Por defecto rota a R/T/Z y sombrea la ventana del misfit (P→R,Z ; S→T), consistente con cómo se calcula el misfit. Si no hay datos de azimut/ tiempos disponibles, cae a las componentes N/E/Z sin ventana.

plot_misfit_breakdown(show: bool = True, save_path: str | None = None, window_s='auto', fig: Any | None = None) → Tuple[Any, Any][source]

Visualiza la contribución al misfit por estación/componente (heatmaps).

plot_yolo(save_path: str | Path = 'dashboard.pdf', show: bool = False, dpi: int = 200) → Path | None[source]

🎲 YOLO: arma un DASHBOARD de una sola página con todos los paneles encajados (GridSpec):

┌───────────────── título (evento · misfit · Mw) ─────────────────┐ │ mapa elipse (cartopy) │ ajuste R/T/Z (7 est, ventanas)│ │ azimutal (radar) │ heatmap mf │ (panel alto) │ │ convergencia de parámetros │ appraisal (corner) │ └─────────────────────────────────────────────────────────────────┘

Cada panel se renderiza con su método y se compone como imagen, así se reaprovecha todo el código existente (cartopy, corner, etc.). El appraisal se corre automáticamente si no estaba.

run_appraisal(n_resample: int = 20000, n_walkers: int = 1, temperature: float | None = None, bounds: numpy.ndarray | None = None, seed: int | None = None, verbose: bool = True, save: bool = True) → numpy.ndarray | None[source]

Run the NA appraisal stage to approximate the posterior. (Ejecuta la etapa de ‘appraisal’ del NA para aproximar la posterior.)

Reuses the models already evaluated during the NA search (all_models) and resamples their Voronoi cells with neighpy.NAAppraiser — it does NOT evaluate the forward model again. Populates appraisal_samples, appraisal_mean and appraisal_covariance.

Parameters:
  • n_resample (length of the resampling random walk (more = smoother PDFs).)

  • n_walkers (parallel walkers (>1 uses neighpy's multiprocessing).)

  • temperature (misfit-to-log-posterior scale, log_ppd = -misfit / T.) – If None, T = 2 * best_misfit (auto-scale). SMALLER T → narrower posterior (more trust in the data). NOTE: the misfit here is the normalized L2 misfit, not a noise-calibrated chi-square, so temperature implicitly sets the assumed noise level. Tune it (or pass an explicit value) if you need calibrated uncertainties.

  • bounds ((n_params, 2) optional; derived from the config if None.)

  • seed (forwarded to NAAppraiser / NAAppraiser.run.)

  • verbose (forwarded to NAAppraiser / NAAppraiser.run.)

  • save (forwarded to NAAppraiser / NAAppraiser.run.)

Returns:

Posterior ensemble, shape (n_samples, n_params), or None if save=False.

Return type:

np.ndarray | None

save(filepath: str | Path) → None[source]

Save the entire NAResult object to a file for later reloading. Uses joblib if available (efficient for numpy arrays), otherwise uses pickle.

MCMC inversion driver using PyMC + ArviZ. (Motor de inversión MCMC usando PyMC + ArviZ.)

This module contains ONLY the MCMC search implementation. For Neighbourhood Algorithm inversion, see inversion_na.py.

Exports

  • MCMCConfig : MCMC sampling hyperparameters

  • MCMCInversionModel : Full inversion driver; call run_mcmc_search()

Dependencies

inversion_base (NAModel, NAResult, BaseInversionModel) pymc (pip install pymc) — includes arviz and pytensor config_parser, forward_model (from this package)

class kdellipspy.inversion.kinematic.model_mcmc.MCMCConfig(total_steps: int = 500, burn_in: int = 0, proposal_scale: float = 0.08, thin: int = 1, random_seed: int | None = None, keep_axitra_files: bool = False, chains: int = 1)[source]

Bases: object

Hyperparameters for the MCMC search with PyMC + ArviZ. (Hiperparámetros para la búsqueda MCMC con PyMC + ArviZ.)

PyMC runs a Metropolis sampler with a uniform prior on the parameter box. ArviZ is used for posterior summary statistics.

total_steps
Type:

Total number of MCMC steps per chain (tune + draws)

burn_in
Type:

Number of tuning steps (excluded from posterior)

proposal_scale

each parameter’s range, e.g. 0.08 → 8 % of width per param

Type:

Metropolis proposal standard deviation as a fraction of

thin
Type:

Thinning factor applied to the posterior draws

random_seed
Type:

Optional seed for reproducibility

keep_axitra_files
Type:

Keep temporary axitra files (useful for debugging)

chains

Warning: chains > 1 may fail when axitra uses shared temp files.

Type:

Number of parallel MCMC chains.

burn_in: int = 0
chains: int = 1
keep_axitra_files: bool = False
proposal_scale: float = 0.08
random_seed: int | None = None
thin: int = 1
total_steps: int = 500
class kdellipspy.inversion.kinematic.model_mcmc.MCMCInversionModel(input_ctl_path: str | Path | None = None, axitra_dir: str | Path | None = None, observed_waveforms: numpy.ndarray | None = None, time_array: numpy.ndarray | None = None, azi_times_array: numpy.ndarray | None = None, axitra_aw: float = 0.5, axitra_ikmax: int = 100000, config: ConfigParser | None = None)[source]

Bases: BaseInversionModel

Kinematic Inversion Model using MCMC (Metropolis, PyMC + ArviZ). (Modelo de Inversión Cinemática usando MCMC — PyMC + ArviZ.)

Integrates observed data, arrival times, and event configuration to sample the posterior distribution of kinematic rupture model parameters. (Integra los datos observados, tiempos de llegada y configuración del evento para muestrear la distribución posterior de los parámetros del modelo cinemático.)

Parameters:
  • input_ctl_path (Path to input.ctl configuration file (optional if config is provided))

  • axitra_dir (Path to axitra binary directory (optional))

  • observed_waveforms (3-component seismograms, shape (nsta, 3, npts))

  • time_array (Time vector, shape (npts,))

  • azi_times_array (P/S arrival time table, shape (nsta, 3))

  • config (ConfigParser object (optional if input_ctl_path is provided))

best_synthetics

Synthetic seismograms of the best model found so far (nsta, 3, npts).

Type:

np.ndarray | None

Example

>>> # Using a config file path:
>>> model = MCMCInversionModel(
...     "path/to/run/input.ctl",
...     axitra_dir="path/to/axitra",
...     observed_waveforms=obs,
...     time_array=t,
...     azi_times_array=azi,
... )
>>> # Or using a ConfigParser object:
>>> cfg = ConfigParser("path/to/input.ctl")
>>> model = MCMCInversionModel(
...     config=cfg,
...     observed_waveforms=obs,
...     time_array=t,
...     azi_times_array=azi,
... )
>>> result = model.run_mcmc_search()
>>> print(result.best_model.misfit)

Run the PyMC Metropolis sampler and return posterior models. (Ejecuta el muestreador Metropolis de PyMC y retorna los modelos posteriores.)

Parameters:

mc_config (MCMCConfig, optional) – MCMC hyperparameters. If None, values are read from self.cfg.inversion_process (input.ctl).

Returns:

Container with posterior samples, the best model (minimum misfit), ArviZ summary in extra_metadata, and JSON/CSV export helpers.

Return type:

NAResult

Raises:
  • ImportError – If pymc (or arviz / pytensor) is not installed.

  • ValueError – If burn_in >= total_steps.

class kdellipspy.inversion.kinematic.model_mcmc.NAModel(model: numpy.ndarray, misfit: float, iteration: int)[source]

Bases: object

Single sampled model and its objective value. (Modelo muestreado individual y su valor objetivo.)

model
Type:

Parameter vector (np.ndarray)

misfit
Type:

Objective/misfit value (float)

iteration
Type:

Iteration index at which this model was sampled (int)

iteration: int
misfit: float
model: numpy.ndarray
class kdellipspy.inversion.kinematic.model_mcmc.NAResult(all_models: List[NAModel], param_names: Sequence[str] | None = None, extra_metadata: Dict[str, Any] | None = None, best_synthetics: numpy.ndarray | None = None, observed: numpy.ndarray | None = None, time: numpy.ndarray | None = None, config: ConfigParser | None = None, azi_times_array: numpy.ndarray | None = None)[source]

Bases: object

Container for all sampled models with export helpers. (Contenedor de todos los modelos muestreados con exportadores.)

Parameters:
  • all_models (List of NAModel instances)

  • param_names (Parameter name labels (optional, defaults to 7-param names))

  • extra_metadata (Algorithm-specific metadata written to JSON export)

  • best_synthetics (Synthetic waveforms of the best model (optional))

  • observed (Observed waveforms used for inversion (optional))

  • time (Time array used for inversion (optional))

  • config (ConfigParser instance (optional))

export_csv(filepath: Path) → None[source]

Export all models as CSV (iteration, misfit, param columns). (Exporta todos los modelos como CSV con columnas de iteración, misfit y parámetros.)

export_results(filepath: Path) → None[source]

Export all models + metadata as JSON. (Exporta todos los modelos y metadatos como JSON.)

classmethod load(filepath: str | Path) → NAResult[source]

Load a NAResult object from a saved file.

plot(show: bool = True, save_path: str | None = None) → Tuple[Any, Any][source]

Grafica el resumen de resultados de la búsqueda NA.

plot_appraisal(samples: np.ndarray | None = None, show: bool = True, save_path: str | None = None, bins: int = 40, title: str | None = None, **run_kwargs: Any) → Tuple[Any, Any][source]

Corner plot of the posterior uncertainty (1D marginals + 2D trade-offs). (Gráfico ‘corner’ de la incertidumbre: marginales 1D y trade-offs 2D.)

If samples is not given and the appraisal has not been run yet, it is executed via run_appraisal() (extra kwargs are forwarded there, e.g. n_resample, temperature, seed).

plot_azimuthal(show: bool = True, save_path: str | None = None, ax: Any | None = None) → Tuple[Any, Any][source]

Diagrama polar (radar) de la cobertura azimutal de las estaciones respecto al epicentro, con el mayor hueco azimutal sombreado. ax (eje polar) permite componerlo dentro de plot_yolo.

plot_convergence(show: bool = True, save_path: str | None = None, fig: Any | None = None) → Tuple[Any, Any][source]

Grafica la convergencia detallada por cada parámetro.

plot_elipse(show: bool = True, save_path: str | None = None, title: str | None = None) → Tuple[Any, Any][source]

Alias por compatibilidad con el nombre en español.

plot_ellipse(show: bool = True, save_path: str | None = None, title: str | None = None, fig: Any | None = None) → Tuple[Any, Any][source]

Grafica la distribución de slip de la elipse para el mejor misfit. Requiere que config y best_model estén presentes en el objeto.

plot_ellipse_depth(show: bool = True, save_path: str | None = None) → Tuple[Any, Any][source]

Secciones transversales de la elipse de slip en profundidad (estilo legacy plot_geometry): corte N–S y corte E–O, coloreados por slip, con hipocentro.

plot_ellipse_map(show: bool = True, save_path: str | None = None, pad: float = 0.25, fig: Any | None = None) → Tuple[Any, Any][source]

Mapa cartopy de la elipse de slip proyectada a la superficie (footprint coloreado por slip + borde + hipocentro + estaciones).

plot_fit(show: bool = True, save_path: str | None = None, rotate: bool = True, mark_windows: bool = True, fig: Any | None = None) → Tuple[Any, Any][source]

Grafica el mejor ajuste de formas de onda encontrado. Requiere que best_synthetics, observed y time estén presentes en el objeto.

Por defecto rota a R/T/Z y sombrea la ventana del misfit (P→R,Z ; S→T), consistente con cómo se calcula el misfit. Si no hay datos de azimut/ tiempos disponibles, cae a las componentes N/E/Z sin ventana.

plot_misfit_breakdown(show: bool = True, save_path: str | None = None, window_s='auto', fig: Any | None = None) → Tuple[Any, Any][source]

Visualiza la contribución al misfit por estación/componente (heatmaps).

plot_yolo(save_path: str | Path = 'dashboard.pdf', show: bool = False, dpi: int = 200) → Path | None[source]

🎲 YOLO: arma un DASHBOARD de una sola página con todos los paneles encajados (GridSpec):

┌───────────────── título (evento · misfit · Mw) ─────────────────┐ │ mapa elipse (cartopy) │ ajuste R/T/Z (7 est, ventanas)│ │ azimutal (radar) │ heatmap mf │ (panel alto) │ │ convergencia de parámetros │ appraisal (corner) │ └─────────────────────────────────────────────────────────────────┘

Cada panel se renderiza con su método y se compone como imagen, así se reaprovecha todo el código existente (cartopy, corner, etc.). El appraisal se corre automáticamente si no estaba.

run_appraisal(n_resample: int = 20000, n_walkers: int = 1, temperature: float | None = None, bounds: numpy.ndarray | None = None, seed: int | None = None, verbose: bool = True, save: bool = True) → numpy.ndarray | None[source]

Run the NA appraisal stage to approximate the posterior. (Ejecuta la etapa de ‘appraisal’ del NA para aproximar la posterior.)

Reuses the models already evaluated during the NA search (all_models) and resamples their Voronoi cells with neighpy.NAAppraiser — it does NOT evaluate the forward model again. Populates appraisal_samples, appraisal_mean and appraisal_covariance.

Parameters:
  • n_resample (length of the resampling random walk (more = smoother PDFs).)

  • n_walkers (parallel walkers (>1 uses neighpy's multiprocessing).)

  • temperature (misfit-to-log-posterior scale, log_ppd = -misfit / T.) – If None, T = 2 * best_misfit (auto-scale). SMALLER T → narrower posterior (more trust in the data). NOTE: the misfit here is the normalized L2 misfit, not a noise-calibrated chi-square, so temperature implicitly sets the assumed noise level. Tune it (or pass an explicit value) if you need calibrated uncertainties.

  • bounds ((n_params, 2) optional; derived from the config if None.)

  • seed (forwarded to NAAppraiser / NAAppraiser.run.)

  • verbose (forwarded to NAAppraiser / NAAppraiser.run.)

  • save (forwarded to NAAppraiser / NAAppraiser.run.)

Returns:

Posterior ensemble, shape (n_samples, n_params), or None if save=False.

Return type:

np.ndarray | None

save(filepath: str | Path) → None[source]

Save the entire NAResult object to a file for later reloading. Uses joblib if available (efficient for numpy arrays), otherwise uses pickle.

Signal Processing & SAC

kdellipspy.core.signal_utils.bandpass_filter_waveforms(synthetic: numpy.ndarray, time: numpy.ndarray, freq1: float, freq2: float, corners: int = 4, zerophase: bool = True) → numpy.ndarray[source]

Apply Butterworth bandpass filter to synthetic waveforms.

This ensures synthetic waveforms match the frequency band of observed data, maintaining consistency in the misfit calculation.

Parameters:
  • synthetic – waveform array with shape (n_stations, 3, npts)

  • time – time array with shape (npts,)

  • freq1 – low frequency cutoff (Hz)

  • freq2 – high frequency cutoff (Hz)

  • corners – filter order (default 4 = 4th order)

  • zerophase – if True, apply filter forward-backward for zero phase distortion

Returns:

filtered waveforms with same shape as input

Return type:

filtered_synthetic

Raises:

ValueError – if time array has < 2 points or freq2 >= Nyquist frequency

kdellipspy.core.signal_utils.build_azi_times_array(input_ctl_path: str | Path | None = None, model_name: str = 'iasp91', p_shift_s: float = -1.0, s_shift_s: float = -1.0, use_iris: bool = False, config: ConfigParser | None = None) → numpy.ndarray[source]

Build legacy-compatible azi_times values as an array with columns: [azimuth_rad, tP, tS].

This reproduces the old script behavior while avoiding IRIS network requirements by default (use_iris=False).

kdellipspy.core.signal_utils.integrate_waveforms(waveforms: numpy.ndarray, delta: float, baseline_samples: int | None = None, steps: int = 1) → numpy.ndarray[source]

Integrate waveforms along the last axis using a digital filter (Matlab integraf.m logic). Cleanest results for seismic signals.

Parameters:
  • waveforms – array with shape (…, npts)

  • delta – sampling interval in seconds

  • baseline_samples – Number of samples at the start for pre-event baseline correction.

  • steps – Number of integration steps (1 for velocity, 2 for displacement).

Returns:

Integrated waveforms with the same shape as the input.

kdellipspy.core.signal_utils.load_and_filter_observed_data(freq1: float | None = None, freq2: float | None = None, input_ctl_path: str | Path | None = None, data_dir: str | Path = 'DATA', prefer_raw: bool = False, config: ConfigParser | None = None) → Tuple[numpy.ndarray, numpy.ndarray][source]

Load observed data with two supported user workflows:

  1. Flat files in DATA: - real_disp_x/y/z (if input.ctl units=1) - real_vel_x/y/z (if input.ctl units=2) Each file must contain 2 columns: time and data, and total rows must be npts * nstations.

  2. RAW files in DATA/RAW: - .sac or .mseed files - station name extracted from waveform metadata - bandpass filtering using input.ctl frequency band - response removal attempted when inventory files are present in RAW

Returns:

ndarray with shape (n_stations, 3, npts) time_array: ndarray with shape (npts,)

Return type:

observed_waveforms

kdellipspy.core.signal_utils.write_azi_times_file(input_ctl_path: str | Path | None = None, output_path: str | Path | None = None, model_name: str = 'iasp91', p_shift_s: float = -1.0, s_shift_s: float = -1.0, use_iris: bool = False, config: ConfigParser | None = None) → Path[source]

Write Event/azi_times.txt with legacy-compatible 3-column format.

SAC/MiniSEED waveform processor for seismic inversion.

Implements robust signal processing following Boore (2001) and Boore & Bommer (2005) standards for seismic data preprocessing: - Zero-phase bandpass filtering with padding - Polynomial baseline correction - Multi-step integration with proper detrending - Station and component extraction from metadata

This module bridges ObsPy with inversion workflows in kdellipspy.

class kdellipspy.core.sac_processor.ProcessingConfig(freq_min: float = 0.04, freq_max: float = 0.2, filter_corners: int = 4, zerophase: bool = True, zeropad_duration: float = 100.0, taper_percentage: float = 0.05, taper_type: str = 'cosine', polynomial_degree_vel: int = 1, polynomial_degree_disp: int = 2)[source]

Bases: object

Configuration for waveform processing pipeline.

class kdellipspy.core.sac_processor.WaveformExtractor(raw_dir: Path, config: ProcessingConfig | None = None)[source]

Bases: object

Extract and process waveforms from SAC/MiniSEED files in DATA/RAW/.

Handles: - Reading multiple files (*.sac, *.mseed) - Grouping by station and component - Preprocessing with full pipeline - Validation and error handling

extract_stations() → List[str][source]

Get sorted list of unique station names.

get_station_waveforms(station: str) → Dict[str, obspy.Trace][source]

Get preprocessed waveforms for a station, keyed by component.

Parameters:

station – Station name (e.g., ‘AC01’).

Returns:

Dict mapping component (‘E’, ‘N’, ‘Z’) to preprocessed Trace.

Raises:

ValueError – If station not found or components incomplete.

preprocess_station(station: str, event_time: obspy.UTCDateTime, target_mode: str = 'VEL', time_window: Tuple[float, float] | None = None) → Dict[str, numpy.ndarray][source]

Preprocess all components for a station.

Parameters:
  • station – Station name.

  • event_time – Event origin time.

  • target_mode – ‘VEL’ or ‘DISP’.

  • time_window – (t_start, t_end) relative to event_time. If None, uses full trace.

Returns:

Dict mapping component to data array.

kdellipspy.core.sac_processor.acausal_bandpass(tr: obspy.Trace, freq_min: float, freq_max: float, corners: int = 4, zerophase: bool = True, zeropad_duration: float = 100.0) → obspy.Trace[source]

Apply zero-phase bandpass filter with manual padding (Boore 2001, §3.2).

Manual padding avoids ObsPy’s automatic tapering, which can degrade low-frequency content. Critical for periods > 5 s.

Parameters:
  • tr – ObsPy Trace.

  • freq_min – Low-frequency cutoff (Hz).

  • freq_max – High-frequency cutoff (Hz).

  • corners – Filter order.

  • zerophase – Apply forward-backward filtering.

  • zeropad_duration – Padding duration (seconds).

Returns:

Filtered Trace (modifies in-place).

kdellipspy.core.sac_processor.polynomial_baseline_correction(tr: obspy.Trace, degree: int = 1) → obspy.Trace[source]

Apply polynomial baseline correction to waveform.

References: - Boore (2001): Effect of baseline corrections on displacements and response spectra. - Graizer (2010): Baseline correction of strong-motion records.

Typical usage: - degree=1 (linear): After ACC→VEL integration. - degree=2 (quadratic): After VEL→DISP integration (preserves static offset).

Parameters:
  • tr – ObsPy Trace.

  • degree – Polynomial degree for fit.

Returns:

Modified Trace (operates in-place, but returns for chaining).

kdellipspy.core.sac_processor.process_trace(tr: obspy.Trace, event_time: obspy.UTCDateTime, target_mode: str = 'VEL', config: ProcessingConfig | None = None) → obspy.Trace[source]

Complete preprocessing pipeline for seismic waveforms.

Follows Boore (2001) and Boore & Bommer (2005) standards:

  1. Pre-event baseline correction (mean of window before earthquake).

  2. Linear detrending.

  3. Cosine taper BEFORE integration (critical for long-period signals).

  4. Multi-step numerical integration (cumtrapz) with polynomial baseline correction: - ACC→VEL: Linear polynomial correction (degree=1). - VEL→DISP: Quadratic polynomial correction (degree=2) to preserve static offset.

  5. Bandpass filtering with zero-phase and padding.

  6. Trim to desired time window.

Parameters:
  • tr – ObsPy Trace.

  • event_time – Event origin time (UTCDateTime).

  • target_mode – Target output (‘VEL’ or ‘DISP’).

  • config – ProcessingConfig instance. If None, uses defaults.

Returns:

Processed Trace (modifies in-place).

Raises:

ValueError – If target_mode not in (‘VEL’, ‘DISP’).

class kdellipspy.core.data_preprocessor.DataPreprocessor(cfg: ConfigParser)[source]

Bases: object

Utility to prepare raw seismic data for axitra inversion. (Utilidad para preparar datos sísmicos crudos para la inversión con axitra.)

plot_record_section(raw_dir: Path, t_start: Any | None = None, t_end: Any | None = None, freqmin: float = 0.05, freqmax: float = 0.5, scale: float = 2.0, components: List[str] = ['Z'], station_names: List[str] | None = None)[source]

Visualizes waveforms from SAC files in a record section, similar to the provided notebook. (Visualiza formas de onda de archivos SAC en una sección de registro, similar al notebook proporcionado.)

process_raw_files(raw_dir: Path, output_dir: Path, freq1: float, freq2: float, t_start: Any = 0.0, t_end: Any | None = None, data_start_time: Any | None = None, units: int = 2, station_indices: List[int] | None = None, station_names: List[str] | None = None) → Dict[str, numpy.ndarray][source]

Loads concatenated raw velocity files, filters, trims with zero-padding, and saves Axitra-ready files. Supports UTCDateTime or float for timing.

If station_names is provided, it filters the stations by name. (Si se proporciona station_names, filtra las estaciones por nombre.)

Command Line Interface

kdellipspy CLI

Corre la inversión cinemática (Neighbourhood Algorithm) de KDEllipsPy.

Lee input.ctl + DATA/ de la carpeta del proyecto (por defecto el directorio actual) y guarda la solución y las figuras en output/:

<proyecto>/ ├── input.ctl ├── DATA/ └── output/

├── inversion_result.joblib ← la solución ├── best_model_live.txt ├── misfit_breakdown.txt └── figures/ ← PNGs + all_plots.pdf

Ejemplos:

kdellipspy # usa ./input.ctl + ./DATA → ./output kdellipspy ruta/al/proyecto kdellipspy –freq1 0.06 –freq2 0.15 kdellipspy –no-plots python -m kdellipspy # equivalente sin instalar el comando

kdellipspy.cli.banner(title, subtitle='')[source]
kdellipspy.cli.err(msg)[source]
kdellipspy.cli.info(msg)[source]
kdellipspy.cli.main(argv=None)[source]
kdellipspy.cli.ok(msg)[source]
kdellipspy.cli.parse_args(argv=None)[source]
kdellipspy.cli.section(name)[source]
kdellipspy.cli.warn(msg)[source]