pylot/pylot/tomography/fmtomo_tools/tradeoff_misfit_norm.py
2025-04-10 13:58:01 +02:00

267 lines
10 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import glob
import subprocess
import json
import numpy as np
import numpy.polynomial.polynomial as poly
from scipy.sparse import spdiags
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
from itertools import cycle
from pylot.tomography.fmtomo_tools.fmtomo_grid_utils import read_vgrid
# def calc_dampnorm(vgrid, vgrid_ref):
# # calculate m - m0
# m_m0 = np.array(vgrid['vps']) - np.array(vgrid_ref['vps'])
#
# # calculate inverse of diagonal elements of a priori model covariance matrix (which should be a diagonal matrix)
# # IMPORTANT: COVARIANCES ARE MOST LIKELY STANDARD DEVIATIONS -> square them
# covs = np.array(vgrid_ref['covs'])**2
#
# covs_inv = 1. / covs
#
# #cm_inv = spdiags(covs_inv, 0, covs_inv.size, covs_inv.size)#
#
# #norm_calc_old = np.dot(m_m0.transpose(), m_m0 * cm_inv)
#
# norm = np.dot(m_m0, m_m0 * covs_inv)
#
# return norm
#
#
# def calc_smoothnorm(vgrid, gridn, R=6371.):
# nR, nTheta, nPhi = gridn
#
# vps = np.array(vgrid['vps'])
# lats = np.array(vgrid['lats'])
# lons = np.array(vgrid['lons'])
# depths = np.array(vgrid['depths'])
# #vgridref = np.array(vgridref['vps'])
# #dvgrid = vgrid - vgridref
#
# vgarray = np.zeros((nR, nTheta, nPhi))
# lonsarray_km = np.zeros((nR, nTheta, nPhi))
# latsarray_km = np.zeros((nR, nTheta, nPhi))
# radsarray_km = np.zeros((nR, nTheta, nPhi))
# #vgarray_diff = np.zeros((nR, nTheta, nPhi))
# smootharray = np.zeros((nR, nTheta, nPhi))
# #for iLayer in range(nlayers):
# globInd = 0
# for iR in range(nR):
# for iTheta in range(nTheta):
# for iPhi in range(nPhi):
# r = R - depths[globInd]
# lat = lats[globInd]
# lon = lons[globInd]
# r_minor = np.cos(np.deg2rad(lat)) * r
# vgarray[iR, iTheta, iPhi] = vps[globInd]
# radsarray_km[iR, iTheta, iPhi] = r
# latsarray_km[iR, iTheta, iPhi] = np.pi * r * lat / 180.
# lonsarray_km[iR, iTheta, iPhi] = np.pi * r_minor * lon / 180.
# #vgarray_diff[iR, iTheta, iPhi] = vgrid[globInd]
# globInd += 1
#
# # iterate over grid diffs (correct?) and sum 1 * point left -2 * point + 1 * point right in all 3 dim.
# smsum = 0.
# for iR in range(nR):
# for iTheta in range(nTheta):
# for iPhi in range(nPhi):
# vg = vgarray[iR, iTheta, iPhi]
# sum1 = sum2 = sum3 = 0.
# if 0 < iPhi < nPhi - 1:
# h = abs(lonsarray_km[iR, iTheta, iPhi + 1] - lonsarray_km[iR, iTheta, iPhi - 1]) / 2
# sum1 = (vgarray[iR, iTheta, iPhi - 1] - 2 * vg + vgarray[iR, iTheta, iPhi + 1]) / h**2
# if 0 < iTheta < nTheta - 1:
# h = abs(latsarray_km[iR, iTheta + 1, iPhi] - latsarray_km[iR, iTheta - 1, iPhi]) / 2
# sum2 = (vgarray[iR, iTheta - 1, iPhi] - 2 * vg + vgarray[iR, iTheta + 1, iPhi]) / h**2
# if 0 < iR < nR - 1:
# h = abs(radsarray_km[iR - 1, iTheta, iPhi] - radsarray_km[iR + 1, iTheta, iPhi]) / 2
# sum3 = (vgarray[iR - 1, iTheta, iPhi] - 2 * vg + vgarray[iR + 1, iTheta, iPhi]) / h**2
# smsum += np.sqrt(sum1**2 + sum2**2 + sum3**2)
# #print(sum1, sum2, sum3, smsum)
# smootharray[iR, iTheta, iPhi] = smsum#sum1 + sum2 + sum3
#
# # m_T * D_T * D * m ?? todo: unsure
# norm = np.sum(smootharray ** 2)
#
# return norm, smootharray
from pylot.tomography.utils import normed_figure
def calc_smoothnorm(wdir, iter):
smv = np.loadtxt(os.path.join(wdir, 'it_{}/smv.out'.format(iter + 1)), skiprows=1)
dm = np.loadtxt(os.path.join(wdir, 'it_{}/dm.out'.format(iter + 1)), skiprows=1)
norm = np.sum(smv*dm)
return norm
def calc_dampnorm(wdir, iter):
ecmi = np.loadtxt(os.path.join(wdir, 'it_{}/ecmi.out'.format(iter + 1)), skiprows=1)
dm = np.loadtxt(os.path.join(wdir, 'it_{}/dm.out'.format(iter + 1)), skiprows=1)
norm = np.sum(ecmi * dm**2)
return norm
def calc_norm(wdir, iteration_number):
dampnorm = calc_dampnorm(wdir, iteration_number)
smoothnorm = calc_smoothnorm(wdir, iteration_number)
print('dampnorm: ', dampnorm)
print('smoothnorm: ', smoothnorm)
norm = dampnorm + smoothnorm
print('Calculated summed norm of', norm)
return norm, dampnorm, smoothnorm
def calc_tradeoff(fpath_in, fname_out=None, iteration_number = 12):
results = {}
for wdir in glob.glob(fpath_in):
#wdir = '/rscratch/minos13/marcel/fmtomo_alparray/alparray_mantle_from_m6.0_diehl_crustal_corrections_sm1000_damp100/'
smooth = float(wdir.split('_')[-2].split('sm')[-1])
damp = float(wdir.split('_damp')[-1].split('/')[0])
print('Calculating tradeoff for smoothing and damping of {}, {}'.format(smooth, damp))
if not smooth in results.keys():
results[smooth] = {}
iteration_number_new = iteration_number
ecmi_path = os.path.join(wdir, 'it_{}'.format(iteration_number_new + 1), 'ecmi.out')
smv_path = os.path.join(wdir, 'it_{}'.format(iteration_number_new + 1), 'smv.out')
while not os.path.isfile(ecmi_path) or not os.path.isfile(smv_path):
iteration_number_new -= 1
ecmi_path = os.path.join(wdir, 'it_{}'.format(iteration_number_new + 1), 'ecmi.out')
smv_path = os.path.join(wdir, 'it_{}'.format(iteration_number_new + 1), 'smv.out')
print('WARNING: Iteration number lowered by 1:', iteration_number_new)
if iteration_number_new <= 1:
break
if iteration_number_new <= 1:
continue
else:
iteration_number = iteration_number_new
#vgrid, gridn, griddelta, gridstart = read_vgrid(vgrid_path)
#vgrid_ref = read_vgrid(os.path.join(wdir, 'vgridsref.in'))[0]
norm, dampnorm, smoothnorm = calc_norm(wdir, iteration_number)
try:
fpath = os.path.join(wdir, 'residuals.dat')
chi = float(subprocess.check_output(['tail', fpath]).split()[-1])
except Exception as e:
print(e)
chi = np.nan
results[smooth][wdir] = {'dampnorm': dampnorm, 'smoothnorm': smoothnorm,
'norm': norm, 'chi': chi, 'damp': damp}
#print some output
for smooth, result in results.items():
print('Smoothing:', smooth)
for wdir, item in result.items():
print(item['chi'], item['norm'])
print(20*'#')
if fname_out:
with open(fname_out, 'w') as outfile:
json.dump(results, outfile)
return results
def quadratic_function(x, a, b, c):
return a * x ** 2 + b * x + c
def one_over_x(x, a, b, c):
return a / (x - b) + c
def exp_func(x, a, b, c):
return a * np.exp(-b * x) + c
def plot_tradeoff(fname_in, fix='smooth', plot_norm='both', min_smooth=0, min_damp=0, max_smooth=1e6, max_damp=1e6):
with open(fname_in, 'r') as infile:
results_smooth = json.load(infile)
lines = ["-", "--", "-.", ":"]
linecycler = cycle(lines)
# array will be built for each line: (smooth, damp, norm, chi)
plot_values = []
for smooth, result in results_smooth.items():
for item in result.values():
smooth = float(smooth)
damping = item['damp']
if smooth < min_smooth or damping < min_damp or smooth > max_smooth or damping > max_damp:
continue
plot_values.append(np.array([smooth, damping, item[plot_norm], item['chi']]))
plot_values = np.array(plot_values)
column_index = {'smooth': 0, 'damp': 1}
keys = np.unique(plot_values[:, column_index[fix]])
names = {'smooth': 'Smoothing', 'damp': 'Damping'}
for key in keys:
plot_line = plot_values[plot_values[:, column_index[fix]] == key]
second_index = column_index['smooth'] if fix == 'damp' else column_index['damp']
plot_line = np.array(sorted(plot_line, key=lambda x: x[second_index]))
norms = plot_line[:, 2]
chis = plot_line[:, 3]
#text = [str(item) for item in plot_line[:, second_index]]
x = np.linspace(min(norms), max(norms), num=100)
#popt, pcov = curve_fit(one_over_x, norms, chis, method='trf')#, bounds=[min(norms), max(norms)])
#fit_result = one_over_x(x, *popt)
#line = plt.plot(x, fit_result, ':', lw=0.8)[0]
fninfo = os.path.split(fname_in)[-1].replace('.json', '').split('_f')[-1]
label = '{}: {:g}'.format(names[fix], float(key))
line = plt.plot(norms, chis, linestyle=next(linecycler), lw=0.8, label=label)[0]
#coefs = poly.polyfit(norms, chis, 4)
#ffit = poly.polyval(x, coefs)
#line = plt.plot(x, ffit, ':', lw=0.8)[0]
#label = label='{}: {:g} (smgrad: {})'.format(names[fix], float(key), fninfo)
plt.plot(norms, chis, c=line.get_color(), marker='.', lw=0.)
#plt.text(norms, chis, text)
for item in plot_line:
plt.text(item[2], item[3], str(item[second_index]), horizontalalignment='left')
#plt.title('Plot of Misfit against Norm ({})'.format(plot_norm))
if __name__ == '__main__':
#calc_tradeoff('/data/AlpArray_Data/fmtomo/v5/tradeoff_curves/crust_included_grad_smooth_FIXED_dts_grad_1.5_sm*_damp*/',
# '/data/AlpArray_Data/various/alparray/tradeoff_v5_f1.5.json')
#calc_tradeoff('/data/AlpArray_Data/fmtomo/v5/tradeoff_curves/crust_included_grad_smooth_FIXED_dts_sm*_damp*/',
# '/data/AlpArray_Data/various/alparray/tradeoff_v5_f2.0.json')
fig = normed_figure(width_cm=10, ratio=1.)
#tradeoff_infiles = ['tradeoff_v4_f1.5.json', 'tradeoff_v4_f3.json', 'tradeoff_v4_f10.json']
tradeoff_infiles = ['tradeoff_v5_f2.0.json']#, 'tradeoff_v5_f1.5.json']
for infile in tradeoff_infiles:
infile = os.path.join('/data/AlpArray_Data/various/alparray/', infile)
plot_tradeoff(infile, fix='damp', plot_norm='norm')
plt.xlim([1900, 16200])
plt.ylim([2.72, 3.8])
plt.xlabel('Norm')
#plt.ylabel(r'Misfit($\frac{\chi^2}{N}$)')
plt.ylabel(r'Misfit($\chi^2/N$)')
#plt.title('Tradeoff curve Misfit vs Norm. Numbers in plot show smoothing values.')
plt.legend()
#plt.show()
plt.savefig('/data/AlpArray_Data/sciebo/AlpArray_home/pictures/paper_II/tradeoff.pdf', dpi=300)