From 7ed47ed4ad87a68ba4955e4eca2d68fd236f5df3 Mon Sep 17 00:00:00 2001
From: JanHab <jan.habscheid@rwth-aachen.de>
Date: Wed, 26 Mar 2025 10:35:32 +0100
Subject: [PATCH] =?UTF-8?q?add=20given=20code=C2=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../mcmc_task2_u0X1_final_student_mars21.py   | 422 ++++++++++++++++++
 Project3/src/GivenCode/simulations_Feb20.py   |  75 ++++
 Project3/src/GivenCode/solver_Feb20.py        | 112 +++++
 3 files changed, 609 insertions(+)
 create mode 100644 Project3/src/GivenCode/mcmc_task2_u0X1_final_student_mars21.py
 create mode 100644 Project3/src/GivenCode/simulations_Feb20.py
 create mode 100644 Project3/src/GivenCode/solver_Feb20.py

diff --git a/Project3/src/GivenCode/mcmc_task2_u0X1_final_student_mars21.py b/Project3/src/GivenCode/mcmc_task2_u0X1_final_student_mars21.py
new file mode 100644
index 0000000..c46cea7
--- /dev/null
+++ b/Project3/src/GivenCode/mcmc_task2_u0X1_final_student_mars21.py
@@ -0,0 +1,422 @@
+import matplotlib.pyplot as plt
+import numpy as np
+#import scipy.io as sio
+import scipy.sparse as sparse
+
+try:
+    from simulations_Feb20 import simulate_g
+except ImportError:
+     from python_scripts.simulations_Feb20 import simulate_g
+
+
+##############################################
+# read observation data provided by teacher  #
+##############################################
+
+# use only the first data set
+observation = np.load("data/True_Observation_data_1_upd.npz")
+#print( list(observation.keys()) )
+
+
+######################################################################################
+#  In the following different parameters required for solving conslaw are extracted  #
+######################################################################################
+
+
+# number of observation data set 
+Ant_Init = 1
+
+#y_k_true = observation["y_k_true"]
+#y_k_true = np.zeros_like(y_k_true)
+
+# provide information about grid, 
+x    = observation["x"]
+xh   = observation["xh"]
+
+# grid cells for cons law
+N = observation["M"].item()
+
+# Final time 
+T = observation["T"].item()
+
+# describe time points for which we have observations
+time_obs = observation["t_obspoint"]
+JX = time_obs <= T
+
+Ldom = observation["Ldom"].item()
+dx = Ldom/N
+
+# positions of point values for k(x)
+x_k = observation["x_k"]
+
+# index vector for numbering positions in space and time
+Nt = np.sum(JX)
+Ix = np.arange(0, N)
+It = np.arange(0, Nt)
+
+# number of unknown points to be determined in k(x) which is assumed piecewise linear
+Mr = observation["Mr"].item() 
+
+###############################
+# Observation data  h(theta)  #
+###############################
+u_true_X1 = np.zeros((Nt))
+u_true_X2 = np.zeros((Nt))
+u_true_X3 = np.zeros((Nt))
+u_true_X4 = np.zeros((Nt))
+
+# correct when Ant_Init = 1
+u_true_X1 = observation["u_true_X1"] 
+u_true_X2 = observation["u_true_X2"] 
+u_true_X3 = observation["u_true_X3"] 
+u_true_X4 = observation["u_true_X4"]   
+
+
+# provide initial data set: each initialdata (of total=Ant_Init) is a column 
+initial_true = np.zeros((N,2))
+initial_true[:,0] = observation["u0"]
+
+
+# provide position of observation points X_1, X_2, X_3, X_4 
+X_position = observation["X_position"]
+X_1 = X_position[0]
+X_2 = X_position[1]
+X_3 = X_position[2]
+X_4 = X_position[3]
+
+
+####################################
+# define parameters used in MCMC   #
+####################################
+add_unc = 0.03            # noise in observation data
+error_val = 100           # lower limit in Loss function
+kk_tolerance = 1000       # number of steps in MCMC algorithm
+teller = 1
+tau = 20                  # extract "theta^k" with step size "tau" to represent a posteriori "theta" 
+
+N_ens = int(kk_tolerance/tau)
+
+# function to create one long column vector to represent the matrix of 
+# 4 column vectors representing observation data at X1, X2, X3, X4
+def gen_measurements(u, weights):
+    measurement = []
+    for i, u in enumerate(u):
+        m = u + add_unc * np.random.normal(size=Nt * Ant_Init)
+        m[m < 0] = 0
+        m[m > 1] = 1
+        measurement.append(weights[i] * m)
+    return np.concatenate(measurement)
+
+
+# weight factor used to adjust the importance of the 4 different time series
+weights = [1, 1, 1, 1]
+
+# compute one single column vector to represent all observation data
+measurement = gen_measurements([u_true_X1, u_true_X2, u_true_X3, u_true_X4], weights)
+
+
+# parametervector "theta" which is updated by the mcmc algorithm
+# theta is composed of both k(x) and f(u) - however, only k(x) unknown
+initial_ensemble_kx = np.zeros((Mr, 1))
+
+# we generate an ensemble parameter vector by extracting every 20th member from "initial_ensemble_F"
+updated_ensemble_kx = np.zeros((Mr, N_ens))
+
+# mcmc parameter 
+#beta = 0.05 * 4  #   
+beta = 0.05   # 
+#beta = 0.05 * 0.25  # 
+
+#  we generate initial k(x) ~ {k_i}_i=1^9 as random normal distribution N(mu,sigma)
+mu = 1
+sigma = 0.25
+
+
+
+#########################################################
+#  Choose what should be the initial parameter vector   #
+#########################################################
+
+
+
+mcmc_start = True
+#mcmc_start = False
+
+if mcmc_start:
+      #####################################
+      #  choice 1:  generate theta^{(0)}  #
+      #####################################
+
+       print("Initial guess of k(x)")
+       kk_x = np.random.normal(loc=mu, scale=sigma, size=Mr)
+       kk_x[kk_x < 0.95] = 0.95
+       kk_x[kk_x > 1.3] = 1.3
+       kkx_u = kk_x
+else:
+      #######################################
+      #  choice 2:  start with theta^{(k)}  #
+      #######################################
+
+      with np.load('data/mcmc_u0X1_sample100_it1_student_Mars21.npz') as data_kx:
+      #with np.load('data/mcmc_u0X1_sample100_it2_student_Mars21.npz') as data_kx: 
+      #with np.load('data/mcmc_u0X1_sample100_it3_student_Mars21.npz') as data_kx:
+      #with np.load('data/mcmc_u0X1_sample100_it4_student_Mars21.npz') as data_kx:     
+       kkx_u = data_kx["theta_1"]
+
+
+# Create the y_k vector with padding 1s at the beginning and the end
+y_k = np.concatenate(([1], kkx_u, [1]))
+
+
+# number of parameter vectors (in case of more than one ensemble)
+K = 0
+
+
+# Create the plot
+plt.figure(1)
+plt.plot(x_k, y_k)
+plt.axis([-0.1, Ldom + 0.1, 0.1, 1.9])
+plt.grid(True)
+plt.xlabel("X")
+plt.ylabel("k(x)")
+plt.title("Resistance k(x)")
+
+# plt.ioff()
+
+# Show the plot and pause execution to view it
+plt.show(block=False)
+plt.pause(2)  # Pause for a short period of time; adjust as needed
+
+# Close the plot after pausing
+plt.close(1)
+
+# "initial_ensemble_kx" is composed of vector k(x) (M:M+Mr) 
+#  which is hidden and we seek to recover 
+initial_ensemble_kx[0:Mr, K] = kkx_u
+
+#######################################################
+# Run simulation to generate h(kkx_u)
+#######################################################
+
+###############################
+#  Step S3: Compute h(theta)  #
+###############################
+
+# h ~ simulate forward model
+# theta ~ initial_ensemble_kx
+
+# u_X =
+# ensembleOfPredictedObservations = 
+
+ensembleOfPredictedObservations = simulate_g(
+    N, x, xh, Ldom, T,
+    initial_true,
+    Ant_Init,
+    Nt,
+    time_obs,
+    It,
+    JX,
+    K,
+    weights,
+    initial_ensemble_kx,
+    X_1, X_2, X_3, X_4,
+    x_k, kkx_u,
+    u_true_X1, u_true_X2, u_true_X3, u_true_X4,
+    #to_plot=False,
+    #to_plot=True,
+)
+
+
+# do a numbering of the column vector of predicted observation = 1 long column vector
+# in case we want to throw away some observation data
+measIndex = np.arange(0, ensembleOfPredictedObservations.shape[0]) 
+weight_obs = sparse.diags(1 / (add_unc * np.ones(measIndex.shape[0])) ** 2)
+
+
+# Compute loss function
+meas_diff = ensembleOfPredictedObservations[measIndex, K] - measurement[measIndex]
+Phi_kkx = meas_diff.T @ weight_obs @ meas_diff
+
+
+# Initialization
+theta_1 = kkx_u.copy()
+Phi_theta_1 = Phi_kkx.copy()
+
+Loss = -1 * np.ones(kk_tolerance + 1)
+Loss[0] = Phi_theta_1.copy()
+
+input("Now entering main loop: \n Press enter to continue!")
+
+# counter of steps in MCMC
+kk = 0
+
+while Loss[kk] > error_val and kk < kk_tolerance:
+    
+    
+    #####################################################################
+    # Step S2:  compute "phi=gu" as a perturbation of "theta=fu_kp1"    #
+    #####################################################################
+    
+    phi_1 = np.sqrt((1 - beta**2)) * theta_1 + beta * sigma * np.random.normal(size = Mr)
+    
+    phi_1[phi_1 < 0.25] = 0.25
+    phi_1[phi_1 > 1.75] = 1.75
+
+    # prepare for solving the conservation law to obtain h(phi)
+    initial_ensemble_kx[np.arange(0, Mr), K] = phi_1
+
+    # Run simulation to generate G(gu)
+    # u_X is the generated time series to be compared with data
+    
+    
+    ###############################
+    #  Step S3: Compute h(phi)    #
+    ###############################
+    ensembleOfPredictedObservations = simulate_g(
+        N, x, xh, Ldom, T, 
+        initial_true,
+        Ant_Init,
+        Nt,
+        time_obs,
+        It,
+        JX,
+        K,
+        weights,
+        initial_ensemble_kx,
+        X_1, X_2, X_3, X_4,
+        x_k, theta_1,
+        u_true_X1, u_true_X2, u_true_X3, u_true_X4
+    )
+
+    # Compute Loss
+    meas_diff = ensembleOfPredictedObservations[measIndex, K] - measurement[measIndex]
+    Phi_phi_1 = meas_diff.T @ weight_obs @ meas_diff
+ 
+    ########################################
+    # Step S3:  Compute a(theta_1, phi_1)  #
+    ########################################
+  
+    # MCMC a(theta,phi)
+    a_theta_1_phi_1 = np.min([1, np.exp(Phi_theta_1 - Phi_phi_1)] )
+
+    ##########################################################
+    # Step S4 and S5:  compare "choice" and "a(theta, phi)"  #
+    #########################################################
+    
+    choice = np.random.uniform(0, 1)
+     
+    if choice <= a_theta_1_phi_1:
+        theta_1      = phi_1.copy()
+        Phi_theta_1  = Phi_phi_1.copy()
+        
+    else:
+        theta_1      = theta_1.copy()
+        Phi_theta_1  = Phi_theta_1.copy()   
+
+    #####################################################
+    #  check if we should extract this parameter vector #
+    #####################################################
+    
+    check = kk / tau
+
+    if check == teller:
+        updated_ensemble_kx[0 : Mr, teller] = theta_1
+        teller += 1
+        
+        fig, axs = plt.subplots(1, 2, figsize=(15, 5))
+        
+        #axs[0].plt.figure()
+        axs[0].to_plot = Loss[Loss >= 0]
+        axs[0].plot(Loss)
+        axs[0].set_title("Loss function")
+        axs[0].set_ylabel("Loss")
+        axs[0].set_xlabel("Iteration number")
+        axs[0].grid(True)
+        #axs[0].show()
+        
+        y_k_ident = np.concatenate([[1], theta_1, [1]])
+        
+        #axs[1].plt.figure()
+        #axs[1].plot(x_k, y_k_true, '-r')
+        axs[1].plot(x_k, y_k_ident,'-b')
+        axs[1].set_title("k(x) function")
+        axs[1].set_ylabel("k(x)")
+        axs[1].set_xlabel("x")
+        #axs[1].legend(['True', 'Identified'])
+        axs[1].legend(['Identified'])
+        plt.axis([0, Ldom, 0.25, 1.75])
+        axs[1].grid(True)
+        #axs[1].show()
+
+        # Update the plot
+        #plt.draw()
+        
+        # Show the plots
+        plt.show()        
+        #plt.pause(2)          
+        #input("Extracting this parameter vector: \n Press enter to continue!")
+
+    #############################################
+    # Step S6:  move to next iteration in MCMC  #
+    #############################################
+
+    kk += 1
+    Loss[kk] = Phi_theta_1.copy()
+
+    y_k_ident = np.concatenate([[1], theta_1, [1]])
+    
+    print("Iteration:", kk, "   Loss:", Loss[kk])
+
+
+######################################################
+#  Save to file variables from this MCMC compuation  #
+######################################################
+
+np.savez(
+   "data/mcmc_u0X1_sample100_it1_student_Mars21.npz",
+   #"data/mcmc_u0X1_sample100_it2_student_Mars21.npz",
+   #"data/mcmc_u0X1_sample100_it3_student_Mars21.npz",
+   # "data/mcmc_u0X1_sample100_it4_student_Mars21.npz",
+   # "data/mcmc_u0X1_sample100_it5_student_Mars21.npz",
+   #"data/mcmc_u0X1_sample100_it6_student_Mars21.npz",
+    Loss=Loss,
+    #fu_kp1=fu_kp1,
+    #Phi_fu_kp1_y=Phi_fu_kp1_y,
+    theta_1=theta_1,
+    Phi_theta_1=Phi_theta_1,
+    updatedEnsemble_kx = updated_ensemble_kx,
+    kk=kk,
+    teller=teller,
+    x_k=x_k,
+    #y_k_true=y_k_true,
+    y_k_ident=y_k_ident,
+    beta=beta,
+)
+print("Sampling of 100 members done")
+print("Final loss was:", Loss)
+print("And the lower error val was set to:", error_val)
+
+
+# Plot the loss function
+plt.figure()
+to_plot = Loss[Loss >= 0]
+plt.plot(Loss)
+plt.title("Loss function")
+plt.ylabel("Loss")
+plt.xlabel("Iteration number")
+#plt.axis([0, Ldom, 0.5, 2.0])
+plt.grid(True)
+plt.show()
+
+plt.figure()
+#plt.plot(x_k, y_k_true, '-r')
+plt.plot(x_k, y_k_ident, '-b')
+plt.title("k(x) function")
+plt.ylabel("k(x)")
+plt.xlabel("x")
+plt.axis([0, Ldom, 0.25, 1.75])
+#plt.legend(['True', 'Identified'])
+plt.legend(['Identified'])
+plt.grid(True)
+plt.show()
+
diff --git a/Project3/src/GivenCode/simulations_Feb20.py b/Project3/src/GivenCode/simulations_Feb20.py
new file mode 100644
index 0000000..571b913
--- /dev/null
+++ b/Project3/src/GivenCode/simulations_Feb20.py
@@ -0,0 +1,75 @@
+#import matplotlib.pyplot as plt
+import numpy as np
+#import scipy.sparse as sparse
+
+try:
+    from python_scripts.solver_Feb20 import my_solver
+except ImportError:
+    from solver_Feb20 import my_solver
+
+
+def simulate_g(
+    N, x, xh, Ldom, Tend, 
+    initial_true,
+    Ant_Init,
+    Nt,
+    time_obs,
+    It,
+    JX,
+    K,
+    weights,
+    initial_ensemble_kx,
+    X_1, X_2, X_3, X_4,
+    x_k, kx_old,
+    u_true_X1, u_true_X2, u_true_X3, u_true_X4,
+    #to_plot=False,
+    #to_plot = True,
+):
+    numEns = 1
+    
+    # vector that can store u(x1,t) for "Ant_Init" different initial data in one column vector 
+    ensembleOfPredictedObs_X1_0toT3 = np.zeros((Nt * Ant_Init, numEns))
+    ensembleOfPredictedObs_X2_0toT3 = np.zeros((Nt * Ant_Init, numEns))
+    ensembleOfPredictedObs_X3_0toT3 = np.zeros((Nt * Ant_Init, numEns))
+    ensembleOfPredictedObs_X4_0toT3 = np.zeros((Nt * Ant_Init, numEns))
+    
+    # list that can store u(x1,t),  u(x2,t),  u(x3,t),  u(x4,t) in column vector for each initial_data #K0
+    u_X = [np.zeros((len(time_obs), Ant_Init)) for _ in range(4)]
+    
+    #####################################
+    # Loop to simulate Ant_Init cases   #
+    #####################################
+    
+    for K_0 in range(0, Ant_Init):
+        
+        u_X[0][:, K_0], u_X[1][:, K_0], u_X[2][:, K_0], u_X[3][:, K_0], y_k = my_solver(
+            N, x, xh, Ldom, Tend, 
+            initial_ensemble_kx[:, K],  
+            initial_true[:, K_0],  
+            time_obs,
+            [X_1, X_2, X_3, X_4],
+            x_k,
+            #to_plot=to_plot,
+            #to_plot = False,
+            #to_plot = True,
+        )
+
+ # Store the obtained 4 time series "u_X" in the "ensembleOfPredictedObs_X1_0toT3"
+
+        ensembleOfPredictedObs_X1_0toT3[It + (K_0) * Nt, K] = u_X[0][JX, K_0]
+        ensembleOfPredictedObs_X2_0toT3[It + (K_0) * Nt, K] = u_X[1][JX, K_0]
+        ensembleOfPredictedObs_X3_0toT3[It + (K_0) * Nt, K] = u_X[2][JX, K_0]
+        ensembleOfPredictedObs_X4_0toT3[It + (K_0) * Nt, K] = u_X[3][JX, K_0]
+
+
+ # generate "ensembleOfPredictedObservations" which possess same structure as "measurement" 
+    ensembleOfPredictedObservations = np.concatenate(
+        [
+            weights[0] * ensembleOfPredictedObs_X1_0toT3,
+            weights[1] * ensembleOfPredictedObs_X2_0toT3,
+            weights[2] * ensembleOfPredictedObs_X3_0toT3,
+            weights[3] * ensembleOfPredictedObs_X4_0toT3,
+        ]
+    )
+    # return u_X, ensembleOfPredictedObservations
+    return ensembleOfPredictedObservations
diff --git a/Project3/src/GivenCode/solver_Feb20.py b/Project3/src/GivenCode/solver_Feb20.py
new file mode 100644
index 0000000..5721f0d
--- /dev/null
+++ b/Project3/src/GivenCode/solver_Feb20.py
@@ -0,0 +1,112 @@
+import matplotlib.pyplot as plt
+import numpy as np
+
+
+def my_solver(
+    #M,
+    N,
+    x,
+    xh,
+    Ldom,
+    Tend,
+    Fluxkx_ensem,
+    initial_true,
+    time_obs,
+    X,
+    x_k,
+    #to_plot=False,
+    #to_plot=True,
+):
+    """
+    Solution of u_t + (k(x)f(u))_x = 0
+    """
+
+    # Final time
+    T = Tend
+
+    # Delta x
+    dx = Ldom / N
+
+    # observation points in spatial domain
+    s = [ (np.sum(x <= X[i]) - 1) for i in range(4)]
+
+    # output predicted observations
+    u_X_out = [np.zeros(len(time_obs)) for _ in range(4)]
+
+    # terrain function
+    k_val = Fluxkx_ensem[:]
+    #y_k = np.concatenate((np.array([1]), k_val, np.array([1])))
+    y_k = np.concatenate(([1.0], k_val, [1.0] ))
+
+    # Define the flux function f(u)
+    def fun_flux(u):
+        f = np.zeros_like(u)
+        f =  u * (1 - u)
+        return f
+    
+    
+    # estimate  the time step dt based om CFL condition
+    dt_estimat = dx * (3/7)    # based on max |f'| = 1, max k(x) < 4/3
+    NTime = round(T / dt_estimat)
+
+    dt = T / NTime
+
+    #MA = max(maxfu_prime, 0.1)
+    
+    MA  = 1         # Rusanov flux is used
+    lmd = dt / dx
+
+    t_obs = np.linspace(0, T, NTime + 1)
+
+    J1 = np.arange(1, N - 1)
+    J2 = np.arange(0, N - 1)
+
+    u0 = initial_true[0:N]
+
+    u = np.zeros(N)
+
+  #  teller = 1
+
+    u_old = u0.copy()
+
+    # Compute predicted time serie state at t=0 
+    u_X = [np.zeros(NTime + 1) for i in range(4)]
+    for ind in range(4):
+        u_X[ind][0] = u0[s[ind]]
+
+   
+
+    for j in range(NTime):
+        
+        # fluxes at grid interface
+        F_half = np.zeros(N - 1)
+        Flux   = np.zeros(N)   # compute flux for all cells
+        
+        Flux = fun_flux(u_old)
+        F_half[J2] = 0.5 * (Flux[J2] + Flux[J2 + 1]) - 0.5 * MA * (u_old[J2 + 1] - u_old[J2] )
+        
+        k_half = np.zeros(N - 1)
+        k_half[J2] = np.interp(xh[J2 + 1], x_k, y_k)
+
+        # The numerical scheme
+
+        u[J1] = u_old[J1] - lmd * (k_half[J1] * F_half[J1] - k_half[J1 - 1] * F_half[J1 - 1])
+
+        u[u > 1] = 1.0
+        u[u < 0] = 0.0
+
+        u[0]  = u[1]
+        u[-1] = u[-2]
+
+       # Extract predicted observation data  
+        for ind in range(4):
+               u_X[ind][j+1] = u[s[ind]]  
+
+       
+        u_old = u.copy()
+
+   # transform predicted observation over to same time grid as used for true observation
+    for ind in range(4):
+      u_X_out[ind][:] = np.interp(time_obs[:], t_obs[:], u_X[ind][:])
+
+    return *u_X_out, y_k
\ No newline at end of file
-- 
GitLab