# Earliness

The differences in performance with respect to earliness, is measured using mean absolute error over the first \textit{k} prefixes, across methods and data-sets. In other words, every \textit{t'th} event is evaluated independently of all prior or subsequent events in its trace.

\begin{equation}MAE_{t} = \frac{1}{N}\sum_{i=1}^{N}\mid y_{t}^i - \hat{y}_{t}^i\mid\end{equation}

Here, \textit{t} is the prefix or event number in each trace, and \textit{i} is the trace in the test period. This approach have been used in similar literature on both remaining time prediction, as well as classification problems with data in prefix-log format \cite{NiekTax2017,Verenich2019,Camargo2019,Teinemaa2018}. Since there is a difference in number of prefixes generated from each of the datasets (due to truncation), the same \textit{k} is used for the comparison of differences.

Due to the nature of the prefix log format, the support of the distribution of prefixes is skewed towards the lowest prefix indexes $t$. However, as \textit{business objective B} is not directed towards traces with low support, no form of normalization is applied across different prefixes in the earliness evaluation. Significance of the results are evaluated through ANOVA test.

```python

```

```python
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
#sns.set_theme(style="whitegrid")
sns.set(rc={'figure.figsize':(16,8)})

experiments = pd.read_csv("../Experiments.csv")

# filter out unfinished experiments
experiments = experiments.loc[experiments.Done == 1]

"""
Filter below needs to be removed!!
"""

# look at only one dataset (initial analysis)
#experiments = experiments.loc[experiments.F_dataset == 0.0]

experiments.head(50)
```

      F_lossfunction
      F_transform
      F_gamma
      F_beta
      F_alpha
      F_dataset
      F_max_epochs
      F_learningrate
      F_batch_size
      F_optimizer
      ...
      num_epochs
      loss_slope
      val_loss_slope
      Done
      AE_1
      AE_2
      AE_3
      AE_4
      AE_5
      Name_fix

      0
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      14
      -0.283260
      -0.061232
      1
      12.609097
      12.605162
      12.868543
      12.848513
      13.372967
      1

      1
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      19
      -0.148793
      -0.024662
      1
      12.895508
      12.886950
      13.127876
      13.087852
      13.561103
      1

      2
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      8
      -0.716135
      -0.143213
      1
      13.358388
      13.348356
      13.553132
      13.483675
      13.877950
      1

      3
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      8
      -0.769168
      -0.173036
      1
      12.774693
      12.773311
      13.018256
      13.005282
      13.494100
      1

      4
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      14
      -0.373218
      -0.226401
      1
      12.738092
      12.740521
      12.985113
      12.990197
      13.482388
      1

      5
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      9
      -0.350391
      -0.101520
      1
      14.404262
      14.411588
      14.545502
      14.463849
      14.710818
      1

      6
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      14
      -0.377374
      -0.194500
      1
      12.423886
      12.420921
      12.704752
      12.693185
      13.261295
      1

      7
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      14
      -0.308135
      -0.054824
      1
      12.380806
      12.372639
      12.668233
      12.621877
      13.206042
      1

      8
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      9
      -0.588406
      -0.124937
      1
      12.425355
      12.422824
      12.705997
      12.690886
      13.257702
      1

      9
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      21
      -0.127012
      -0.037865
      1
      12.572371
      12.564835
      12.835654
      12.803332
      13.339520
      1

      10
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      9
      -0.588102
      -0.221653
      1
      12.340967
      12.340548
      12.634565
      12.626893
      13.219011
      1

      11
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      18
      -0.235679
      -0.161399
      1
      12.284325
      12.274928
      12.590013
      12.601625
      13.246797
      1

      12
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      17
      -0.196408
      -0.054092
      1
      12.437930
      12.431116
      12.716992
      12.686429
      13.253915
      1

      13
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      6
      -0.910748
      0.150653
      1
      12.589520
      12.579846
      12.851006
      12.799526
      13.333861
      1

      14
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      23
      -0.154224
      -0.101628
      1
      12.829251
      12.820517
      13.067660
      13.025762
      13.509463
      1

      15
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      7
      -0.450103
      0.549500
      1
      12.227350
      12.218563
      12.584452
      12.543816
      13.281505
      1

      16
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      13
      -0.454347
      -0.235470
      1
      12.763612
      12.753952
      13.008222
      12.977686
      13.481466
      1

      17
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      12
      -0.408977
      -0.168218
      1
      12.804922
      12.791017
      13.045630
      12.978874
      13.474239
      1

      18
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      11
      -0.395881
      -0.108922
      1
      12.790626
      12.788358
      13.032684
      13.011582
      13.498121
      1

      19
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      7
      -0.690709
      -0.015611
      1
      13.367019
      13.361660
      13.931293
      13.926564
      15.099461
      1

      20
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      7
      -0.722017
      -0.019136
      1
      14.550177
      14.539499
      14.685976
      14.611763
      14.883795
      1

      21
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      21
      -0.174625
      -0.123762
      1
      12.639992
      12.630789
      12.896276
      12.859916
      13.378878
      1

      22
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      14
      -0.270343
      -0.073100
      1
      12.471729
      12.466491
      12.746534
      12.718327
      13.275327
      1

      23
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      11
      -0.517733
      -0.193540
      1
      12.227044
      12.217311
      12.560331
      12.510034
      13.196923
      1

      24
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      13
      -0.365772
      -0.098184
      1
      12.661763
      12.659631
      12.915820
      12.900611
      13.412396
      1

      25
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      7
      -0.836404
      -0.125462
      1
      14.502239
      14.486868
      14.639833
      14.540883
      14.821478
      1

      26
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      20
      -0.220818
      -0.134173
      1
      12.623423
      12.614506
      12.881403
      12.850334
      13.377544
      1

      27
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      22
      -0.189796
      -0.113394
      1
      12.282263
      12.278613
      12.588615
      12.599437
      13.231822
      1

      28
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      20
      -0.227283
      -0.147854
      1
      12.557439
      12.551617
      12.822381
      12.795164
      13.332774
      1

      29
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      19
      -0.255559
      -0.136644
      1
      12.813569
      12.801565
      13.053460
      13.000793
      13.501752
      1

      30
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      7
      -0.900350
      -0.181612
      1
      14.466274
      14.458543
      14.605176
      14.542796
      14.817124
      1

      31
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      6
      -0.915292
      0.174888
      1
      12.224976
      12.217404
      12.566095
      12.523708
      13.223673
      1

      32
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      7
      -1.130279
      -0.548571
      1
      12.287630
      12.280790
      12.592359
      12.555046
      13.180689
      1

      33
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      18
      -0.260875
      -0.150619
      1
      12.547757
      12.539981
      12.813775
      12.782441
      13.323645
      1

      34
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      27
      -0.158324
      -0.130616
      1
      12.326160
      12.322507
      12.622270
      12.609949
      13.212344
      1

      35
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      6
      -0.977821
      0.102744
      1
      12.226571
      12.218156
      12.560543
      12.522357
      13.212680
      1

      36
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      10
      -0.475101
      -0.121382
      1
      12.679166
      12.677179
      12.931519
      12.917018
      13.425051
      1

      37
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      7
      -0.744400
      -0.073276
      1
      15.843481
      15.832219
      15.935597
      15.863040
      16.052426
      1

      38
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      18
      -0.242554
      -0.194943
      1
      12.766460
      12.758389
      13.010801
      12.971383
      13.462736
      1

      39
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      15
      -0.293377
      -0.102722
      1
      12.286542
      12.277875
      12.591589
      12.559797
      13.189676
      1

      40
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      15
      -0.240536
      -0.050637
      1
      12.525158
      12.517258
      12.793687
      12.757056
      13.303525
      1

      41
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      13
      -0.360228
      -0.222715
      1
      12.225397
      12.217974
      12.570934
      12.533519
      13.245262
      1

      42
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      23
      -0.172525
      -0.112638
      1
      12.631717
      12.619553
      12.888848
      12.819251
      13.334937
      1

      43
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      7
      -0.874027
      -0.106191
      1
      12.771726
      12.762730
      13.015569
      12.961065
      13.458485
      1

      44
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      22
      -0.163778
      -0.106536
      1
      12.591314
      12.584966
      12.852609
      12.819744
      13.349178
      1

      45
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      7
      -0.976895
      -0.282828
      1
      17.726645
      17.715966
      17.775612
      17.698299
      17.794789
      1

      46
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      21
      -0.208460
      -0.143984
      1
      12.513779
      12.504814
      12.783450
      12.739445
      13.285174
      1

      47
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      11
      -0.677221
      -0.428980
      1
      12.251958
      12.241178
      12.569531
      12.511776
      13.168353
      1

      48
      MAE
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      7
      -1.084513
      -0.276722
      1
      13.514122
      13.508984
      13.698000
      13.667078
      14.034763
      1

      49
      MAE_td
      0.0
      0.0
      1.0
      1.75
      Sepsis
      200.0
      0.1
      2048.0
      0.0
      ...
      14
      -0.363471
      -0.203834
      1
      12.595350
      12.583878
      12.856215
      12.763032
      13.281620
      1

50 rows × 28 columns

```python
results = []

# go trough all experiments
for exp in set(experiments.RUN):

    # get experiment results
    experiment_i = experiments.loc[experiments.RUN == exp]

    # get inference table
    inference_test = pd.read_csv("../experiments/"+str(exp)+"/inference_tables/Inf_test.csv")

    ########## process the results #####################

    #placeholders
    event_numbers = []
    #num_of_events = []
    event_mae = []
    event_mae_normalized = []

    for prefix in set(inference_test.event_number):

        mae_prefix = np.mean(inference_test.loc[inference_test.event_number == prefix].AE)/60/60/24

        #get average duration per prefix
        avg_duration = np.mean(inference_test.loc[inference_test.event_number == prefix].caseduration_days)

        mae_prefix_normalized = mae_prefix/avg_duration

        num_events = inference_test.loc[inference_test.event_number == prefix].num_events

        event_numbers.append(prefix)
        event_mae_normalized.append(mae_prefix_normalized)
        event_mae.append(mae_prefix)

    # Collect experiment results
    results_i = pd.DataFrame({"event_number":event_numbers,
                              "event_mae_normalized":event_mae_normalized,
                              "event_mae":event_mae})

    for column in experiment_i.columns:
        results_i[column] = experiment_i.loc[experiment_i.index[0]][column]

    results.append(results_i)

#Create df
results = pd.concat(results)
```

```python
results.columns
```

    Index(['event_number', 'event_mae_normalized', 'event_mae', 'F_lossfunction',
           'F_transform', 'F_gamma', 'F_beta', 'F_alpha', 'F_dataset',
           'F_max_epochs', 'F_learningrate', 'F_batch_size', 'F_optimizer',
           'F_earlystop_steps', 'F_experiment', 'Notes', 'DOE', 'Duration_sec',
           'MAE', 'MAE_td', 'RUN', 'num_epochs', 'loss_slope', 'val_loss_slope',
           'Done', 'AE_1', 'AE_2', 'AE_3', 'AE_4', 'AE_5', 'Name_fix'],
          dtype='object')

```python
#Save results
results.to_csv("../Earliness_results.csv",index=False)
```

## Inspect a single dataset

```python
#Quick inspection of single dataset
dataset = ["Sepsis","helpdesk","traffic_fines","hospital_billing"][0]

"""
Look at only log-transformed models:
"""

max_num_events = 10

results_sub = results.loc[results.F_transform == 0]
results_sub = results_sub.loc[results_sub.F_max_epochs == 200]
results_sub = results_sub.loc[results_sub.F_dataset == dataset]

results_sub = results_sub.loc[results_sub.event_number < max_num_events+1]
results_sub = results_sub.loc[results_sub.event_mae < 16]
print("Num runs: ",str(len(results_sub.RUN.unique())))

#betas = results_sub.F_beta.unique()
#print(betas)
#results_sub = results_sub.loc[results_sub.F_beta > 0.5]

alphas = results_sub.F_alpha.unique()
print(alphas)
results_sub = results_sub.loc[results_sub.F_alpha == alphas[0]]
#results_sub = results_sub.loc[results_sub.F_alpha == 1]

ax = sns.boxplot(x="event_number", y="event_mae", hue="F_lossfunction",
                 data=results_sub)#,fliersize=0.0)
ax.set_title(dataset)
```

```python
import statsmodels.api as sm
from statsmodels.formula.api import ols
import statsmodels

# Subset results
results_sub = results.loc[results.event_number < max_num_events+1]
#results_sub = results_sub.loc[results_sub.event_mae < 25]

table = results_sub

#for loss in results_sub.F_lossfunction.unique():
#    print("\n\nLOSS: "+str(loss)+"\n\n")

alphas = results_sub.F_alpha.unique()
print(alphas)

for alpha in alphas:

    testdata = results_sub.loc[results_sub.F_alpha==alpha]

    #specify loss name
    baseline = "MAE"

    d1 = testdata.loc[testdata.F_lossfunction==baseline].event_mae.values

    #specify loss name
    comparison = "MAE_td"

    d2 = testdata.loc[testdata.F_lossfunction==comparison]#.MAE.values

    #further specify subset
    d2 = d2.event_mae.values

    out = statsmodels.stats.weightstats.CompareMeans.from_data(d1, d2).summary(use_t=True, alpha=0.05, usevar='pooled')

    print("DATA: "+ dataset)
    print("DATA: "+ str(alpha))
    print("MAE:     "+ str(np.mean(d1)))
    print(str(comparison)+":  "+ str(np.mean(d2)))
    print(out)
```

    [1.75]
    DATA: Sepsis
    DATA: 1.75
    MAE:     13.568666198328511
    MAE_td:  14.00893518498561
                              Test for equality of means
    ==============================================================================
                     coef    std err          t      P>|t|      [0.025      0.975]
    ------------------------------------------------------------------------------
    subset #1     -0.4403      0.100     -4.397      0.000      -0.637      -0.244
    ==============================================================================

```python
import statsmodels.api as sm
from statsmodels.formula.api import ols
import statsmodels

# Subset results
results_sub = results.loc[results.event_number < max_num_events+1]
results_sub = results_sub.loc[results_sub.event_mae < 16]

table = results_sub

#for loss in results_sub.F_lossfunction.unique():
#    print("\n\nLOSS: "+str(loss)+"\n\n")

alphas = results_sub.F_alpha.unique()
print(alphas)

alpha = alphas[0]

for evno in list(range(1,max_num_events)):

    testdata = results_sub.loc[results_sub.event_number == evno]

    testdata = testdata.loc[testdata.F_alpha==alpha]

    #specify loss name
    baseline = "MAE"

    d1 = testdata.loc[testdata.F_lossfunction==baseline].event_mae.values

    #specify loss name
    comparison = "MAE_td"

    d2 = testdata.loc[testdata.F_lossfunction==comparison]#.MAE.values

    #further specify subset
    d2 = d2.event_mae.values

    out = statsmodels.stats.weightstats.CompareMeans.from_data(d1, d2).summary(use_t=True, alpha=0.05, usevar='pooled')

    print("DATA: "+ dataset)
    print("Alpha: "+ str(alpha))
    print("Event: "+ str(evno))
    print("MAE:     "+ str(np.mean(d1)))
    print(str(comparison)+":  "+ str(np.mean(d2)))
    print(out)
```

    [1.75]
    DATA: Sepsis
    Alpha: 1.75
    Event: 1
    MAE:     12.775305093628898
    MAE_td:  12.816476410924887
                              Test for equality of means
    ==============================================================================
                     coef    std err          t      P>|t|      [0.025      0.975]
    ------------------------------------------------------------------------------
    subset #1     -0.0412      0.189     -0.218      0.829      -0.420       0.338
    ==============================================================================
    DATA: Sepsis
    Alpha: 1.75
    Event: 2
    MAE:     12.768535670548616
    MAE_td:  12.808785877030937
                              Test for equality of means
    ==============================================================================
                     coef    std err          t      P>|t|      [0.025      0.975]
    ------------------------------------------------------------------------------
    subset #1     -0.0403      0.189     -0.213      0.832      -0.419       0.339
    ==============================================================================
    DATA: Sepsis
    Alpha: 1.75
    Event: 3
    MAE:     13.025299834312843
    MAE_td:  13.091312987130882
                              Test for equality of means
    ==============================================================================
                     coef    std err          t      P>|t|      [0.025      0.975]
    ------------------------------------------------------------------------------
    subset #1     -0.0660      0.177     -0.372      0.711      -0.422       0.290
    ==============================================================================
    DATA: Sepsis
    Alpha: 1.75
    Event: 4
    MAE:     12.988680961820407
    MAE_td:  13.04980288646974
                              Test for equality of means
    ==============================================================================
                     coef    std err          t      P>|t|      [0.025      0.975]
    ------------------------------------------------------------------------------
    subset #1     -0.0611      0.175     -0.350      0.728      -0.411       0.289
    ==============================================================================
    DATA: Sepsis
    Alpha: 1.75
    Event: 5
    MAE:     13.495366607092796
    MAE_td:  13.517724799262153
                              Test for equality of means
    ==============================================================================
                     coef    std err          t      P>|t|      [0.025      0.975]
    ------------------------------------------------------------------------------
    subset #1     -0.0224      0.127     -0.176      0.861      -0.278       0.233
    ==============================================================================
    DATA: Sepsis
    Alpha: 1.75
    Event: 6
    MAE:     13.837139447957087
    MAE_td:  13.904736279685675
                              Test for equality of means
    ==============================================================================
                     coef    std err          t      P>|t|      [0.025      0.975]
    ------------------------------------------------------------------------------
    subset #1     -0.0676      0.124     -0.544      0.589      -0.317       0.182
    ==============================================================================
    DATA: Sepsis
    Alpha: 1.75
    Event: 7
    MAE:     13.736726474706966
    MAE_td:  13.819914184018169
                              Test for equality of means
    ==============================================================================
                     coef    std err          t      P>|t|      [0.025      0.975]
    ------------------------------------------------------------------------------
    subset #1     -0.0832      0.125     -0.668      0.507      -0.333       0.167
    ==============================================================================
    DATA: Sepsis
    Alpha: 1.75
    Event: 8
    MAE:     14.467001403007831
    MAE_td:  14.50014418016427
                              Test for equality of means
    ==============================================================================
                     coef    std err          t      P>|t|      [0.025      0.975]
    ------------------------------------------------------------------------------
    subset #1     -0.0331      0.076     -0.434      0.666      -0.186       0.120
    ==============================================================================
    DATA: Sepsis
    Alpha: 1.75
    Event: 9
    MAE:     13.957658561785887
    MAE_td:  14.059038973766757
                              Test for equality of means
    ==============================================================================
                     coef    std err          t      P>|t|      [0.025      0.975]
    ------------------------------------------------------------------------------
    subset #1     -0.1014      0.092     -1.103      0.275      -0.286       0.083
    ==============================================================================

```python
alpha = alphas[0]
plotdata = results_sub.loc[results_sub.F_alpha==alpha]

ax = sns.boxplot(x="event_number", y="event_mae_normalized", hue="F_lossfunction",
                 data=plotdata)#,fliersize=0.0)
ax.set_title(dataset+", Alpha = "+str(alpha))
#ax.set_ylabel(target)
```

```python
table = pd.pivot_table(results_sub,
                       values='event_mae_normalized', index=['F_lossfunction','F_alpha'],
                    columns=['event_number'], aggfunc=np.mean)
table
```

      event_number
      1
      2
      3
      4
      5
      6
      7
      8
      9

      F_lossfunction
      F_alpha

      MAE
      1.0
      0.49320
      0.256113
      0.274370
      0.705503
      0.653811
      0.570428
      0.445933
      0.538072
      1.024955

      MAE_td
      1.0
      0.50707
      0.249815
      0.251501
      0.704614
      0.645678
      0.581853
      0.442242
      0.518662
      1.026022

```python
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_theme()

# Load the example flights dataset and convert to long-form
flights_long = sns.load_dataset("flights")
flights = flights_long.pivot("month", "year", "passengers")

# Draw a heatmap with the numeric values in each cell
f, ax = plt.subplots(figsize=(16, 12))

cmap = sns.cm.rocket_r
sns.heatmap(table,
            cmap=cmap,
            annot=True,
            #fmt="d",
            linewidths=.5,
            ax=ax)
```

```python

```

# Normalized comparison

```python
results = pd.read_csv("../Earliness_results.csv")
results.head(120)
```

      event_number
      event_mae_normalized
      event_mae
      F_transform
      F_gamma
      F_beta
      F_alpha
      F_dataset
      F_max_epochs
      F_experiment
      Notes
      DOE
      Duration_sec
      MAE
      RUN
      Done

      0
      1
      0.922724
      13.543839
      0.0
      1.0
      0.2
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      216.170466
      15.473099
      1
      1

      1
      2
      0.922328
      13.538031
      0.0
      1.0
      0.2
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      216.170466
      15.473099
      1
      1

      2
      3
      0.921436
      14.131433
      0.0
      1.0
      0.2
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      216.170466
      15.473099
      1
      1

      3
      4
      0.921121
      14.126614
      0.0
      1.0
      0.2
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      216.170466
      15.473099
      1
      1

      4
      5
      0.919967
      15.347748
      0.0
      1.0
      0.2
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      216.170466
      15.473099
      1
      1

      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...

      115
      4
      0.923331
      14.160498
      0.0
      0.5
      0.2
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      206.296323
      15.517496
      5
      1

      116
      5
      0.922465
      15.389415
      0.0
      0.5
      0.2
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      206.296323
      15.517496
      5
      1

      117
      6
      0.921350
      16.159066
      0.0
      0.5
      0.2
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      206.296323
      15.517496
      5
      1

      118
      7
      0.914754
      16.126082
      0.0
      0.5
      0.2
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      206.296323
      15.517496
      5
      1

      119
      8
      0.906422
      17.592821
      0.0
      0.5
      0.2
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      206.296323
      15.517496
      5
      1

120 rows × 16 columns

```python
results.F_lossfunction.value_counts()
```

    MAE       800
    MAE_td    800
    Name: F_lossfunction, dtype: int64

```python
import seaborn as sns, matplotlib.pyplot as plt
#sns.set(style="whitegrid")

data = results.loc[results.event_number< 6]

ordered_data = sorted(data['F_transform'].unique())

g = sns.FacetGrid(data,col='F_transform',
                  col_order=ordered_data,
                  col_wrap=2,
                  aspect=1,
                  height=5.5,
                  legend_out=False,# xlim=5,
                  sharex=False)

g.map(sns.boxplot,'event_number','event_mae_normalized',"F_beta",
      fliersize=0.0,
      width=0.8,
      linewidth=1,
      saturation=0.75,
      palette='muted')

#g.despine(left=True)

#for ax in g.axes.flatten():
#    ax.tick_params(labelbottom=True)

plt.legend(loc='lower right')
plt.tight_layout()
plt.savefig("Earliness.png")
plt.show()
```

```python
import seaborn as sns, matplotlib.pyplot as plt
#sns.set(style="whitegrid")

data = results.loc[results.event_number< 6]
ordered_data = sorted(data['F_dataset'].unique())

g = sns.FacetGrid(data,col='F_dataset',
                  col_order=ordered_data,
                  col_wrap=2,
                  aspect=1,
                  height=5.5,
                  legend_out=False,# xlim=5,
                  sharex=False)

g.map(sns.boxplot,'event_number','event_mae_normalized',"F_lossfunction",
      fliersize=0.0,
      width=0.8,
      linewidth=1,
      saturation=0.75,
      palette='muted')

#g.despine(left=True)

#for ax in g.axes.flatten():
#    ax.tick_params(labelbottom=True)

plt.legend(loc='lower right')
plt.tight_layout()
plt.savefig("Earliness2.png")
plt.show()
```

# Generate a table: Event 1 only

```python
# Drop irrelevant loss functions
table = results_sub
#table = table.loc[table.F_lossfunction!="MSE"]
table = table.loc[table.event_number==1]
table
```

      event_number
      event_mae_normalized
      event_mae
      F_transform
      F_gamma
      F_beta
      F_alpha
      F_dataset
      F_max_epochs
      F_experiment
      Notes
      DOE
      Duration_sec
      MAE
      RUN
      Done

      84
      1
      0.858582
      12.602367
      1.0
      0.75
      0.20
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      34.761585
      13.494128
      4
      1

      140
      1
      0.845391
      12.408745
      1.0
      0.50
      0.20
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      21.894994
      13.414497
      6
      1

      196
      1
      0.880808
      12.928605
      1.0
      0.25
      0.20
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      22.348303
      13.534812
      8
      1

      252
      1
      0.962158
      14.122665
      1.0
      1.00
      0.25
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      16.646838
      14.330710
      10
      1

      308
      1
      0.837693
      12.295752
      1.0
      0.75
      0.25
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      14.832275
      13.319688
      12
      1

      364
      1
      0.969151
      14.225311
      1.0
      0.50
      0.25
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      13.490425
      14.228258
      14
      1

      420
      1
      0.858337
      12.598760
      1.0
      0.25
      0.25
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      18.657403
      13.512953
      16
      1

      476
      1
      0.857773
      12.590487
      1.0
      1.00
      0.30
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      24.645917
      13.639674
      18
      1

      532
      1
      0.852666
      12.515531
      1.0
      0.75
      0.30
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      17.396790
      14.009788
      20
      1

      588
      1
      0.852646
      12.515238
      1.0
      0.50
      0.30
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      22.639477
      13.469660
      22
      1

      644
      1
      0.861820
      12.649891
      1.0
      0.25
      0.30
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      18.598634
      13.493339
      24
      1

      700
      1
      0.838150
      12.302456
      1.0
      1.00
      0.35
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      15.820144
      14.074968
      26
      1

      756
      1
      0.903929
      13.267966
      1.0
      0.75
      0.35
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      13.971335
      14.995211
      28
      1

      812
      1
      0.842175
      12.361537
      1.0
      0.50
      0.35
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      21.252439
      13.375710
      30
      1

      868
      1
      0.856871
      12.577250
      1.0
      0.25
      0.35
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      23.147880
      13.514678
      32
      1

      924
      1
      0.846205
      12.420685
      1.0
      1.00
      0.00
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      19.864759
      13.476562
      34
      1

      980
      1
      0.890684
      13.073553
      1.0
      0.75
      0.00
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      17.115807
      13.846320
      36
      1

      1036
      1
      0.844489
      12.395498
      1.0
      0.50
      0.00
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      25.012746
      13.422136
      38
      1

      1092
      1
      0.851532
      12.498884
      1.0
      0.25
      0.00
      1.0
      0.0
      200.0
      1.0
      NaN
      Full_factorial
      27.431771
      13.426259
      40
      1

```python
import numpy as np
import scipy.stats

def mean_confidence_interval(data, confidence=0.95):
    a = 1.0 * np.array(data)
    n = len(a)
    m, se = np.mean(a), scipy.stats.sem(a)
    h = se * scipy.stats.t.ppf((1 + confidence) / 2., n-1)

    ci = np.round(h,3)
    mean = np.round(m,3)
    string = str(mean)+ " ! " +str(ci)
    return string

df = pd.pivot_table(table, index='F_beta', values="event_mae",
    columns="F_gamma", aggfunc=mean_confidence_interval)

latex = str((df.to_latex(index=True)))
latex = latex.replace('!', '\pm')
print(latex)
```

    \begin{tabular}{lllll}
    \toprule
    F\_gamma &          0.25 &          0.50 &          0.75 &          1.00 \\
    F\_beta &               &               &               &               \\
    \midrule
    0.00   &  12.499 \pm nan &  12.395 \pm nan &  13.074 \pm nan &  12.421 \pm nan \\
    0.20   &  12.929 \pm nan &  12.409 \pm nan &  12.602 \pm nan &           NaN \\
    0.25   &  12.599 \pm nan &  14.225 \pm nan &  12.296 \pm nan &  14.123 \pm nan \\
    0.30   &   12.65 \pm nan &  12.515 \pm nan &  12.516 \pm nan &   12.59 \pm nan \\
    0.35   &  12.577 \pm nan &  12.362 \pm nan &  13.268 \pm nan &  12.302 \pm nan \\
    \bottomrule
    \end{tabular}

    C:\Users\Mike\anaconda3\envs\TF2_CUD11_CRM_PAPER\lib\site-packages\numpy\core\_methods.py:233: RuntimeWarning: Degrees of freedom <= 0 for slice
      ret = _var(a, axis=axis, dtype=dtype, out=out, ddof=ddof,
    C:\Users\Mike\anaconda3\envs\TF2_CUD11_CRM_PAPER\lib\site-packages\numpy\core\_methods.py:226: RuntimeWarning: invalid value encountered in double_scalars
      ret = ret.dtype.type(ret / rcount)

```python
df = pd.pivot_table(table, index='F_gamma', values="event_mae",
    columns="F_beta", aggfunc=mean_confidence_interval)

df
```

    C:\Users\Mike\anaconda3\envs\TF2_CUD11_CRM_PAPER\lib\site-packages\numpy\core\_methods.py:233: RuntimeWarning: Degrees of freedom <= 0 for slice
      ret = _var(a, axis=axis, dtype=dtype, out=out, ddof=ddof,
    C:\Users\Mike\anaconda3\envs\TF2_CUD11_CRM_PAPER\lib\site-packages\numpy\core\_methods.py:226: RuntimeWarning: invalid value encountered in double_scalars
      ret = ret.dtype.type(ret / rcount)

      F_beta
      0.00
      0.20
      0.25
      0.30
      0.35

      F_gamma

      0.25
      12.499 ! nan
      12.929 ! nan
      12.599 ! nan
      12.65 ! nan
      12.577 ! nan

      0.50
      12.395 ! nan
      12.409 ! nan
      14.225 ! nan
      12.515 ! nan
      12.362 ! nan

      0.75
      13.074 ! nan
      12.602 ! nan
      12.296 ! nan
      12.516 ! nan
      13.268 ! nan

      1.00
      12.421 ! nan
      NaN
      14.123 ! nan
      12.59 ! nan
      12.302 ! nan

# F tests - all datasets

```python
table
```

      event_number
      event_mae_normalized
      event_mae
      F_lossfunction
      F_loss_weight
      F_dataset
      F_num_blocks
      F_learningrate
      F_batch_size
      F_num_neurons
      F_earlystop_steps
      F_max_epochs
      F_experiment
      Notes
      DOE
      Duration_sec
      MAE
      RUN
      Done
      Name_fix

      0
      1
      1.001540
      115.931302
      MAE
      0.25
      hospital_billing
      1.0
      0.10
      2048.0
      100.0
      5.0
      25.0
      1.0
      NaN
      Full_factorial
      188.900873
      72.493195
      1
      1
      1

      0
      1
      1.001118
      115.882476
      MAE_td
      0.25
      hospital_billing
      1.0
      0.10
      2048.0
      100.0
      5.0
      25.0
      1.0
      NaN
      Full_factorial
      265.634506
      72.443599
      2
      1
      1

      0
      1
      1.001128
      115.883626
      MAE
      0.50
      hospital_billing
      1.0
      0.10
      2048.0
      100.0
      5.0
      25.0
      1.0
      NaN
      Full_factorial
      188.968374
      72.444767
      3
      1
      1

      0
      1
      1.001194
      115.891244
      MAE_td
      0.50
      hospital_billing
      1.0
      0.10
      2048.0
      100.0
      5.0
      25.0
      1.0
      NaN
      Full_factorial
      269.171463
      72.452505
      4
      1
      1

      0
      1
      1.001493
      115.925927
      MAE
      0.75
      hospital_billing
      1.0
      0.10
      2048.0
      100.0
      5.0
      25.0
      1.0
      NaN
      Full_factorial
      191.823343
      72.487737
      5
      1
      1

      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...

      0
      1
      1.010220
      40.603386
      MAE_td
      0.75
      helpdesk
      2.0
      0.05
      2048.0
      200.0
      5.0
      25.0
      1.0
      NaN
      Full_factorial
      147.433831
      31.681558
      156
      1
      1

      0
      1
      1.010238
      40.604102
      MAE
      1.00
      helpdesk
      2.0
      0.05
      2048.0
      200.0
      5.0
      25.0
      1.0
      NaN
      Full_factorial
      145.472195
      31.682273
      157
      1
      1

      0
      1
      1.010221
      40.603402
      MAE_td
      1.00
      helpdesk
      2.0
      0.05
      2048.0
      200.0
      5.0
      25.0
      1.0
      NaN
      Full_factorial
      147.056692
      31.681574
      158
      1
      1

      0
      1
      1.010468
      40.613361
      MAE
      1.25
      helpdesk
      2.0
      0.05
      2048.0
      200.0
      5.0
      25.0
      1.0
      NaN
      Full_factorial
      150.079650
      31.691534
      159
      1
      1

      0
      1
      1.010187
      40.602044
      MAE_td
      1.25
      helpdesk
      2.0
      0.05
      2048.0
      200.0
      5.0
      25.0
      1.0
      NaN
      Full_factorial
      145.060077
      31.680217
      160
      1
      1

160 rows × 20 columns

```python
import statsmodels.api as sm
from statsmodels.formula.api import ols
```

```python
acc_lm = ols('MAE ~ C(F_lossfunction) + C(F_dataset) + C(F_lossfunction) * C(F_dataset)', data=table).fit()
```

```python
anovatable = sm.stats.anova_lm(acc_lm, typ=2) # Type 2 ANOVA DataFrame
```

```python
print(anovatable)
```

                                          sum_sq     df             F    PR(>F)
    C(F_lossfunction)                   0.003422    1.0  1.022939e+00  0.313389
    C(F_dataset)                    66430.692153    1.0  1.985607e+07  0.000000
    C(F_lossfunction):C(F_dataset)      0.002726    1.0  8.146686e-01  0.368135
    Residual                            0.521915  156.0           NaN       NaN

# T-test comparisons with MAE as baseline
## Event_number == 1*

```python
import statsmodels

datasets = ["Sepsis","helpdesk","hospital_billing","traffic_fines"]

losses = ["MAE","MAE_td"]#,"MAE_Mtd","MAE_Etd","MSE"]

# Subset results on first N events
table = results.loc[results.event_number==1]

for loss in losses:
    print("\n\nLOSS: "+loss+"\n\n")
    for dataset in datasets:

        testdata = results.loc[results.F_dataset==dataset]

        baseline = "MAE"

        d1 = testdata.loc[testdata.F_lossfunction==baseline].MAE.values

        comparison = loss

        d2 = testdata.loc[testdata.F_lossfunction==comparison].MAE.values
        out = statsmodels.stats.weightstats.CompareMeans.from_data(d1, d2).summary(use_t=True, alpha=0.05, usevar='pooled')
        print("DATA: "+dataset)
        print("MAE:     "+ str(np.mean(d1)))
        print(comparison+":  "+ str(np.mean(d2)))
        print(out)
```

    LOSS: MAE

    DATA: Sepsis
    MAE:     14.03114797274301
    MAE:  14.03114797274301
                              Test for equality of means
    ==============================================================================
                     coef    std err          t      P>|t|      [0.025      0.975]
    ------------------------------------------------------------------------------
    subset #1           0      0.103          0      1.000      -0.203       0.203
    ==============================================================================
    DATA: helpdesk
    MAE:     13.992142083792192
    MAE:  13.992142083792192
                              Test for equality of means
    ==============================================================================
                     coef    std err          t      P>|t|      [0.025      0.975]
    ------------------------------------------------------------------------------
    subset #1           0      0.420          0      1.000      -0.826       0.826
    ==============================================================================
    DATA: hospital_billing
    MAE:     55.52804211126678
    MAE:  55.52804211126678
                              Test for equality of means
    ==============================================================================
                     coef    std err          t      P>|t|      [0.025      0.975]
    ------------------------------------------------------------------------------
    subset #1           0      0.388          0      1.000      -0.764       0.764
    ==============================================================================
    DATA: traffic_fines
    MAE:     76.8739544625139
    MAE:  76.8739544625139
                              Test for equality of means
    ==============================================================================
                     coef    std err          t      P>|t|      [0.025      0.975]
    ------------------------------------------------------------------------------
    subset #1           0      0.067          0      1.000      -0.132       0.132
    ==============================================================================

    LOSS: MAE_td

    DATA: Sepsis
    MAE:     14.03114797274301
    MAE_td:  33.219569865902706
                              Test for equality of means
    ==============================================================================
                     coef    std err          t      P>|t|      [0.025      0.975]
    ------------------------------------------------------------------------------
    subset #1    -19.1884      3.447     -5.567      0.000     -25.952     -12.425
    ==============================================================================
    DATA: helpdesk
    MAE:     13.992142083792192
    MAE_td:  586.3274581958929
                              Test for equality of means
    ==============================================================================
                     coef    std err          t      P>|t|      [0.025      0.975]
    ------------------------------------------------------------------------------
    subset #1   -572.3353    151.530     -3.777      0.000    -870.023    -274.647
    ==============================================================================
    DATA: hospital_billing
    MAE:     55.52804211126678
    MAE_td:  55.166474504441986
                              Test for equality of means
    ==============================================================================
                     coef    std err          t      P>|t|      [0.025      0.975]
    ------------------------------------------------------------------------------
    subset #1      0.3616      0.385      0.940      0.348      -0.395       1.119
    ==============================================================================
    DATA: traffic_fines
    MAE:     76.8739544625139
    MAE_td:  76.82713585844172
                              Test for equality of means
    ==============================================================================
                     coef    std err          t      P>|t|      [0.025      0.975]
    ------------------------------------------------------------------------------
    subset #1      0.0468      0.066      0.708      0.479      -0.083       0.177
    ==============================================================================

```python

max_events = np.max(inference_test.event_number)

if max_events < 10:
    events = 5
else:
    events = 10

inf_results = inference_test.loc[inference_test.event_number < events+1]
inf_results.AE = inf_results.AE /(24.0*3600)

pivottable = pd.pivot_table(inf_results,
                       values='AE',
                    columns=['event_number'], aggfunc=np.mean)

newcols = []
for colno in range(0,len(pivottable.columns)):
    colno = colno + 1
    name = "AE_"+str(colno)+""
    newcols.append(name)

pivottable.columns = newcols
pivottable.index = [0]
pivottable
```

    C:\Users\Mike\anaconda3\envs\TF2_CUD11_CRM_PAPER\lib\site-packages\pandas\core\generic.py:5170: SettingWithCopyWarning:
    A value is trying to be set on a copy of a slice from a DataFrame.
    Try using .loc[row_indexer,col_indexer] = value instead

    See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
      self[name] = value

      AE_1
      AE_2
      AE_3
      AE_4
      AE_5
      AE_6
      AE_7
      AE_8
      AE_9
      AE_10

      0
      12.238627
      12.237781
      12.562031
      12.562555
      13.222226
      13.633513
      13.625139
      14.455449
      14.018538
      14.725038

```python
events
```

    10

```python

```


Earthquake physics

 Dynamic Rupture in 2D

##### Authors:
* Mitchell Williams
* Kenneth Duru
---

## Description

### Background

This notebook simulates  dynamic earthquake ruptures and elastic wave propagation in two space dimensions (2D). Spontaneously propagating shear ruptures on a frictional interface in an elastic solid is a useful idealization of natural earthquakes. The conditions relating slip-rate and fault strength are often expressed as nonlinear friction laws.

During an earthquake, two sides of the fault, initially held in contact by a high level of frictional resistance, slip suddenly when that resistance catastrophically decreases, generating strong ground shaking which is carried by seismic waves to remote areas, far away from fault zones.

This notebook describes and implements a high order accurate finite difference method for enforcing nonlinear friction laws in a consistent and provably stable manner, suitable for efficient explicit time integration and accurate propagation of seismic waves in heterogeneous media with free surface boundary conditions.

### The elastic wave equation
--------------------------------------------------------------------
Consider two elastic solids separated by a fault at the interface $x = 0$. In each elastic solid, wave motion is described by the elastic wave equation, in velocity-stress formulation:

\begin{align}
{\rho} {\partial_t v_i} = {\frac{ \partial \sigma_{ij}}{\partial x_j},  \quad}  {\quad}   S_{klij}{\partial_t \sigma_{ij}} = \frac{1}{2}  \cdot (\frac{\partial v_k}{\partial x_l} + \frac{\partial v_l}{\partial x_k}), \quad i, j = 1, 2,
\end{align}

 with particle velocities ${v_i}$, stress tensor ${\sigma_{ij} }$, compliance tensor $S_{klij}$, and density ${\rho}$.

-----------------------------------------------------------------

On the fault there is the velocity vector $\left(v_{n}, v_{m}\right)=\left(v_{x}, v_{y}\right)$, and the traction vector $\left(T_{n}, T_{m}\right)=\left(\sigma_{xx}, \sigma_{xy}\right)$, where $v_{n}$, $T_{n}$ are the normal components and $v_{m}$, $T_{m}$ being the shear components on the  interface. Denoting the fields in the positive and the negative parts of the fault with the superscripts $\pm$, we introduce the jumps in particle velocities by $\lbrack{v_j}\rbrack = {v_j}^{+}-{v_j}^{-}$, with $j = m, n$, and  the total traction by $T_j = T_{0j} + \Delta{T_j}$, where $T_{0j}$ are the initial background tractions and $\Delta{T_j}$ are the traction changes on the fault. The conditions on the fault connecting the two elastic solids are

\begin{align}
\text{force balance}&: \quad T_{j}^{+} = T_{j}^{-} = T_{j}, \quad j = m, n, \\
\text{no opening}&: \quad \lbrack{v_n}\rbrack = 0,\\
\text{friction laws}&: \quad T_m = \sigma_n\frac{f(V, \theta)}{V}\lbrack{v_m}\rbrack, \quad V = \left|\lbrack{v_m}\rbrack\right|.
\end{align}

 Here $\sigma_n > 0$ is the compressive normal stress and $ f(V, \theta) \ge 0 $ is the nonlinear fricition coefficient relating to the fault's shear strength, $\tau = |T_m| > 0$, and will be described  in more detail below.

-------------------------------------------------------------

### Fracture modes

There are three modes of fracture as illustrated below. Mode I fracture is an in plane motion where the direction of movement is perpendicular to the fracture itself, this opening can be described by a single component tensile component. Mode II is an in plane fracture that moves parallel to the fracture, it can be described by a compressive component that is perpendicular to the fracture (non-opening) and another parallel component that is creating shearing forces. This is a widely used mode for rupture dynamics because, both P and S waves emanate from this mode. This can allow 'super shear' ruptures, shear ruptures propagating above the S wave velocity. Mode III is an out of plane shearing motion and can be described in one out of plane component. This mode of rupture has a strict upper bound of the S wave velocity. Since earthquakes don't open wide chasms into the earth, mode I is not used here.

Figure source: https://www.doitpoms.ac.uk/tlplib/brittle_fracture/same.php

### Friction laws

It is necessary to consider the role of friction in all of this because it modulates the stresses and slip speed as the separate sides of the fault slip past each other. The first thing to consider is that as the fault slips it experiences shear strength ${\tau}$ which is exerted by the normal stresses on the fault ${\sigma_n}$. The way these two variables are related is through the friction coefficient ${f}$: ${\quad} {\tau} = f \cdot {\sigma_n}$.

 For the purposes of this notebook  we consider two widely used models of friction, *slip-weakening* and *rate-and-state*.

#### Slip Weakening Friction
Slip weakening friction describes a friction behavior in which friction coefficient weakens linearly with slip.  When the fault is at rest the friction coefficient is denoted by static friction coefficient ${f_s}$, when the fault is in motion the friction coefficient is denoted by the dynamic friction coefficient ${f_d}$. An earthquake begins when the load $\tau$ at a finite patch on the fault overcomes the peak frictional strength, $\tau \ge \tau_p$, ${\quad \tau_p = f_s \cdot \sigma_n}$.  As the earthquake continues the fault surfaces slide relative to each other  and the frictional coefficient evolves from linearly from static friction coefficient $f_s$  to the dynamic friction coefficient ${f_d}$. The fault is fully weakened when the slip reaches the critical slip ${D_c}$. This can be seen in the figure above which plots the friction coefficient as a function of slip.

#### Rate and State Friction

Rate and state friction is a constitutive law that was empirically found and is considered more realistic than slip weakening. It takes the form:

\begin{align}
 &\tau = \sigma_n \cdot f(V, \theta), \quad f(V, \theta) = [ f_0 + a \cdot ln(\frac {V}{V_0}) + b \cdot ln( \frac{V_0 \theta}{D_c})], \\
  &\frac {d \theta}{dt} = G(V, \theta).
 \end{align}

In this equation ${V}$ is the current slip velocity and ${V_0}$ is a reference slip velocity while ${f_0}$ is the steady state friction coefficient at the reference velocity: ${f_{V= V_0}}$. There is the state variable ${\theta}$, and the state evolution law $G(V, \theta)$. We consider specifically the aging law: ${ G(V, \theta) = 1 - \frac {V\theta }{D_c}.\quad}$ At steady state the state variable is proportional to the critical distance divided by velocity ${ \theta_{ss} = \frac {D_c}{V}}$

Here ${a}$ is the *direct effect* and is used to model how the system responds to velocity changes. ${b}$ is the *evolution effect* and describes the magnitude of the steady state friction.  ${D_c}$ is the critical distance and corresponds to the slip length over which the evolution of ${a \to b}$ occurs. The relationship of ${a}$ and ${b}$ to the slip weakening friction variables is as follows: ${\quad b = \frac {df_s}{d(ln(t))} \quad, \quad (a-b) = \frac {df_d}{d(ln(V))} \quad}$ where ${t}$ is the amount of time of contact without an earthquake. It is interesting to notice that there is a relationship between slip velocity ${V}$ and dynamic friction ${f_d}$ that depends on the difference ${(a-b)}$. If ${(a-b)>0}$ it shows that there is a velocity strengthening relationship ${\frac {df_d}{d(ln(V))} > 0}$. Any rupture that enters this region will arrest and is called a *stable* region. If ${(a-b)<0}$ it shows that there is a velocity weakening relationship ${\frac {df_d}{d(ln(V))} < 0}$, this is where earthquakes can nucleate and grow and is called *unstable*. On the border between *stable* and *unstable* regions is a zone of *conditional stability* where ruptures can sustain propagation while not growing or terminating. This notebook only exists in the unstable zone.

The equations are discretized using the SBP-SAT finite difference scheme. Time integration is performed using the classical fourth order accurate Runge-Kutta method. For more elaborate discussions, we refer the reader to the references and the  notebooks on the SBP-SAT method. A summary of the numerical implementations used in the this notebook is presentated in the flow-chart below.

### Putting it together

In the parameter window you can modulate the material properties, run time, domain size, and CFL criterion. In addition, you can choose rate and state friction or slip weakening friction, notice the differences that occur with this choice. In the Calculations and plotting cell the resulting on fault tractions and slip velocities are solved for in the RK4_2D.elastic_RK4_2D function, which also calls on the other associated functions. The bottom cell contains 5 output plots of the rupture behavior.

The plot that is observed immediately under this cell is the particle velocity plotted in the entire domain. The fault runs vertically through the center of the model and is marked by a vertical line. When this animation finishes the seismograms of the earthquake pop up under this. In the last cell there are three plots that come from the data generated in the previous cell. These three plots show the on fault slip, slip rate, and tractions as they evlove through time. Lastly there is a plot that shows the slip rate on the fault through time.

#### References
Byerlee, J. “Friction of Rocks.” Rock Friction and Earthquake Prediction, vol. 116, 1978, pp. 615–626., doi:10.1007/978-3-0348-7182-2_4.

Duru, Kenneth, and Eric M. Dunham. “Dynamic Earthquake Rupture Simulations on Nonplanar Faults Embedded in 3D
Geometrically Complex,
${\quad}$ Heterogeneous Elastic Solids.” Journal of Computational Physics, vol. 305, 2016, pp. 185–207., doi: 10.1016/j.jcp.2015.10.0215

Gustafsson, Bertil. High Order Difference Methods for Time Dependent PDE. Springer, 2008.

Kozdon, J. E., E. M. Dunham, and J. Nordström (2012), Interaction of waves with frictional interfaces using summation-by-parts difference operators:
${\quad}$  Weak enforcement of nonlinear boundary conditions, Journal of Scientific Computing, 50(2), 341-367, doi:10.1007/s10915-011-9485-3

Leeman, John. “Modeling Rate and State Friction with Python.” SciPy 2016. Austin, USA.

Scholz, Christopher H. “Earthquakes and Friction Laws.” Nature, vol. 391, no. 6662, 1998, pp. 37–42., doi:10.1038/34097.

### Exercises

Explore the following questions by interacting with the variables in the parameter windows.

* **1)** Explore the differences between slip weakening and rate and state frictions. How does this alter the rupture behavior?

* **2)** How does altering the resolution change the model time? At what point does poor resolution negatively affect the model.

* ** 3)** Compare the differences between mode II and mode III. How does altering the P wave velocity alter this?

