Skip to content

Commit 61f385f

Browse files
authored
Merge pull request #102 from MTgeophysics/ak_occam2d_fixes_June2026
Update data.py and use of IRLs
2 parents 1ed5000 + 77cca9a commit 61f385f

2 files changed

Lines changed: 78 additions & 33 deletions

File tree

mtpy/modeling/occam2d/data.py

Lines changed: 66 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,9 @@ def read_response_file(self, response_fn=None, data_fn=None):
504504

505505
entries = []
506506

507+
# initialise at zero
508+
self.dataframe["t_zy"] = 0.0 + 0 * 1j
509+
507510
for line in data_list:
508511
res_log = False
509512
# try:
@@ -556,35 +559,70 @@ def read_response_file(self, response_fn=None, data_fn=None):
556559
self.dataframe[key + "_model_error"] = 0.0
557560

558561
def _group_df(self):
562+
559563
df = self.dataframe
560-
for station in np.unique(df["station"]):
561-
for period in np.unique(df["period"]):
562-
filt = np.all(
563-
[
564-
df["station"] == station,
565-
df["period"] == period,
566-
],
567-
axis=0,
568-
)
569-
if np.any(filt):
570-
for key in ["res_xy", "res_yx", "phase_xy", "phase_yx"]:
571-
df.loc[filt, key] = np.unique(df.loc[filt, key])[0]
572-
df.loc[filt, key + "_model_error"] = np.unique(
573-
df.loc[filt, key + "_model_error"]
574-
)[0]
575-
tx_real_vals = np.unique(np.real(df.loc[filt, "t_zy"]))
576-
tx_imag_vals = np.unique(np.imag(df.loc[filt, "t_zy"]))
577-
578-
if np.any(tx_real_vals != 0) or np.any(tx_imag_vals != 0):
579-
tx_real_val, tx_imag_val = 0.0, 0.0
580-
if len(tx_real_vals[tx_real_vals != 0]) > 0:
581-
tx_real_val = tx_real_vals[tx_real_vals != 0][0]
582-
if len(tx_imag_vals[tx_imag_vals != 0]) > 0:
583-
tx_imag_val = tx_imag_vals[tx_imag_vals != 0][0]
584-
585-
df.loc[filt, "t_zy"] = tx_real_val + 1j * tx_imag_val
586-
587-
df.drop(df.index[filt][1:], inplace=True)
564+
565+
# 1. Helper function for the complex number logic
566+
def process_t_zy(series):
567+
# Drop NaNs and convert to a standard Python list
568+
vals = series.dropna().tolist()
569+
570+
reals = []
571+
imags = []
572+
573+
for v in vals:
574+
try:
575+
# Force the value into a standard complex type safely
576+
c = complex(v)
577+
if c.real != 0.0:
578+
reals.append(c.real)
579+
if c.imag != 0.0:
580+
imags.append(c.imag)
581+
except (TypeError, ValueError):
582+
# Safely ignore any weird strings or corrupted data
583+
continue
584+
585+
# sort the values, so [0] grabs the minimum.
586+
r = sorted(set(reals))[0] if reals else 0.0
587+
i = sorted(set(imags))[0] if imags else 0.0
588+
589+
return complex(r, i)
590+
591+
# 2. Identify the specific columns
592+
keys = ["res_xy", "res_yx", "phase_xy", "phase_yx"]
593+
error_keys = [f"{k}_model_error" for k in keys]
594+
special_cols = keys + error_keys
595+
596+
# 3. Build the aggregation dictionary with Python Lambdas
597+
agg_funcs = {}
598+
for col in df.columns:
599+
if col in ["station", "period"]:
600+
continue
601+
elif col == "t_zy":
602+
agg_funcs[col] = process_t_zy
603+
elif col in special_cols:
604+
# Bypass Cython by using a lambda for minimum
605+
agg_funcs[col] = lambda x: x.min()
606+
else:
607+
# Bypass Cython by explicitly asking for the first index
608+
agg_funcs[col] = lambda x: x.iloc[0]
609+
610+
# 4. Perform the GroupBy operation
611+
self.dataframe = df.groupby(["station", "period"], as_index=False).agg(
612+
agg_funcs
613+
)
614+
615+
# def _group_df(self):
616+
# print("grouping df")
617+
618+
# # A custom Python function that does exactly what .first() does:
619+
# # It drops NaNs, and grabs the first available value.
620+
# def get_first_valid(series):
621+
# valid_data = series.dropna()
622+
# return valid_data.iloc[0] if not valid_data.empty else np.nan
623+
624+
# # .agg() with a custom lambda forces pandas to stay in Python space
625+
# self.dataframe = self.dataframe.groupby(['station', 'period'], as_index=False).agg(get_first_valid)
588626

589627
def _get_model_mode_from_data(self, res_log):
590628
"""Get inversion mode from the data.

mtpy/modeling/simpeg/recipes/inversion_2d.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ def __init__(
120120
self.beta_cooling_rate = 1
121121

122122
self.target_misfit_chi_factor = 1
123+
self.irls_chifact_start = 2.0
124+
self.irls_chifact_target = 1.0
125+
self.irls_cooling_factor = 1.5
123126

124127
self.saved_model_outputs = directives.SaveOutputDictEveryIteration()
125128
self.saved_model_outputs.outDict = {}
@@ -314,7 +317,7 @@ def regularization(self):
314317
)
315318

316319
if self.use_irls:
317-
reg.norms = np.c_[self.p_s, self.p_y, self.p_z]
320+
reg.norms = [self.p_s, self.p_y, self.p_z]
318321

319322
return reg
320323

@@ -395,10 +398,14 @@ def directives(self):
395398
"""
396399

397400
if self.use_irls:
398-
IRLS = directives.Update_IRLS(
399-
max_irls_iterations=self.max_iteration_irls,
400-
minGNiter=self.minimum_gauss_newton_iterations,
401+
IRLS = directives.UpdateIRLS(
402+
max_irls_iterations=self.max_iterations_irls,
403+
irls_cooling_factor=self.irls_cooling_factor,
401404
f_min_change=self.f_min_change,
405+
cooling_rate=self.beta_cooling_rate,
406+
cooling_factor=self.beta_cooling_factor,
407+
chifact_start=self.irls_chifact_start,
408+
chifact_target=self.irls_chifact_target,
402409
)
403410
return [
404411
IRLS,
@@ -410,7 +417,7 @@ def directives(self):
410417
self.starting_beta,
411418
self.beta_schedule,
412419
self.saved_model_outputs,
413-
# self.target_misfit,
420+
self.target_misfit,
414421
]
415422

416423
def run_inversion(self):

0 commit comments

Comments
 (0)