import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
# Konfiguriere matplotlib um schönere Plots zu erhalten
plt.rcParams.update({
    "interactive": True,
    "figure.dpi": 200,
    "figure.figsize": (10, 6),
    "font.family": "serif",
    "mathtext.fontset": "cm",
    "axes.grid": True,
    "axes.labelsize": 14,
    "grid.linewidth": .5,
    "grid.alpha": .5,
    "lines.linewidth": 1,
    "lines.dashed_pattern": (6, 4),
    "axes.prop_cycle": mpl.cycler(color=['k', 'r', 'b', 'g', 'y'])
})
h, delta = np.loadtxt("abweichung.dat", unpack=True)
plt.scatter(h, delta, s=5, label="Messwerte")
plt.xlabel("$h$")
plt.ylabel("$\\Delta_g(h)$")
plt.xscale("log")
plt.yscale("log")
plt.legend();
def fit_linear_region(i1, i2):
    lin_h = h[i1:i2]
    lin_delta = delta[i1:i2]
    m, b = np.polyfit(np.log(lin_h), np.log(lin_delta), 1)
    print(f"m = {m}, b = {b}")
    fit_delta = np.exp(b) * lin_h ** m
    plt.scatter(lin_h, lin_delta, s=20, label="Messwerte")
    plt.plot(lin_h, fit_delta, c="r", label="Fit der linearen Region")
    plt.xlabel("$h$")
    plt.ylabel("$\\Delta_g(h)$")
    plt.xscale("log")
    plt.yscale("log")
    plt.legend()
    return m, b
m_V, b_V = fit_linear_region(110, 150)
m_M, b_M = fit_linear_region(0, 90)
delta_M = np.sqrt(6) * np.exp(b_M)
print(f"delta_M = {delta_M}")
def f(x, omega):
    return np.cos(2 * np.pi * omega * x)
def f_deriv(x, omega):
    return -2 * np.pi * omega * np.sin(2 * np.pi * omega * x)
def g2(x, h, omega):
    return (f(x + h, omega) - f(x, omega)) / h 
def g3(x, h, omega):
    return (f(x + h, omega) - f(x - h, omega)) / (2 * h)
def find_h_max(delta_g: np.ndarray, h: np.ndarray, start_h: float = -np.inf):
    """Finds the first local maximum of delta_g for h >= start_h."""
    # Iteriere über alle relevanten h- und Delta-Werte
    for i in range(len(h) - 1):
        # Überspringe Werte vor dem angegebenen h-Startwert.
        # Dies wird benötigt, um die Maximums-Suche erst hinter dem Maschinenfehler-Bereich zu starten.
        if h[i] < start_h:
            continue
        if delta_g[i+1] < delta_g[i]:
            # Wenn der nächste Wert kleiner ist, als der aktuelle, hat man das Maximum erreicht.
            # Der zugehörige h-Wert wird zurückgegeben.
            return h[i]
    raise "Could not find local maximum."
def analyze_delta_g(
    x: float,
    omega: float,
    h: np.ndarray,
    start_h: float = -np.inf,
    plot: bool = True,
):
    # Berechne die Approximationsfehler
    delta_g2 = np.abs(g2(x, h, omega) - f_deriv(x, omega))
    delta_g3 = np.abs(g3(x, h, omega) - f_deriv(x, omega))
    # Bestimme h_max für beide Approximationen
    h_max2 = find_h_max(delta_g2, h, start_h)
    h_max3 = find_h_max(delta_g3, h, start_h)
    if plot:
        # Trage Approximationsfehler gegen h auf
        plt.scatter(h, delta_g2, s=5, label="2-Punkt-Formel", c="r", alpha=.7)
        plt.scatter(h, delta_g3, s=5, label="3-Punkt-Formel", c="g", alpha=.7)
        plt.xlabel("$h$")
        plt.ylabel("$\\Delta_g(h)$")
        plt.xscale("log")
        plt.yscale("log")
        # Trage h_max im Plot ein
        plt.axvline(h_max2, c="r", label="$h_\\mathrm{max, 2P} = " + f"{h_max2:.4f}$")
        plt.axvline(h_max3, c="g", label="$h_\\mathrm{max, 3P} = " + f"{h_max3:.4f}$")
        plt.legend()
    return h_max2, h_max3
analyze_delta_g(x=.1, omega=1, h=np.logspace(-14, 3, 400), start_h=1e-3)
def plot_for_many_omegas(omega_arr, x_arr):
    plt.figure(figsize=(10, 20))
    for i, (x, omega) in enumerate(zip(x_arr, omega_arr)):
        # Erstelle Subplot für jeden omega-Wert
        plt.subplot(len(omega_arr), 1, i + 1)
        plt.title(f"$\\omega = {omega:.2f}$")
        analyze_delta_g(x, omega, h=np.logspace(-14, 3, 400), start_h=1e-5)
    plt.tight_layout() # 
omega_arr = np.linspace(1, 20, num=6)
plot_for_many_omegas(omega_arr, x_arr=.1 / omega_arr)
def find_all_h_max(omega_arr, x_arr):
    h_max2_list = []
    h_max3_list = []
    for x, omega in zip(x_arr, omega_arr):
        h_max2, h_max3 = analyze_delta_g(x, omega, h=np.logspace(-3, 1, 2000), plot=False)
        h_max2_list.append(h_max2)
        h_max3_list.append(h_max3)
    return np.array(h_max2_list), np.array(h_max3_list)
omega_arr = np.linspace(1, 20, num=40)
h_max2_arr, h_max3_arr = find_all_h_max(omega_arr, x_arr=.1 / omega_arr)
plt.scatter(omega_arr, h_max2_arr, s=20, label="2-Punkt-Formel")
plt.scatter(omega_arr, h_max3_arr, s=20, label="3-Punkt-Formel")
plt.xlabel("$\\omega$")
plt.ylabel("$h_\\mathrm{max}$")
plt.legend();
def plot_h_max_against_omega(omega_arr, x_arr):
    h_max2_arr, h_max3_arr = find_all_h_max(omega_arr, x_arr)
    m2, b2 = np.polyfit(np.log(omega_arr), np.log(h_max2_arr), 1)
    m3, b3 = np.polyfit(np.log(omega_arr), np.log(h_max3_arr), 1)
    print(f"m2 = {m2}, m3 = {m3}")
    plt.scatter(omega_arr, h_max2_arr, s=20, label="2-Punkt-Formel", alpha=.5)
    plt.scatter(omega_arr, h_max3_arr, s=20, label="3-Punkt-Formel", alpha=.5)
    plt.xlabel("$\\omega$")
    plt.ylabel("$h_\\mathrm{max}$")
    plt.xscale("log")
    plt.yscale("log")
    plt.legend()
omega_arr = np.logspace(0, 1, base=20, num=40)
plot_h_max_against_omega(omega_arr, x_arr=.1/omega_arr)
omega_arr = np.linspace(1, 20, num=6)
plot_for_many_omegas(omega_arr, x_arr=.249 / omega_arr)
omega_arr = np.logspace(0, 1, base=20, num=40)
plot_h_max_against_omega(omega_arr, x_arr=.249/omega_arr)