* **4) ** Modulate the background stresses in the friction and fracture parameter window. If the stresses are too high what happens to the rupture? If they are too low?

* **5)** When altering all of these parameters what changes do you see in the evolution of slip-rate? Which combination of parameters would create the most dangerous earthquake?

```python
# Import necessary routines
%matplotlib notebook
import numpy as np
import matplotlib.pyplot as plt
import RK4_2D

#plt.switch_backend("TkAgg")          # plots in external window
plt.switch_backend("nbagg")           # plots within this notebook
```

In order to show a less time consuming solution the spatial and temporal resolution has been decreased. In the case you would like to increase it, you can modify the grid points in x and y (mantaining their proportions), the snapshot frequency and the fracture mode.

```python
# Parameter cell
Lx = 10.0     # length of the domain (x-axis)
Ly = 20.0     # width of the domain (y-axis)

nx = 50       # grid points in x
ny = 100      # grid points in y
tend = 0.98*3.464      # final time
CFL = 0.5

isnap = 5     # snapshot frequency
order = 4     # spatial order of accuracy (2,4,6)

nf = 5        # number of fields

cs_l = 3.464    # shear wave speed
cp_l = 6.0      # compresional wave speed
rho_l = 2.6702  # density

cs_r = 3.464    # shear wave speed
cp_r = 6.0      # compresional wave speed
rho_r = 2.6702  # density

# Bi-material:
#cs_r = 2.0      # shear wave speed
#cp_r = 4.0      # compresional wave speed
#rho_r = 2.6     # density

dx = Lx/nx    # spatial step in x
dy = Ly/ny    # spatial step in y

print('your x-axis resolution is '+str(dx)+' km')
print('your y-axis resolution is '+str(dy)+' km')
```

    your x-axis resolution is 0.2 km
    your y-axis resolution is 0.2 km

```python
# Friction and Fracture parameter cell

# Friction type
fric_law = 'SW'   #RS OR SW

# Fracture mode
mode = 'III'   #  'II' or 'III'

cs = np.max([cs_l, cs_r])
cp = np.max([cp_l, cp_r])

# Set mode info
# Mode II crack: In-plane shear
if mode == 'II':
    nf = 5        # number of fields [vx, vy, sxx, syy, sxy]
    dt = CFL/np.sqrt(cp**2 + cs**2)*dx    # Time step

# Mode III crack: out-of-plane (anti-plane) shear
if mode == 'III':
    nf = 3        # number of fields  [vz, sxz, sxy]
    dt = dx*CFL/cs    # Time step

nt = np.int(np.round(tend/dt)) # number of time steps

# To test your own friction parameters change the varible 'user_params' from 'default' to 'edit'
# then reasign your own values to the following variables
user_params = 'default'

Tau_0_user = np.ones((ny, 1))                           # shear stress
alp_s_user = np.ones((ny, 1))                           # stastic friction
alp_d_user = np.ones((ny, 1))                           # dynamic friction
D_c_user = np.ones((ny, 1))                             # critical slip
sigma_n_user = -np.ones((ny, 1))                        # normal stress
L0_user = np.ones((ny, 1))                              # state evolution distance
f0_user = np.ones((ny, 1))                              # referance friction coeff
a_user = np.ones((ny, 1))                               # direct effect
b_user = np.ones((ny, 1))                               # evolution parameter
V0_user = np.ones((ny, 1))                              # reference slip rate
```

```python
# Initializaion cell
# Do not alter

# Friction condition
Y_fault =np.zeros((ny, 1))
Y0 = 10
slip = np.zeros((ny, 1))
psi = np.zeros((ny, 1))
FaultOutput = np.zeros((ny, nt, 6)) # c
FaultOutput0 = np.zeros((ny, 6))

for j in range(0, ny):
        #Y_fault[j, 0] = j*dy

        if np.abs(j*dy-Y0) <= 1.5:
            Y_fault[j, 0] = 1.0
if fric_law not in ('SW', 'RS'):
    # Choose friction law: fric_law
    # Slip-weakening (SW)
    # Rate-and-state friction law (RS)

    print('friction law not implemented. choose fric_law = SW or fric_law = RS')
    exit(-1)

if user_params not in ('edit', 'default'):

    print('friction law not implemented. choose user_params = edit or fric_law = default')
    exit(-1)

if user_params == 'edit':
    Tau_0 = Tau_0_user                          # shear stress
    alp_s = alp_s_user                           # stastic friction
    alp_d = alp_d_user                         # dynamic friction
    D_c = D_c_user                             # critical slip
    sigma_n = sigma_n_user                        # normal stress
    L0 = L0_user                               # state evolution distance
    f0 = f0_user                             # referance friction coeff
    a = a_user                              # direct effect
    b = b_user                              # evolution parameter
    V0 = V0_user                             # reference slip rate

if fric_law  in ('SW'):
     alpha = np.ones((ny, 1))*1e1000000                      # initial friction coefficient
     slip = np.zeros((ny, 1))                        # initial slip (in m)
     slip_new = np.zeros((ny, 1))
     #Tau_0 = np.ones((ny, 1))*(70+11.6*(np.exp(-(Y_fault-7.0)**2/(2*9))))               # initial load (81.24 in MPa), slight increase will unlock the fault

     if user_params == 'default':
        Tau_0 = np.ones((ny, 1))*(70+11.6*Y_fault)
        alp_s = np.ones((ny, 1))*0.677                          # stastic friction
        alp_d = np.ones((ny, 1))*0.525                          # dynamic friction
        D_c = np.ones((ny, 1))*0.4                              # critical slip
        sigma_n = -np.ones((ny, 1))*120.0                        # normal stress

     # These are not needed for the slip weakening case
     psi = np.ones((ny, 1))*0.0                         # initial condition for the state variable in friction law
     psi_new = np.ones((ny, 1))*0.0
     L0 = np.ones((ny, 1))*1.0                               # state evolution distance
     f0 = np.ones((ny, 1))*1.0                               # referance friction coeff
     a = np.ones((ny, 1))*1.0                                # direct effect
     b = np.ones((ny, 1))*1.0                                # evolution parameter
     V0 = np.ones((ny, 1))*1.0                               # reference slip rate

     FaultOutput[:, 0, 2] = sigma_n[:,0]
     FaultOutput[:, 0, 3] = Tau_0[:,0]
     FaultOutput[:, 0, 4] = slip[:,0]
     FaultOutput[:, 0, 5] = psi[:,0]

if fric_law  in ('RS'):
    alpha = np.ones((ny, 1))*1e1000000                      # initial friction coefficient
    slip = np.ones((ny, 1))*0.0                         # initial slip (in m)
    slip_new = np.zeros((ny, 1))
    #Tau_0 = np.ones((ny, 1))*81.24+0.1*0.36                 # initial load (81.24 in MPa), slight increase will unlock the fault
    psi = np.ones((ny, 1))*0.4367                      # initial condition for the state variable in friction law
    psi_new = np.ones((ny, 1))*0.0

    if user_params == 'default':
        sigma_n = -np.ones((ny, 1))*120.0                        # background normal stress
        Tau_0 = np.ones((ny, 1))*75 #-2*0.2429*sigma_n*Y_fault
        L0 = np.ones((ny, 1))*0.02                              # state evolution distance
        f0 = np.ones((ny, 1))*0.6                               # referance friction coeff
        a = np.ones((ny, 1))*0.008                              # direct effect
        b = np.ones((ny, 1))*0.012                              # evolution parameter
        V0 = np.ones((ny, 1))*1.0e-6                            # reference slip rate

    Vin = np.ones((ny, 1))*2.0e-12
    theta = L0/V0*np.exp(((a*np.log(2.0*np.sinh(75/(a*120)))-f0-a*np.log(Vin/V0))/b))
    psi[:,0] = f0[:,0] + b[:,0]*np.log(V0[:,0]/L0[:,0]*theta[:,0])

     # These are not needed for the rate and state case
    alp_s = np.ones((ny, 1))*1.0                             # stastic friction
    alp_d = np.ones((ny, 1))*1.0                             # dynamic friction
    D_c = np.ones((ny, 1))*1.0                               # critical slip

    FaultOutput[:, 0, 2] = sigma_n[:,0]
    FaultOutput[:, 0, 3] = Tau_0[:,0]
    FaultOutput[:, 0, 4] = slip[:,0]
    FaultOutput[:, 0, 5] = psi[:,0]

friction_parameters = np.zeros((12, ny))
#friction_parameters = [alpha, alpha, Tau_0, L0, f0, a, b, V0, sigma_n, alp_s, alp_d, D_c]
#                        0         1      2     3   4  5  6   7    8       9      10  11
for j in range(0, ny):
        friction_parameters[0, j] = alpha[j, 0]
        friction_parameters[1, j] = alpha[j, 0]
        friction_parameters[2, j] = Tau_0[j, 0]
        friction_parameters[3, j] = L0[j, 0]
        friction_parameters[4, j] = f0[j, 0]
        friction_parameters[5, j] = a[j, 0]

        friction_parameters[6, j] = b[j, 0]
        friction_parameters[7, j] = V0[j, 0]
        friction_parameters[8, j] = sigma_n[j, 0]
        friction_parameters[9, j] = alp_s[j, 0]
        friction_parameters[10, j] = alp_d[j, 0]
        friction_parameters[11, j] = D_c[j, 0]
# end friction condition

# source parameters
x0 = -15.0        # [km]
y0 = 7.5         # [km]
t0 = 0.0         # [s]
T =  0.1         # [s]
M0 = 00.0      # [MPa]
M = [0, 0, 1., 1., 0]
source_type = 'Gaussian' # 'Gaussian', 'Brune'
source_parameter = [x0, y0, t0, T, M0, source_type, M]

# extract Lame parameters
mu_l = rho_l*cs_l**2
Lambda_l = rho_l*cp_l**2-2.0*mu_l

mu_r = rho_r*cs_r**2
Lambda_r = rho_r*cp_r**2-2.0*mu_r

# Model type, available are "homogeneous", "random":
model_type = "homogeneous"

# Initialize velocity model
Mat_l = np.zeros((nx, ny, 3))

Mat_r = np.zeros((nx, ny, 3))

if model_type == "homogeneous":
    Mat_l[:,:,0] += rho_l
    Mat_l[:,:,1] += Lambda_l
    Mat_l[:,:,2] += mu_l

    Mat_r[:,:,0] += rho_r
    Mat_r[:,:,1] += Lambda_r
    Mat_r[:,:,2] += mu_r

elif model_type == "random":
    pert = 0.4

    rho_pert = np.zeros((nx+nx-1, ny))
    mu_pert = np.zeros((nx+nx-1, ny))
    lambda_pert = np.zeros((nx+nx-1, ny))

    rho_pert = 2.0 * (np.random.rand(nx+nx-1, ny) - 0.5) * pert
    mu_pert = 2.0 * (np.random.rand(nx+nx-1, ny) - 0.5) * pert
    lambda_pert = 2.0 * (np.random.rand(nx+nx-1, ny) - 0.5) * pert

    r_rho_l = rho_pert[0:nx, :]
    r_mu_l =  mu_pert[0:nx, :]
    r_lambda_l =  lambda_pert[0:nx, :]

    Mat_l[:,:,0] += rho_l*(1.0 + r_rho_l)
    Mat_l[:,:,1] += Lambda_l*(1.0 + r_lambda_l)
    Mat_l[:,:,2] += mu_l*(1.0 + r_mu_l)

    r_rho_r = rho_pert[nx-1:2*(nx-1)+1, :]
    r_mu_r =  mu_pert[nx-1:2*(nx-1)+1, :]
    r_lambda_r =  lambda_pert[nx-1:2*(nx-1)+1, :]

    Mat_r[:,:,0] += rho_r*(1.0 + r_rho_r)
    Mat_r[:,:,1] += Lambda_r*(1.0 + r_lambda_r)
    Mat_r[:,:,2] += mu_r*(1.0 + r_mu_r)

# Initialize pressure at different time steps and the second
# derivatives in each direction
F_l = np.zeros((nx, ny, nf))
Fnew_l = np.zeros((nx, ny, nf))
X_l = np.zeros((nx, ny))
Y_l = np.zeros((nx, ny))
p_l = np.zeros((nx, ny))
v = 2.0
F_r = np.zeros((nx, ny, nf))
Fnew_r = np.zeros((nx, ny, nf))
X_r = np.zeros((nx, ny))
Y_r = np.zeros((nx, ny))
p_r = np.zeros((nx, ny))

for i in range(0, nx):
    for j in range(0, ny):
        X_l[i,j] = -Lx + i*dx
        Y_l[i,j] = j*dy

        X_r[i,j] = i*dx
        Y_r[i,j] = j*dy

# Set up seismograms
# Receiver locations left
rx_l = np.array([0, 0])
ry_l = np.array([0, Y0])

irx_l = np.array([1, 1])
iry_l = np.array([0, 0])

for i in range(len(rx_l)):
    irx_l[i] = (np.round(rx_l[i]/dx))+(nx-1)
    iry_l[i] = (np.round(ry_l[i]/dy))

seisvx_l = np.zeros((len(irx_l), nt))
seisvy_l = np.zeros((len(irx_l), nt))

# Receiver locations right

rx_r = np.array([0, 0])
ry_r = np.array([0, Y0])

irx_r = np.array([1, 1])
iry_r = np.array([0, 0])

for i in range(len(rx_r)):
    irx_r[i] = (np.round(rx_r[i]/dx))
    iry_r[i] = (np.round(ry_r[i]/dy))

seisvx_r = np.zeros((len(irx_r), nt))
seisvy_r = np.zeros((len(irx_r), nt))

# Boundary reflection coefficients: 0<= r[j] <= 1
r_l = np.array([0.,0.,1.,0.])
r_r = np.array([0.,0.,1.,0.])

# required for seismograms
ir_l = np.arange(len(irx_l))
ir_r = np.arange(len(irx_r))

print('The final model time will be '+str(nt*dt)+' seconds')
```

    The final model time will be 3.406466512702079 seconds

```python
# Plotting cell

# create plot
image_r = plt.imshow(np.squeeze(np.append([p_l.transpose()],[p_r.transpose()],axis=2)), aspect='auto',extent=[-Lx,Lx,Ly,0]
          ,cmap = 'seismic', vmin=-v, vmax=+v, animated=True,
          interpolation='none')

# Plot the receivers
for x, y in zip(rx_l, ry_l):
    plt.text(x, y, '+')

for x, y in zip(rx_r, ry_r):
    plt.text(x, y, '+')

plt.text(x0, y0, 'o')
plt.colorbar()
plt.xlabel('x')
plt.ylabel('y')
plt.show()

# Time-stepping
for it in range(nt):

    t = it*dt

    #4th order Runge-Kutta
    RK4_2D.elastic_RK4_2D(Fnew_l, F_l, Mat_l, X_l, Y_l, t, nf, nx, ny, dx, dy, dt, order, r_l, source_parameter, Fnew_r, F_r, Mat_r, X_r, Y_r, r_r, friction_parameters, slip,  psi, slip_new, psi_new, fric_law, FaultOutput0, Y0, mode)

    # update fields and extract parameters
    F_l = Fnew_l
    F_r = Fnew_r
    slip = slip_new
    psi = psi_new
    FaultOutput[:, it, :] = FaultOutput0
    if mode == 'II':
        p_l = F_l[:,:,1]
        p_r = F_r[:,:,1]
    if mode == 'III':
        p_l = F_l[:,:,0]
        p_r = F_r[:,:,0]

    t = it*dt

    # Plot every isnap-th iteration
    if it % isnap == 0:                    # you can change the speed of the plot by increasing the plotting interval
        p_b = np.squeeze(np.append([p_l.transpose()],[p_r.transpose()],axis=2)) # p for both, transposed to plot
        plt.title("time: %.2f" % t)
        plt.axvline(x=0.0)
        image_r.set_data(p_b)
        plt.gcf().canvas.draw()

    # Save seismograms
    if mode == 'II':
        seisvy_l[ir_l, it] = F_l[irx_l[ir_l], iry_l[ir_l], 1]
        seisvy_r[ir_r, it] = F_r[irx_r[ir_r], iry_r[ir_r], 1]

    if mode == 'III':
        seisvy_l[ir_l, it] = F_l[irx_l[ir_l], iry_l[ir_l], 0]
        seisvy_r[ir_r, it] = F_r[irx_r[ir_r], iry_r[ir_r], 0]

# Plot seismogram
plt.ioff()
plt.figure(figsize=(10, 10))

plt.subplot(2,1,1)
ymax = seisvx_l.ravel().max()
time = np.arange(nt) * dt
for ir_l in range(len(seisvx_l)):
    plt.plot(time, seisvy_l[ir_l, :] + ymax * ir_l)
    plt.title('Left block')
    plt.xlabel('Time (s)')
    plt.ylabel('vy (m/s)')

plt.subplot(2,1,2)
ymax = seisvy_l.ravel().max()
for ir_l in range(len(seisvy_r)):
    plt.plot(time, seisvy_r[ir_l, :] + ymax * ir_l)
    plt.title('Right block')
    plt.xlabel('Time (s)')
    plt.ylabel('vy (m/s)')

plt.show()
```

    <IPython.core.display.Javascript object>

    <IPython.core.display.Javascript object>

```python
# View slip, slip rate and traction.

fig1 = plt.figure(figsize=(10,10))
ax3 = fig1.add_subplot(3,1,1)
line3 = ax3.plot(0,0,'g')
plt.title('Slip')
plt.xlabel('Depth [km]')
plt.ylabel('Slip [m]')

ax4 = fig1.add_subplot(3,1,2)
line4 = ax4.plot(0,0,'g')
plt.title('Slip rate')
plt.xlabel('Depth [km]')
plt.ylabel('Slip rate[m/s]]')

ax5 = fig1.add_subplot(3,1,3)
line5 = ax5.plot(0,0,'g')
plt.title('Traction')
plt.xlabel('Depth [km]')
plt.ylabel('stress [MPa]')

plt.tight_layout()
plt.ion()
plt.show()

y_fault = Y_l[-1,:]

for it in range(nt):
    slip_ = FaultOutput[:, it, 4]
    sliprate_ = np.sqrt(FaultOutput[:, it, 0]**2 + FaultOutput[:, it, 1]**2)
    traction_ = FaultOutput[:, it, 3]
    if it % 1 == 0:

        for l in line3:
            l.remove()
            del l
        for l in line4:
            l.remove()
            del l
        for l in line5:
            l.remove()
            del l

        # Display lines
        line3 = ax3.plot(y_fault,slip_,'g')
        line4 = ax4.plot(y_fault,sliprate_,'g')
        line5 = ax5.plot(y_fault,traction_,'g')
        plt.gcf().canvas.draw()

plt.ioff()
plt.show()
```

    <IPython.core.display.Javascript object>

```python
#Plot time history of the slip-rate on the fault

VT = np.zeros((nt, ny))

for it in range(nt):
    for j in range(ny):

        VT[it, j] = np.sqrt(FaultOutput[j, it, 0]**2 + FaultOutput[j, it, 1]**2)

v = 2.5
image_f = plt.imshow(VT, aspect='auto',extent=[0,Ly, nt*dt, 0]
          ,cmap = 'viridis', vmin=0, vmax=+v, animated=True, interpolation='none')

plt.title('slip-rate')
plt.colorbar()
plt.xlabel('fault [km]')
plt.ylabel('t [s]')
plt.show()
```

    <IPython.core.display.Javascript object>


# 拉普拉斯算子

理解图的拉普拉斯矩阵，要从微积分中的拉普拉斯算子说起。多元函数 $f(x_1, \ldots, x_n)$的拉普拉斯算子是所有自变量的非混合二阶偏导数之和
\begin{equation}
\Delta {f} = \sum_{i = 1}^{n} \frac{\partial^2 f}{\partial x^2_i}
\end{equation}

例如对于三元函数 $f(x, y, z)$, 其拉普拉斯算子为:
\begin{equation}
\Delta f = \frac{\partial^2 f}{\partial x^2} + \frac{\partial^2 f}{\partial y^2}  + \frac{\partial^2 f}{\partial z^2}
\end{equation}

很多时候我们之只能近似计算导数值，称为数值微分。如果$\Delta_x$的值接近于0，则在$x$点处的导数可以用下面的公式近似计算:
\begin{equation}
f^{'}(x) \approx \frac{f(x + \Delta_x) - f(x)}{\Delta_x}
\end{equation}
对于二阶导数，有
\begin{equation}
\begin{aligned}
f^{''}(x) &\approx \frac{f'(x) - f'(x - \Delta_x)}{\Delta_x} \\
& = \frac{\frac{f(x+\Delta_x) - f(x)}{\Delta_x} - \frac{f(x) - f(x - \Delta_x)}{\Delta_x}}{\Delta_x} \\
&= \frac{f(x+\Delta_x) - 2f(x) + f(x-\Delta_x)}{(\Delta_x)^2}
\end{aligned}
\end{equation}

下面考虑多元函数的偏导数。对于二元函数$f(x, y)$ ，其拉普拉斯算子可以用下面的公式近似计算:
\begin{equation}
\begin{aligned}
\Delta f &= \frac{\partial^2 f}{\partial x^2} + \frac{\partial^2 f}{\partial y^2} \\
&= \frac{f(x+\Delta_x, y) - 2f(x, y) + f(x-\Delta_x, y)}{(\Delta_x)^2} + \frac{f(x, y+\Delta_y) - 2f(x, y) + f(x, y-\Delta_y)}{(\Delta_y)^2}
\end{aligned}
\end{equation}

如果上面的二元函数$f(x, y)$进行离散化，对自变量$x, y$的一系列点处的函数值进行采样，得到下面一系列点处的函数值，构成一个矩阵
\begin{equation}
\begin{bmatrix}
f(x_1, y_1) & f(x_2, y_1) & \cdots & f(x_n, y_1) \\
f(x_1, y_2) & f(x_2, y_2) & \cdots & f(x_n, y_2) \\
\vdots  & \vdots  & \ddots & \vdots  \\
f(x_1, y_n) & f(x_2, y_n) & \cdots & f(x_n, y_n) \\
\end{bmatrix}
\end{equation}
在这里$x$为水平方向, $y$为垂直方向。为了简化，假设 $x$ 和 $y$ 的增量即步长全为1，即
\begin{equation}
\Delta_x = x_{i+1} - x_i = 1, \;\; \Delta_y = y_{i+1} - y_i = 1
\end{equation}
则点$(x_i, y_i)$处的拉普拉斯算子可以用下面的公式近似计算:
\begin{equation}
\begin{aligned}
\Delta f (x_i, y_i) &\approx \frac{f(x_i+\Delta_x, y_i) - 2f(x_i, y_i) + f(x_i-\Delta_x, y_i)}{(\Delta_x)^2} + \frac{f(x_i, y_i+\Delta_y) - 2f(x_i, y_i) + f(x_i, y_i-\Delta_y)}{(\Delta_y)^2} \\
&=\frac{f(x_{i+1}, y_i) - 2f(x_i, y_i) + f(x_{i-1}, y_i)}{(1)^2} + \frac{f(x_i, y_{i+1}) - 2f(x_i, y_i) + f(x_i, y_{i-1})}{(1)^2} \\
&= f(x_{i+1}, y_i) - 2f(x_i, y_i) + f(x_{i-1}, y_i) + f(x_i, y_{i+1}) - 2f(x_i, y_i) + f(x_i, y_{i-1}) \\
&= f(x_{i+1}, y_i) + f(x_{i-1}, y_i) + f(x_i, y_{i+1}) + f(x_i, y_{i-1}) - 4f(x_i, y_i) \\
&= (f(x_{i+1}, y_i) - f(x_i, y_i)) + (f(x_{i-1}, y_i) - f(x_i, y_i)) + (f(x_i, y_{i+1}) - f(x_i, y_i)) + (f(x_i, y_{i-1}) - f(x_i, y_i)) \\
&= \sum_{(k,l) \in N(i, j)}(f(x_k, y_l) - f(x_i, y_i))
\end{aligned}
\end{equation}
其中 N(i, j) 是(x_i, y_i)邻居节点的集合. 这是一个非常优美的结果，它就是$(x_i, y_i)$的4个相邻点处的函数值之和与$(x_i, y_i)$点处的函数值乘以4后的差值。如下图所示:

# 图的邻接矩阵与加权度矩阵

图是一种几何结构，对它的研究起源于古老的哥尼斯堡七桥问题。一个图 $G$ 由顶点和边构成，通常将顶点的集合记为 $V$，边的集合记为 $E$ 。边由其连接的起点和终点表示。下面是一个典型的图

图的边可以是有方向的，也可以是没有方向的，前者称为有向图，后者称为无向图。

## 邻接矩阵
邻接矩阵是图的矩阵表示，借助它可以方便地存储图的结构，用线性代数的方法研究图的问题。如果一个图有 $n$ 个顶点，其邻接矩阵 $W$为 $n \times n$的矩阵，矩阵元素 $w_{ij}$ 表示边 $e_{ij}$ 的权重。如果两个顶点之间没有边连接，则在邻接矩阵中对应的元素为0。对与上面的无向图，它的邻接矩阵为

- 无向图的邻接矩阵为对称矩阵。
- 对于无向图，顶点的加权度是与该顶点相关的所有边的权重之和。如果无向图的邻接矩阵为 $W$ ，则顶点 $i$ 的加权度为邻接矩阵第 $i$ 行元素之和: \begin{equation}
d_i = \sum^n_{j = 1} w_{ij}
\end{equation}

加权度矩阵 $D$ 是一个对角矩阵，其主对角线元素为每个顶点的加权度，其他位置的元素为0

\begin{equation}
d_{ii} = d_i = \sum^n_{j = 1} w_{ij}
\end{equation}
对于上面的无向图，它的加权度矩阵为

# 拉普拉斯矩阵

在前面二元函数的例子种，一个点只与上下左右4个采样点相邻。图的顶点的连接关系可以是任意的，下面将拉普拉斯算子推广到图。如果将图的顶点处的值看作是函数值，则在顶点 $i$ 处的拉普拉斯算子为

\begin{equation}
\Delta{f_{i}} = -\sum_{j \in N_i}(f_i - f_j)
\end{equation}

其中 $N_i$ 是顶点 $i$ 的所有邻居顶点的集合。这里我们调换了 $f_i$ 和 $f_j$ 的位置，和之前的拉普拉斯算子相比，相当于多了一个负号。

由于图的边可以带有权重，我们可以在上面的计算公式中加上权重:
\begin{equation}
\Delta{f_{i}} = -\sum_{j \in N_i}w_{ij}*(f_i - f_j)
\end{equation}

这以推广如下图所示，图中红色的顶点是 $i$ ，蓝色的顶点是它的邻居顶点，灰色的顶点是其他顶点。

如果 $j$ 不是 $i$ 的邻居，则 $w_{ij} = 0$ 。因此上面的式子也可以写成:
\begin{equation}
\begin{aligned}
\Delta{f_{i}} &= -\sum_{j \in V}w_{ij}*(f_i - f_j) \\
&= -(\sum_{j \in V}w_{ij}*f_i - \sum_{j \in V}w_{ij}* f_j) \\
&= -(d_if_i -w_if)
\end{aligned}
\end{equation}
这里的 $d_i$ 就是顶点 $i$ 的加权度， $w_i$ 邻接矩阵的第 $i$ 行， $f$ 是所有顶点的值构成的里列向量， $w_if$ 是二者的内积。

对于图的所有顶点，有:
\begin{equation}
\Delta f = \begin{bmatrix}
\Delta f_1\\
\ldots \\
\Delta f_n
\end{bmatrix} = \begin{bmatrix}
d_1f_1 - w_1f\\
\ldots \\
d_nf_n - w_nf
\end{bmatrix} = \begin{bmatrix}
d_1 & \ldots & \ldots \\
\ldots & \ldots & \ldots \\
\ldots & \ldots & d_n
\end{bmatrix} \begin{bmatrix}
f_1 \\
\ldots \\
f_n
\end{bmatrix} - \begin{bmatrix}
w_1 \\
\ldots \\
w_n
\end{bmatrix} \begin{bmatrix}
f_1 \\
\ldots \\
f_n
\end{bmatrix} = (D-W)f
\end{equation}

上面的结论启发我们，可以在邻接矩阵($W$)和加权度矩阵($D$)的基础上定义拉普拉斯矩阵。

假设无向图 $G$ 有 $n$ 个顶点，邻接矩阵为 $W$ ，加权度矩阵为 $D$ 。拉普拉斯矩阵定义为加权度矩阵与邻接矩阵之差:
\begin{equation}
L = D - w
\end{equation}

- 由于 $w$ 和 $D$ 都是对称矩阵，因此拉普拉斯矩阵也是对称矩阵。
- 显然拉普拉斯矩阵每一行元素之和都为0
- 根据前面的介绍，拉普拉斯矩阵实际代表了图的二阶导数。

以上面的图为例，它的拉普拉斯矩阵为:

下面介绍拉普拉斯矩阵的若干重要性质。

1. 对任意向量 $f \in \mathbb{R}^n$, 有: $f^T L f = \frac{1}{2} \sum^n_{i=1} \sum^n_{j=1} w_{ij} (f_i - f_j)^2$
2. 拉普拉斯矩阵是对称半正定矩阵
3. 拉普拉斯矩阵的最小特征值为0，其对应的特征向量为常向量 1 ，即所有分量为 1
4. 拉普拉斯矩阵有 $n$ 个非负实数特征值，并且满足: $0 = \lambda_1 \leq \lambda_2 \leq \ldots \leq \lambda_n$
下面进行证明。根据加权度 $d_i$ 的定义，有:

因此结论（1）成立。根据结论（1），对任意非0向量 $f$ ，有 $f^T L f = \frac{1}{2} \sum^n_{i=1} \sum^n_{j=1} w_{ij} (f_i - f_j)^2 \geq 0$

## 归一化拉普拉斯矩阵

对前面定义的拉普拉斯矩阵进行归一化从而得到归一化的拉普拉斯矩阵。

1. 第一种称为对称归一化，定义为

\begin{equation}
L_{sym} = D^{-1/2} L D^{-1/2} = I - D^{-1/2} W  D^{-1/2}
\end{equation}

2. 第二种称为随机漫步归一化，定义为

# Reference

1. [理解图的拉普拉斯矩阵](https://zhuanlan.zhihu.com/p/362416124)

```python

```


```python
# autoreload nangs
%reload_ext autoreload
%autoreload 2
%matplotlib inline
```

    Bad key "text.kerning_factor" on line 4 in
    C:\Users\sensio\miniconda3\lib\site-packages\matplotlib\mpl-data\stylelib\_classic_test_patch.mplstyle.
    You probably need to get an updated matplotlibrc file from
    https://github.com/matplotlib/matplotlib/blob/v3.1.3/matplotlibrc.template
    or from the matplotlib source distribution

## Smith-Hutton Problem

We want to solve the following PDE:

\begin{equation}
    u \frac{\partial \phi}{\partial x} + v \frac{\partial \phi}{\partial y} = 0
\end{equation}

The independen variables (i.e, $x$, $y$) are used as input values for the NN, and the solution (i.e. $\phi(x,y)$) is the output. In order to find the solution, at each step the NN outputs are derived w.r.t the inputs. Then, a loss function that matches the PDE is built and the weights are updated accordingly. If the loss function goes to zero, we can assume that our NN is indeed the solution to our PDE. We will try to find a general solution for different values of $u$, so it will be set also as an input. The geometry of the problem is as follows:

```python
#imports

import numpy as np
import matplotlib.pyplot as plt
import torch
import nangs

device = "cuda" if torch.cuda.is_available() else "cpu"

nangs.__version__, torch.__version__
```

    ('0.1.2', '1.5.0')

```python
from nangs import PDE

# velocity field
vel = lambda x, y: (2*y*(1-x**2), -2*x*(1-y**2))

class SH(PDE):
    def computePDELoss(self, inputs, outputs):

        # compute gradients
        grads = self.computeGrads(outputs, inputs)

        # compute loss
        dpdx, dpdy = grads[:, 0], grads[:, 1]
        x, y = inputs[:, 0], inputs[:, 1]
        u, v = vel(x, y)
        return {'pde': u*dpdx + v*dpdy}

# instanciate pde
pde = SH(inputs=('x', 'y'), outputs='p')
```

```python
# define the mesh

from nangs import Mesh

x = np.linspace(-1,1,80)
y = np.linspace(0,1,40)

mesh = Mesh({'x': x, 'y': y}, device=device)
pde.set_mesh(mesh)
```

```python
# boundary conditions

from nangs import Dirichlet

ALPHA = 10

# left and rigth b.c

x1 = np.array([-1, 1])
p = np.full(2*len(y), 1. - np.tanh(ALPHA))
boco = Dirichlet({'x': x1, 'y': y}, {'p': p}, name='left_rigth', device=device)
pde.add_boco(boco)
```

```python
# top b.c

p = np.full(len(x), 1. - np.tanh(ALPHA))
boco = Dirichlet({'x': x, 'y': y[-1:]}, {'p': p}, device=device, name='top')
pde.add_boco(boco)
```

```python
# bottom b.c

x1, x2 = x[x<0], x[x>=0]
p1 = 1. + np.tanh(ALPHA*(2*x1+1))

# bottom left b.c

boco = Dirichlet({'x': x1, 'y': y[:1]}, {'p': p1}, name='bottom_left', device=device)
pde.add_boco(boco)
```

```python
plt.plot(x1,p1)
plt.xlabel('x')
plt.ylabel('p      ', rotation=np.pi/2)
plt.title('Solution at y=0')
plt.grid()
plt.show()
```

```python
# bottom right b.c

from nangs import Neumann

class MyNeumann(Neumann):
    def computeBocoLoss(self, inputs, outputs):
        dpdy = self.computeGrads(outputs, inputs)[:, 1]
        return {'gradY': dpdy}

boco = MyNeumann({'x': x2, 'y': y[:1]}, name='bottom_right', device=device)
pde.add_boco(boco)
```

```python
from nangs import MLP

BATCH_SIZE = 1024
LR = 5e-3
EPOCHS = 100
NUM_LAYERS = 5
NUM_HIDDEN = 256

mlp = MLP(len(pde.inputs), len(pde.outputs), NUM_LAYERS, NUM_HIDDEN).to(device)
optimizer = torch.optim.Adam(mlp.parameters())
scheduler = torch.optim.lr_scheduler.OneCycleLR(optimizer, max_lr=LR, pct_start=0.1, total_steps=EPOCHS)

pde.compile(mlp, optimizer, scheduler)
%time hist = pde.solve(EPOCHS, BATCH_SIZE)
```

```python
# evaluate the solution

x = np.linspace(-1,1,100)
y = np.linspace(0,1,50)
eval_mesh = Mesh({'x': x, 'y': y}, device=device)

p = pde.eval(eval_mesh).cpu().numpy()

plt.imshow(p.reshape((len(y),len(x))), vmin=p.min(), vmax=p.max(), origin='lower',
           extent=[x.min(), x.max(), y.min(), y.max()])
plt.colorbar()
plt.show()
```

```python
x = np.linspace(-1,1,200)
y = np.array([0])
eval_mesh = Mesh({'x': x, 'y': y}, device=device)
p = pde.eval(eval_mesh).cpu().numpy()

plt.plot(x1,p1)
plt.plot(x1+1,np.flip(p1), label="exact")
plt.plot(x, p, '.k', label="predicted")
plt.legend()
plt.xlabel('x')
plt.ylabel('p      ', rotation=np.pi/2)
plt.title('Solution at y=0')
plt.grid()
plt.show()
```