Page MenuHomec4science

No OneTemporary

File Metadata

Created
Tue, Mar 11, 20:23
diff --git a/Contrastive Loss/CM/CM_Training_loss.npy b/Contrastive Loss/CM/CM_Training_loss.npy
new file mode 100644
index 0000000..78474b1
Binary files /dev/null and b/Contrastive Loss/CM/CM_Training_loss.npy differ
diff --git a/Contrastive Loss/CM/CM_Training_loss_mean.npy b/Contrastive Loss/CM/CM_Training_loss_mean.npy
new file mode 100644
index 0000000..c399e39
Binary files /dev/null and b/Contrastive Loss/CM/CM_Training_loss_mean.npy differ
diff --git a/Contrastive Loss/CM/CM_Training_loss_std.npy b/Contrastive Loss/CM/CM_Training_loss_std.npy
new file mode 100644
index 0000000..2869e19
Binary files /dev/null and b/Contrastive Loss/CM/CM_Training_loss_std.npy differ
diff --git a/Contrastive Loss/CM/CM_data_Visualize.png b/Contrastive Loss/CM/CM_data_Visualize.png
new file mode 100644
index 0000000..3e1b9a8
Binary files /dev/null and b/Contrastive Loss/CM/CM_data_Visualize.png differ
diff --git a/Contrastive Loss/CM/CM_test_embeddings_.npy b/Contrastive Loss/CM/CM_test_embeddings_.npy
new file mode 100644
index 0000000..834b5be
Binary files /dev/null and b/Contrastive Loss/CM/CM_test_embeddings_.npy differ
diff --git a/Contrastive Loss/CM/CM_test_labels_.npy b/Contrastive Loss/CM/CM_test_labels_.npy
new file mode 100644
index 0000000..6b93673
Binary files /dev/null and b/Contrastive Loss/CM/CM_test_labels_.npy differ
diff --git a/Contrastive Loss/CM/CM_train_embeddings_.npy b/Contrastive Loss/CM/CM_train_embeddings_.npy
new file mode 100644
index 0000000..45af9a3
Binary files /dev/null and b/Contrastive Loss/CM/CM_train_embeddings_.npy differ
diff --git a/Contrastive Loss/CM/CM_train_labels_.npy b/Contrastive Loss/CM/CM_train_labels_.npy
new file mode 100644
index 0000000..08516fd
Binary files /dev/null and b/Contrastive Loss/CM/CM_train_labels_.npy differ
diff --git a/Contrastive Loss/CM/CM_trained_model.pth b/Contrastive Loss/CM/CM_trained_model.pth
new file mode 100644
index 0000000..f3feb2e
Binary files /dev/null and b/Contrastive Loss/CM/CM_trained_model.pth differ
diff --git a/Contrastive Loss/CM/LR_Confusion_Matrix_No_Opt.png b/Contrastive Loss/CM/LR_Confusion_Matrix_No_Opt.png
new file mode 100644
index 0000000..b0188a4
Binary files /dev/null and b/Contrastive Loss/CM/LR_Confusion_Matrix_No_Opt.png differ
diff --git a/Contrastive Loss/CM/LR_Confusion_Matrix_Opt.png b/Contrastive Loss/CM/LR_Confusion_Matrix_Opt.png
new file mode 100644
index 0000000..31523d3
Binary files /dev/null and b/Contrastive Loss/CM/LR_Confusion_Matrix_Opt.png differ
diff --git a/Contrastive Loss/CM/LR_model.sav b/Contrastive Loss/CM/LR_model.sav
new file mode 100644
index 0000000..d03a0ed
Binary files /dev/null and b/Contrastive Loss/CM/LR_model.sav differ
diff --git a/Contrastive Loss/CM/Testing_Feature_2D.png b/Contrastive Loss/CM/Testing_Feature_2D.png
new file mode 100644
index 0000000..94eb6fe
Binary files /dev/null and b/Contrastive Loss/CM/Testing_Feature_2D.png differ
diff --git a/Contrastive Loss/CM/Testing_Feature_3D.gif b/Contrastive Loss/CM/Testing_Feature_3D.gif
new file mode 100644
index 0000000..4b84c79
Binary files /dev/null and b/Contrastive Loss/CM/Testing_Feature_3D.gif differ
diff --git a/Contrastive Loss/CM/Testing_Feature_3D.png b/Contrastive Loss/CM/Testing_Feature_3D.png
new file mode 100644
index 0000000..774d30a
Binary files /dev/null and b/Contrastive Loss/CM/Testing_Feature_3D.png differ
diff --git a/Contrastive Loss/CM/Training loss average.png b/Contrastive Loss/CM/Training loss average.png
new file mode 100644
index 0000000..35094d0
Binary files /dev/null and b/Contrastive Loss/CM/Training loss average.png differ
diff --git a/Contrastive Loss/CM/Training loss.png b/Contrastive Loss/CM/Training loss.png
new file mode 100644
index 0000000..6d4a655
Binary files /dev/null and b/Contrastive Loss/CM/Training loss.png differ
diff --git a/Contrastive Loss/CM/Training_Feature_2D.png b/Contrastive Loss/CM/Training_Feature_2D.png
new file mode 100644
index 0000000..a30c365
Binary files /dev/null and b/Contrastive Loss/CM/Training_Feature_2D.png differ
diff --git a/Contrastive Loss/CM/Training_Feature_3D.gif b/Contrastive Loss/CM/Training_Feature_3D.gif
new file mode 100644
index 0000000..8575ead
Binary files /dev/null and b/Contrastive Loss/CM/Training_Feature_3D.gif differ
diff --git a/Contrastive Loss/CM/Training_Feature_3D.png b/Contrastive Loss/CM/Training_Feature_3D.png
new file mode 100644
index 0000000..7095d7e
Binary files /dev/null and b/Contrastive Loss/CM/Training_Feature_3D.png differ
diff --git a/Contrastive Loss/Classifier.py b/Contrastive Loss/Classifier.py
index 60a34af..4545059 100644
--- a/Contrastive Loss/Classifier.py
+++ b/Contrastive Loss/Classifier.py
@@ -1,113 +1,127 @@
# -*- coding: utf-8 -*-
"""
@author: srpv
contact: vigneashwara.solairajapandiyan@empa.ch, vigneashpandiyan@gmail.com
The codes in this following script will be used for the publication of the following work
-"Acoustic emission signature of martensitic phase transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction "
+"Acoustic emission signature of martensitic transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction"
@any reuse of this code should be authorized by the first owner, code author
"""
# libraries to import
import numpy as np
+import pandas as pd
import matplotlib.pyplot as plt
+from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import confusion_matrix
from sklearn.metrics import classification_report, confusion_matrix
+import itertools
import os
from sklearn import metrics
+# import pydot
import collections
+# import pydotplus
from sklearn.metrics import ConfusionMatrixDisplay
+from sklearn.model_selection import RandomizedSearchCV
+from sklearn.feature_selection import SelectFromModel
import joblib
+from sklearn.model_selection import cross_val_score
+from IPython.display import Image
+from sklearn.preprocessing import StandardScaler
+from sklearn.model_selection import train_test_split # implementing train-test-split
+from sklearn.neural_network import MLPClassifier
+from sklearn.decomposition import PCA
+from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
import os
-
+# from Plots import *
# %%
def classifier_linear(Exptype, folder_created):
"""
This function performs linear classification using logistic regression.
Parameters:
Exptype (str): The type of experiment.
folder_created (str): The path to the folder where the embeddings and labels are stored.
Returns:
None
"""
train_embeddings = Exptype+'_train_embeddings'+'_' + '.npy'
train_embeddings = os.path.join(folder_created, train_embeddings)
train_labelsname = Exptype+'_train_labels'+'_'+'.npy'
train_labelsname = os.path.join(folder_created, train_labelsname)
test_embeddings = Exptype+'_test_embeddings'+'_' + '.npy'
test_embeddings = os.path.join(folder_created, test_embeddings)
test_labelsname = Exptype+'_test_labels'+'_'+'.npy'
test_labelsname = os.path.join(folder_created, test_labelsname)
X_train = np.load(train_embeddings).astype(np.float64)
y_train = np.load(train_labelsname).astype(np.float64)
X_test = np.load(test_embeddings).astype(np.float64)
y_test = np.load(test_labelsname).astype(np.float64)
y_pred_prob, pred_prob = LR(X_train, X_test, y_train, y_test, folder_created)
def LR(X_train, X_test, y_train, y_test, folder_created):
"""
Logistic Regression classifier.
Args:
X_train (array-like): Training data features.
X_test (array-like): Test data features.
y_train (array-like): Training data labels.
y_test (array-like): Test data labels.
folder_created (str): Path to the folder where the output files will be saved.
Returns:
tuple: A tuple containing the predicted probabilities and predicted labels.
"""
model = LogisticRegression(max_iter=1000, random_state=123)
model.fit(X_train, y_train)
predictions = model.predict(X_test)
pred_prob = model.predict_proba(X_test)
y_pred_prob = np.vstack((y_test, predictions)).transpose()
y_pred_prob = np.hstack((y_pred_prob, pred_prob))
print("LogisticRegression Accuracy:", metrics.accuracy_score(y_test, predictions))
print(classification_report(y_test, predictions))
print(confusion_matrix(y_test, predictions))
graph_name1 = 'LR'+'_without normalization w/o Opt'
graph_name2 = 'Logistic Regression'
graph_1 = 'LR_Confusion_Matrix_No_Opt.png'
graph_2 = 'LR_Confusion_Matrix_Opt.png'
titles_options = [(graph_name1, None, graph_1),
(graph_name2, 'true', graph_2)]
for title, normalize, graphname in titles_options:
plt.figure(figsize=(20, 10), dpi=400)
disp = ConfusionMatrixDisplay.from_estimator(model, X_test, y_test,
display_labels=[
'Ti64', 'Ti64_3Fe', 'Ti64_6Fe'],
cmap=plt.cm.Reds, xticks_rotation='vertical',
normalize=normalize)
plt.title(title, size=12)
plt.savefig(os.path.join(folder_created, graphname), bbox_inches='tight', dpi=400)
savemodel = os.path.join(folder_created, 'LR_model.sav')
joblib.dump(model, savemodel)
return y_pred_prob, pred_prob
diff --git a/Contrastive Loss/Dataloader.py b/Contrastive Loss/Dataloader.py
index 8beb816..61021ab 100644
--- a/Contrastive Loss/Dataloader.py
+++ b/Contrastive Loss/Dataloader.py
@@ -1,276 +1,277 @@
# -*- coding: utf-8 -*-
"""
@author: srpv
contact: vigneashwara.solairajapandiyan@empa.ch, vigneashpandiyan@gmail.com
The codes in this following script will be used for the publication of the following work
-"Acoustic emission signature of martensitic phase transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction "
+"Acoustic emission signature of martensitic transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction"
@any reuse of this code should be authorized by the first owner, code author
"""
# libraries to import
from torch.utils.data import DataLoader, Dataset
import numpy as np
import random
import os
import pandas as pd
from torchvision import transforms
from sklearn.model_selection import train_test_split # implementing train-test-split
import matplotlib.pyplot as plt
class contraster_dataset(Dataset):
"""
Dataset class for contrastive loss.
Args:
df (pandas.DataFrame): The input dataframe containing image data and labels.
train (bool): Indicates whether the dataset is for training or not.
transform (callable, optional): Optional transform to be applied on a sample.
Returns:
tuple: A tuple containing the anchor image, positive image, anchor label, and label.
- anchor_img (numpy.ndarray): The anchor image.
- positive_img (numpy.ndarray): The positive image.
- anchor_label (int): The anchor label.
- label (int): The label.
"""
def __init__(self, df, train, transform=None):
self.is_train = train
self.transform = transform
# self.to_pil = transforms.ToPILImage()
if self.is_train:
self.images = df.iloc[:, 1:].values.astype(np.uint8)
self.labels = df.iloc[:, 0].values
self.index = df.index.values
else:
self.images = df.iloc[:, 1:].values.astype(np.uint8)
self.labels = df.iloc[:, 0].values
self.index = df.index.values
def __len__(self):
return len(self.images)
def __getitem__(self, item):
#anchor_img = self.images[item].reshape(28, 28, 1)
anchor_img = self.images[item]
# print(item)
should_get_same_class = random.randint(0, 1)
if self.is_train:
if should_get_same_class:
label = self.labels[item]
anchor_label = self.labels[item]
# print(anchor_label)
positive_list = self.index[self.index !=
item][self.labels[self.index != item] == anchor_label]
positive_item = random.choice(positive_list)
positive_img = self.images[positive_item]
anchor_label = 1
return anchor_img, positive_img, anchor_label, label
else:
anchor_label = self.labels[item]
label = self.labels[item]
# print(anchor_label)
negative_list = self.index[self.index !=
item][self.labels[self.index != item] != anchor_label]
negative_item = random.choice(negative_list)
#negative_img = self.images[negative_item].reshape(28, 28, 1)
negative_img = self.images[negative_item]
anchor_label = 0
return anchor_img, negative_img, anchor_label, label
else:
# if self.transform:
# anchor_img = self.transform(self.to_pil(anchor_img))
label = self.labels[item]
return anchor_img, label
def dataprocessing(df):
"""
Preprocesses the input dataframe by standardizing its values.
Args:
df (pandas.DataFrame): The input dataframe.
Returns:
pandas.DataFrame: The preprocessed dataframe with standardized values.
"""
database = df
print(database.shape)
database = database.apply(lambda x: (x - np.mean(x))/np.std(x), axis=1)
+ # anomaly_database=anomaly_database.to_numpy().astype(np.float64)
return database
def data_extract(datapath, Exptype):
"""
Extracts data from the given datapath based on the experiment type.
Parameters:
datapath (str): The path to the data directory.
Exptype (str): The experiment type.
Returns:
train_df (DataFrame): The training data as a pandas DataFrame.
test_df (DataFrame): The testing data as a pandas DataFrame.
"""
classfile = Exptype+'_Class_label.npy'
classfile = os.path.join(datapath, classfile)
rawfile = Exptype+'_Rawspace.npy'
rawfile = os.path.join(datapath, rawfile)
classspace = np.load(classfile).astype(np.int64)
rawspace = np.load(rawfile).astype(np.float64)
rawspace = pd.DataFrame(rawspace)
rawspace = dataprocessing(rawspace)
rawspace = rawspace.to_numpy()
X_train, X_test, y_train, y_test = train_test_split(
rawspace, classspace, test_size=0.20, random_state=66)
Training = np.concatenate((y_train, X_train), axis=1)
Testing = np.concatenate((y_test, X_test), axis=1)
train_df = pd.DataFrame(Training)
test_df = pd.DataFrame(Testing)
train_df.head()
return train_df, test_df
def data_plot(datapath, Exptype, folder_created):
"""
Plots the data for visualization.
Args:
datapath (str): The path to the data files.
Exptype (str): The experiment type.
folder_created (str): The path to the folder where the graph will be saved.
Returns:
None
"""
classfile = Exptype+'_Class_label.npy'
classfile = os.path.join(datapath, classfile)
rawfile = Exptype+'_Rawspace.npy'
rawfile = os.path.join(datapath, rawfile)
classspace = np.load(classfile).astype(np.int64)
rawspace = np.load(rawfile).astype(np.float64)
rawspace = pd.DataFrame(rawspace)
rawspace = dataprocessing(rawspace)
rawspace = rawspace.to_numpy()
rawspace = pd.DataFrame(rawspace)
classspace = pd.DataFrame(classspace)
data = pd.concat([rawspace, classspace], axis=1)
new_columns = list(data.columns)
new_columns[-1] = 'target'
data.columns = new_columns
data.target.value_counts()
data = data.sample(frac=1.0)
class_names = ['Ti64', 'Ti64_3Fe', 'Ti64_6Fe']
colour = ['green', 'red', 'blue', 'cyan', 'orange', 'purple']
graphname = Exptype+'_data'+'_Visualize'+'.png'
classes = data.target.unique()
classes = np.sort(classes)
fig, axs = plt.subplots(
nrows=3,
ncols=1,
sharey=False,
figsize=(8, 7),
dpi=800
)
for i, cls in enumerate(classes):
ax = axs.flat[i]
df = data[data.target == cls].drop(labels='target', axis=1).mean(axis=0).to_numpy()
plot_time_series(df, class_names[i], ax, colour[i], i)
fig.tight_layout()
plt.savefig(os.path.join(folder_created, graphname),
bbox_inches='tight', pad_inches=0.1, dpi=800)
plt.show()
plt.clf()
def plot_time_series(data, class_name, ax, colour, i, n_steps=10):
"""
Plots a time series data with rolling mean and standard deviation.
Args:
data (list or numpy array): The time series data to be plotted.
class_name (str): The name of the class.
ax (matplotlib.axes.Axes): The axes object to plot the data on.
colour (str): The color of the plot.
i (int): The index of the plot.
n_steps (int, optional): The number of steps for rolling mean and standard deviation. Defaults to 10.
Returns:
None
"""
time_series_df = pd.DataFrame(data)
smooth_path = time_series_df.rolling(n_steps).mean()
path_deviation = 3 * time_series_df.rolling(n_steps).std()
under_line = (smooth_path - path_deviation)[0]
over_line = (smooth_path + path_deviation)[0]
ax.plot(smooth_path, color=colour, linewidth=3)
ax.fill_between(
path_deviation.index,
under_line,
over_line,
alpha=.450
)
ax.set_title(class_name)
ax.set_ylim([-0.2, 0.2])
ax.set_ylabel('Amplitude (V)')
ax.set_xlabel('Window size (μs)')
def torch_loader(batch_size, train_df, test_df):
"""
Loads and returns the train and test data loaders for the contrastive loss model.
Parameters:
batch_size (int): The batch size for the data loaders.
train_df (pandas.DataFrame): The training data as a pandas DataFrame.
test_df (pandas.DataFrame): The test data as a pandas DataFrame.
Returns:
train_loader (torch.utils.data.DataLoader): The data loader for the training data.
test_loader (torch.utils.data.DataLoader): The data loader for the test data.
"""
train_ds = contraster_dataset(train_df,
train=True,
transform=transforms.Compose([
transforms.ToTensor()
]))
train_loader = DataLoader(train_ds, batch_size=batch_size, shuffle=True, num_workers=0)
test_ds = contraster_dataset(test_df,
train=False,
transform=transforms.Compose([
transforms.ToTensor()
]))
test_loader = DataLoader(test_ds, batch_size=batch_size, shuffle=False, num_workers=0)
return train_loader, test_loader
diff --git a/Contrastive Loss/KM/KM_Training_loss.npy b/Contrastive Loss/KM/KM_Training_loss.npy
new file mode 100644
index 0000000..baa3194
Binary files /dev/null and b/Contrastive Loss/KM/KM_Training_loss.npy differ
diff --git a/Contrastive Loss/KM/KM_Training_loss_mean.npy b/Contrastive Loss/KM/KM_Training_loss_mean.npy
new file mode 100644
index 0000000..7602226
Binary files /dev/null and b/Contrastive Loss/KM/KM_Training_loss_mean.npy differ
diff --git a/Contrastive Loss/KM/KM_Training_loss_std.npy b/Contrastive Loss/KM/KM_Training_loss_std.npy
new file mode 100644
index 0000000..c5e6e11
Binary files /dev/null and b/Contrastive Loss/KM/KM_Training_loss_std.npy differ
diff --git a/Contrastive Loss/KM/KM_data_Visualize.png b/Contrastive Loss/KM/KM_data_Visualize.png
new file mode 100644
index 0000000..e1b9e82
Binary files /dev/null and b/Contrastive Loss/KM/KM_data_Visualize.png differ
diff --git a/Contrastive Loss/KM/KM_test_embeddings_.npy b/Contrastive Loss/KM/KM_test_embeddings_.npy
new file mode 100644
index 0000000..7b96296
Binary files /dev/null and b/Contrastive Loss/KM/KM_test_embeddings_.npy differ
diff --git a/Contrastive Loss/KM/KM_test_labels_.npy b/Contrastive Loss/KM/KM_test_labels_.npy
new file mode 100644
index 0000000..42daa41
Binary files /dev/null and b/Contrastive Loss/KM/KM_test_labels_.npy differ
diff --git a/Contrastive Loss/KM/KM_train_embeddings_.npy b/Contrastive Loss/KM/KM_train_embeddings_.npy
new file mode 100644
index 0000000..8caf9db
Binary files /dev/null and b/Contrastive Loss/KM/KM_train_embeddings_.npy differ
diff --git a/Contrastive Loss/KM/KM_train_labels_.npy b/Contrastive Loss/KM/KM_train_labels_.npy
new file mode 100644
index 0000000..7126b38
Binary files /dev/null and b/Contrastive Loss/KM/KM_train_labels_.npy differ
diff --git a/Contrastive Loss/KM/KM_trained_model.pth b/Contrastive Loss/KM/KM_trained_model.pth
new file mode 100644
index 0000000..6af7e56
Binary files /dev/null and b/Contrastive Loss/KM/KM_trained_model.pth differ
diff --git a/Contrastive Loss/KM/LR_Confusion_Matrix_No_Opt.png b/Contrastive Loss/KM/LR_Confusion_Matrix_No_Opt.png
new file mode 100644
index 0000000..cb1833e
Binary files /dev/null and b/Contrastive Loss/KM/LR_Confusion_Matrix_No_Opt.png differ
diff --git a/Contrastive Loss/KM/LR_Confusion_Matrix_Opt.png b/Contrastive Loss/KM/LR_Confusion_Matrix_Opt.png
new file mode 100644
index 0000000..1cd4e2a
Binary files /dev/null and b/Contrastive Loss/KM/LR_Confusion_Matrix_Opt.png differ
diff --git a/Contrastive Loss/KM/LR_model.sav b/Contrastive Loss/KM/LR_model.sav
new file mode 100644
index 0000000..214c2be
Binary files /dev/null and b/Contrastive Loss/KM/LR_model.sav differ
diff --git a/Contrastive Loss/KM/Testing_Feature_2D.png b/Contrastive Loss/KM/Testing_Feature_2D.png
new file mode 100644
index 0000000..b797d87
Binary files /dev/null and b/Contrastive Loss/KM/Testing_Feature_2D.png differ
diff --git a/Contrastive Loss/KM/Testing_Feature_3D.gif b/Contrastive Loss/KM/Testing_Feature_3D.gif
new file mode 100644
index 0000000..b1cb3b5
Binary files /dev/null and b/Contrastive Loss/KM/Testing_Feature_3D.gif differ
diff --git a/Contrastive Loss/KM/Testing_Feature_3D.png b/Contrastive Loss/KM/Testing_Feature_3D.png
new file mode 100644
index 0000000..fd31474
Binary files /dev/null and b/Contrastive Loss/KM/Testing_Feature_3D.png differ
diff --git a/Contrastive Loss/KM/Training loss average.png b/Contrastive Loss/KM/Training loss average.png
new file mode 100644
index 0000000..966c8d6
Binary files /dev/null and b/Contrastive Loss/KM/Training loss average.png differ
diff --git a/Contrastive Loss/KM/Training loss.png b/Contrastive Loss/KM/Training loss.png
new file mode 100644
index 0000000..9cd3c83
Binary files /dev/null and b/Contrastive Loss/KM/Training loss.png differ
diff --git a/Contrastive Loss/KM/Training_Feature_2D.png b/Contrastive Loss/KM/Training_Feature_2D.png
new file mode 100644
index 0000000..8481127
Binary files /dev/null and b/Contrastive Loss/KM/Training_Feature_2D.png differ
diff --git a/Contrastive Loss/KM/Training_Feature_3D.gif b/Contrastive Loss/KM/Training_Feature_3D.gif
new file mode 100644
index 0000000..da5f790
Binary files /dev/null and b/Contrastive Loss/KM/Training_Feature_3D.gif differ
diff --git a/Contrastive Loss/KM/Training_Feature_3D.png b/Contrastive Loss/KM/Training_Feature_3D.png
new file mode 100644
index 0000000..07007d5
Binary files /dev/null and b/Contrastive Loss/KM/Training_Feature_3D.png differ
diff --git a/Contrastive Loss/Loss.py b/Contrastive Loss/Loss.py
index d175b5f..52bf044 100644
--- a/Contrastive Loss/Loss.py
+++ b/Contrastive Loss/Loss.py
@@ -1,45 +1,45 @@
# -*- coding: utf-8 -*-
"""
@author: srpv
contact: vigneashwara.solairajapandiyan@empa.ch, vigneashpandiyan@gmail.com
The codes in this following script will be used for the publication of the following work
-"Acoustic emission signature of martensitic phase transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction "
+"Acoustic emission signature of martensitic transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction"
@any reuse of this code should be authorized by the first owner, code author
"""
# libraries to import
import torch.nn as nn
import torch
from torch.nn import functional as F
class ContrastiveLoss(nn.Module):
"""
Contrastive loss
Takes embeddings of two samples and a target label == 1 if samples are from the same class and label == 0 otherwise
Args:
margin (float): The margin value for the contrastive loss. Default is 1.0.
Inputs:
output1 (Tensor): Embeddings of the first sample. Shape (batch_size, embedding_size)
output2 (Tensor): Embeddings of the second sample. Shape (batch_size, embedding_size)
target (Tensor): Target labels indicating whether the samples are from the same class or not. Shape (batch_size,)
Returns:
Tensor: The contrastive loss value. Shape (1,)
"""
def __init__(self, margin=1.0):
super(ContrastiveLoss, self).__init__()
self.margin = margin
self.eps = 1e-6
def forward(self, output1, output2, target):
distances = (output2 - output1).pow(2).sum(1) # squared distances
losses = 0.5 * (target.float() * distances +
(1 + -1 * target).float() * F.relu(self.margin - (distances + self.eps).sqrt()).pow(2))
return losses.mean()
diff --git a/Contrastive Loss/Main.py b/Contrastive Loss/Main.py
index 15ca029..a756424 100644
--- a/Contrastive Loss/Main.py
+++ b/Contrastive Loss/Main.py
@@ -1,124 +1,131 @@
# -*- coding: utf-8 -*-
"""
@author: srpv
contact: vigneashwara.solairajapandiyan@empa.ch, vigneashpandiyan@gmail.com
The codes in this following script will be used for the publication of the following work
-"Acoustic emission signature of martensitic phase transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction "
+"Acoustic emission signature of martensitic transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction"
@any reuse of this code should be authorized by the first owner, code author
"""
# libraries to import
+import time
import torch
import random
import numpy as np
+import pandas as pd
+import torch.nn as nn
import torch.optim as optim
from tqdm.notebook import tqdm
+import matplotlib.pyplot as plt
+from torchvision import transforms
+from torch.utils.data import DataLoader, Dataset
from torch.nn import functional as F
from Utils import *
from Network import *
from Dataloader import *
from Loss import *
from Classifier import *
from Trainer import *
+from matplotlib import animation
import os
# %%
# Clearing the cache
torch.cuda.empty_cache()
torch.manual_seed(2020)
np.random.seed(2020)
random.seed(2020)
# %%
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
if device.type == "cuda":
torch.cuda.get_device_name()
print('Using device:', device)
# %%
-datapath = r'C:\Users\vigneashwara.p\Desktop\GitC4science\lpbf-acoustic-dynamics-of-in-situ-alloying-of-titanium-fe\Data_preprocessing'
+datapath = r'C:\Users\srpv\Desktop\Git\Additive-Manufacturing-Acoustic-Dynamics-of-in-situ-alloying-of-Titanium-Fe\Data_preprocessing'
embedding_dims = 8
batch_size = 64
-epochs = 200
+epochs = 5
Datasets = ['CM', 'KM']
for Exptype in Datasets:
category = ['Ti64', 'Ti64-3Fe', 'Ti64-6Fe']
class_names = []
for x in category:
x = str(Exptype)+'-'+x
class_names.append(x)
# %%
# folder creation to store latent spaces, graphs and figure.
file = os.path.join(os.getcwd(), os.listdir(os.getcwd())[0])
total_path = os.path.dirname(file)
print(total_path)
folder_created = os.path.join(total_path, Exptype)
print(folder_created)
try:
os.makedirs(folder_created, exist_ok=True)
print("Directory created....")
except OSError as error:
print("Directory already exists....")
# %%
# Data loading and preprocessing
data_plot(datapath, Exptype, folder_created)
train_df, test_df = data_extract(datapath, Exptype)
train_loader, test_loader = torch_loader(batch_size, train_df, test_df)
# %%
# Model training
model = Network(embedding_dims)
model.apply(init_weights)
model = torch.jit.script(model).to(device)
optimizer = optim.Adam(model.parameters(), lr=0.001)
criterion = torch.jit.script(ContrastiveLoss())
model.train()
Training_loss, Training_loss_mean, Training_loss_std, running_loss, model = model_trainer(
epochs, train_loader, device, optimizer, model, criterion, Exptype, folder_created)
# %%
# Plotting the training loss
plot_function(Exptype, folder_created, Training_loss,
Training_loss_mean, Training_loss_std, running_loss)
# %%
# Saving the model embeddings
train_results, train_labels = save_train_embeddings(
device, Exptype, folder_created, train_loader, model)
test_results, test_labels = save_test_embeddings(
device, Exptype, folder_created, test_loader, model)
# %%
# Plotting the latent space
graph_name_2D = os.path.join(folder_created, 'Training_Feature_2D.png')
plot_embeddings(train_results, train_labels, graph_name_2D, class_names)
graph_name_2D = os.path.join(folder_created, 'Testing_Feature_2D.png')
plot_embeddings(test_results, test_labels, graph_name_2D, class_names)
# %%
# Plotting the latent space in 3D
latent_animation(class_names, folder_created, train_results,
train_labels, test_results, test_labels)
# %%
# Model parameters
count_parameters(model)
# %%
# Classifier training
classifier_linear(Exptype, folder_created)
diff --git a/Contrastive Loss/Network.py b/Contrastive Loss/Network.py
index 97857d5..7d47aab 100644
--- a/Contrastive Loss/Network.py
+++ b/Contrastive Loss/Network.py
@@ -1,77 +1,77 @@
# -*- coding: utf-8 -*-
"""
@author: srpv
contact: vigneashwara.solairajapandiyan@empa.ch, vigneashpandiyan@gmail.com
The codes in this following script will be used for the publication of the following work
-"Acoustic emission signature of martensitic phase transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction "
+"Acoustic emission signature of martensitic transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction"
@any reuse of this code should be authorized by the first owner, code author
"""
# libraries to import
import matplotlib.pyplot as plt
import numpy as np
from prettytable import PrettyTable
import torch.nn as nn
import torch
class Network(nn.Module):
"""
This class represents a neural network model for processing 1D input data.
Args:
emb_dim (int): The dimension of the output embedding. Default is 4.
Attributes:
conv (nn.Sequential): The convolutional layers of the network.
fc (nn.Sequential): The fully connected layers of the network.
"""
def __init__(self, emb_dim=4):
super(Network, self).__init__()
self.conv = nn.Sequential(
nn.Conv1d(in_channels=1, out_channels=4, kernel_size=8, stride=8),
nn.BatchNorm1d(4),
nn.PReLU(),
nn.Dropout(0.1),
nn.Conv1d(4, 8, kernel_size=8, stride=8),
nn.BatchNorm1d(8),
nn.PReLU(),
nn.Dropout(0.1),
nn.Conv1d(8, 16, kernel_size=8, stride=4),
nn.BatchNorm1d(16),
nn.PReLU(),
nn.Dropout(0.1),
nn.Conv1d(16, 32, kernel_size=8, stride=4),
nn.BatchNorm1d(32),
nn.PReLU(),
nn.Dropout(0.1),
)
self.fc = nn.Sequential(
nn.Linear(32*3, 64),
nn.PReLU(),
nn.Linear(64, emb_dim),
)
def forward(self, x):
"""
Forward pass of the network.
Args:
x (torch.Tensor): The input tensor of shape (batch_size, 1, sequence_length).
Returns:
torch.Tensor: The output tensor of shape (batch_size, emb_dim).
"""
x = self.conv(x)
x = x.view(-1, 32*3)
x = self.fc(x)
return x
diff --git a/Contrastive Loss/Trainer.py b/Contrastive Loss/Trainer.py
index dd34607..a88d6c1 100644
--- a/Contrastive Loss/Trainer.py
+++ b/Contrastive Loss/Trainer.py
@@ -1,174 +1,174 @@
# -*- coding: utf-8 -*-
"""
@author: srpv
contact: vigneashwara.solairajapandiyan@empa.ch, vigneashpandiyan@gmail.com
The codes in this following script will be used for the publication of the following work
-"Acoustic emission signature of martensitic phase transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction "
+"Acoustic emission signature of martensitic transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction"
@any reuse of this code should be authorized by the first owner, code author
"""
# libraries to import
from tqdm.notebook import tqdm
import torch
import numpy as np
import os
def model_trainer(epochs, train_loader, device, optimizer, model, criterion, Exptype, folder_created):
"""
Trains a model using the provided training data and parameters.
Args:
epochs (int): The number of epochs to train the model.
train_loader (torch.utils.data.DataLoader): The data loader for the training data.
device (torch.device): The device to use for training (e.g., 'cuda' or 'cpu').
optimizer (torch.optim.Optimizer): The optimizer to use for training.
model (torch.nn.Module): The model to train.
criterion (torch.nn.Module): The loss function to use for training.
Exptype (str): The experiment type.
folder_created (str): The path to the folder where the trained model will be saved.
Returns:
tuple: A tuple containing the following elements:
- Training_loss (list): The training loss for each epoch.
- Training_loss_mean (list): The mean training loss for each epoch.
- Training_loss_std (list): The standard deviation of the training loss for each epoch.
- running_loss (list): The running loss for each training step.
- model (torch.nn.Module): The trained model.
"""
running_loss = []
Training_loss = []
Training_loss_mean = []
Training_loss_std = []
for epoch in tqdm(range(epochs), desc="Epochs"):
total_loss = 0
iterations = 0
epoch_smoothing = []
for step, (anchor_img, pair_img, anchor_label, label) in enumerate(tqdm(train_loader, desc="Training", leave=False)):
#torch.Size([100, 1, 5000])
anchor_img = anchor_img.to(device, dtype=torch.float)
anchor_img = anchor_img.unsqueeze(1)
# print(anchor_label)
anchor_label = anchor_label.to(device, dtype=torch.float)
pair_img = pair_img.to(device, dtype=torch.float)
pair_img = pair_img.unsqueeze(1)
# print(positive_img.shape)
optimizer.zero_grad()
anchor_out = model(anchor_img)
pair_out = model(pair_img)
# negative_out = model(negative_img)
loss = criterion(anchor_out, pair_out, anchor_label)
loss.backward()
optimizer.step()
running_loss.append(loss.cpu().detach().numpy())
epoch_smoothing.append(loss.cpu().detach().numpy())
total_loss += loss
iterations = iterations+1
loss_train = total_loss/len(train_loader)
Training_loss.append(loss_train.cpu().detach().numpy())
Training_loss_mean.append(np.mean(epoch_smoothing))
Training_loss_std.append(np.std(epoch_smoothing))
print("Epoch: {}/{} - Loss: {:.4f}".format(epoch+1, epochs, loss_train))
model_name = Exptype+"_trained_model.pth"
torch.save({"model_state_dict": model.state_dict(),
"optimzier_state_dict": optimizer.state_dict()
}, os.path.join(folder_created, model_name))
return Training_loss, Training_loss_mean, Training_loss_std, running_loss, model
def save_train_embeddings(device, Exptype, folder_created, train_loader, model):
"""
Save the embeddings of the training data using the specified model.
Args:
device (torch.device): The device to use for computation.
Exptype (str): The experiment type.
folder_created (str): The path to the folder where the embeddings will be saved.
train_loader (torch.utils.data.DataLoader): The data loader for the training data.
model: The model used to generate the embeddings.
Returns:
tuple: A tuple containing the training embeddings and labels.
"""
train_results = []
labels = []
model.eval()
with torch.no_grad():
for img, _, _, label in tqdm(train_loader):
img = img.unsqueeze(1)
train_results.append(model(img.to(device, dtype=torch.float)).cpu().numpy())
labels.append(label)
train_results = np.concatenate(train_results)
train_labels = np.concatenate(labels)
train_embeddings = Exptype+'_train_embeddings_.npy'
train_embeddings = os.path.join(folder_created, train_embeddings)
train_labelsname = Exptype+'_train_labels_.npy'
train_labelsname = os.path.join(folder_created, train_labelsname)
np.save(train_embeddings, train_results, allow_pickle=True)
np.save(train_labelsname, train_labels, allow_pickle=True)
return train_results, train_labels
def save_test_embeddings(device, Exptype, folder_created, test_loader, model):
"""
Saves the test embeddings and labels generated by the model.
Args:
device (torch.device): The device on which the model is loaded.
Exptype (str): The experiment type.
folder_created (str): The path to the folder where the embeddings and labels will be saved.
test_loader (torch.utils.data.DataLoader): The data loader for the test dataset.
model: The trained model.
Returns:
tuple: A tuple containing the test embeddings and labels.
"""
test_results = []
labels = []
model.eval()
with torch.no_grad():
for img, label in tqdm(test_loader):
img = img.unsqueeze(1)
test_results.append(model(img.to(device, dtype=torch.float)).cpu().numpy())
labels.append(label)
test_results = np.concatenate(test_results)
test_labels = np.concatenate(labels)
test_results.shape
test_embeddings = Exptype+'_test_embeddings_.npy'
test_embeddings = os.path.join(folder_created, test_embeddings)
test_labelsname = Exptype+'_test_labels_.npy'
test_labelsname = os.path.join(folder_created, test_labelsname)
np.save(test_embeddings, test_results, allow_pickle=True)
np.save(test_labelsname, test_labels, allow_pickle=True)
return test_results, test_labels
diff --git a/Contrastive Loss/Utils.py b/Contrastive Loss/Utils.py
index 52ab4e7..b7ebab9 100644
--- a/Contrastive Loss/Utils.py
+++ b/Contrastive Loss/Utils.py
@@ -1,282 +1,278 @@
# -*- coding: utf-8 -*-
"""
@author: srpv
contact: vigneashwara.solairajapandiyan@empa.ch, vigneashpandiyan@gmail.com
The codes in this following script will be used for the publication of the following work
-"Acoustic emission signature of martensitic phase transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction "
+"Acoustic emission signature of martensitic transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction"
@any reuse of this code should be authorized by the first owner, code author
"""
# libraries to import
import matplotlib.pyplot as plt
import numpy as np
from prettytable import PrettyTable
import torch.nn as nn
import torch
import pandas as pd
import os
from matplotlib import animation
-marker = ["o", "s", "d", "*", ">", "X"]
-color = ['cyan', 'orange', 'purple', 'blue', 'green', 'red']
-
def plot_function(Exptype, folder_created, Training_loss, Training_loss_mean, Training_loss_std, running_loss):
"""
Plot and save training loss curves.
Inputs:
- Exptype: A string representing the experiment type.
- folder_created: A string representing the path to the folder where the plots will be saved.
- Training_loss: A list of training loss values.
- Training_loss_mean: A list of mean training loss values.
- Training_loss_std: A list of standard deviation of training loss values.
- running_loss: A list of running loss values.
Outputs:
None
"""
Training_loss = np.asarray(Training_loss)
Training_lossfile = Exptype+'_Training_loss.npy'
Training_lossfile = os.path.join(folder_created, Training_lossfile)
np.save(Training_lossfile, Training_loss, allow_pickle=True)
Training_loss_mean = np.asarray(Training_loss_mean)
Training_loss_meanfile = Exptype+'_Training_loss_mean.npy'
Training_loss_meanfile = os.path.join(folder_created, Training_loss_meanfile)
np.save(Training_loss_meanfile, Training_loss_mean, allow_pickle=True)
Training_loss_std = np.asarray(Training_loss_std)
Training_loss_stdfile = Exptype+'_Training_loss_std.npy'
Training_loss_stdfile = os.path.join(folder_created, Training_loss_stdfile)
np.save(Training_loss_stdfile, Training_loss_std, allow_pickle=True)
plt.rcParams.update({'font.size': 15})
plt.figure(1)
plt.plot(running_loss, 'b--', linewidth=2.0)
plt.title('Iteration vs Loss value')
plt.xlabel('Iteration')
plt.ylabel('Loss value')
plt.savefig(os.path.join(folder_created, 'Training loss.png'), dpi=600, bbox_inches='tight')
plt.show()
fig, ax = plt.subplots()
plt.plot(Training_loss, 'g', linewidth=1.0)
ax.fill_between(Training_loss, Training_loss_mean - Training_loss_std,
Training_loss_mean + Training_loss_std, alpha=0.5)
ax.legend(['Training loss'])
plt.title('Training loss')
plt.xlabel('Epoch')
plt.ylabel('Loss value')
plt.savefig(os.path.join(folder_created, 'Training loss average.png'),
dpi=600, bbox_inches='tight')
plt.show()
def init_weights(m):
"""
Initializes the weights of a convolutional layer using the Kaiming normal initialization.
Args:
m (nn.Conv1d): The convolutional layer to initialize.
Returns:
None
"""
if isinstance(m, nn.Conv1d):
torch.nn.init.kaiming_normal_(m.weight)
def count_parameters(model):
"""
Counts the number of trainable parameters in a given model.
Args:
model (torch.nn.Module): The model for which to count the parameters.
Returns:
int: The total number of trainable parameters in the model.
"""
table = PrettyTable(["Modules", "Parameters"])
total_params = 0
for name, parameter in model.named_parameters():
if not parameter.requires_grad:
continue
param = parameter.numel()
table.add_row([name, param])
total_params += param
print(table)
print(f"Total Trainable Params: {total_params}")
return total_params
+marker = ["o", "s", "d", "*", ">", "X"]
+color = ['cyan', 'orange', 'purple', 'blue', 'green', 'red']
+
+
def plot_embeddings(embeddings, targets, graph_name_2D, classes, xlim=None, ylim=None):
"""
Plots the embeddings in a 2D graph.
Args:
embeddings (numpy.ndarray): The embeddings to be plotted.
targets (numpy.ndarray): The target labels for each embedding.
graph_name_2D (str): The name of the graph file to be saved.
classes (list): The list of class labels.
xlim (tuple, optional): The x-axis limits of the graph. Defaults to None.
ylim (tuple, optional): The y-axis limits of the graph. Defaults to None.
Returns:
None
"""
plt.figure(figsize=(7, 5))
count = 0
for i in np.unique(targets):
inds = np.where(targets == i)[0]
plt.scatter(embeddings[inds, 0], embeddings[inds, 1], alpha=0.7,
color=color[count], marker=marker[count], s=100)
count = count+1
if xlim:
plt.xlim(xlim[0], xlim[1])
if ylim:
plt.ylim(ylim[0], ylim[1])
plt.legend(classes, bbox_to_anchor=(1.41, 1.05))
plt.xlabel('Weights_1', labelpad=10)
plt.ylabel('Weights_2', labelpad=10)
graph_title = "Feature space distribution"
plt.title(str(graph_title), fontsize=15)
plt.savefig(graph_name_2D, bbox_inches='tight', dpi=600)
plt.show()
def Three_embeddings(embeddings, targets, graph_name, class_names, ang, xlim=None, ylim=None):
"""
Plot the feature space distribution of three-dimensional embeddings.
Args:
embeddings (numpy.ndarray): The three-dimensional embeddings.
targets (numpy.ndarray): The target labels for each embedding.
graph_name (str): The name of the graph file to be saved.
class_names (list): The list of class names.
ang (float): The azimuth angle for the 3D plot.
xlim (tuple, optional): The limits for the x-axis. Defaults to None.
ylim (tuple, optional): The limits for the y-axis. Defaults to None.
Returns:
tuple: A tuple containing the matplotlib Axes3D object and the matplotlib Figure object.
"""
group = targets
df2 = pd.DataFrame(group)
df2.columns = ['Categorical']
df2 = df2['Categorical'].replace(0, 'Ti64')
df2 = pd.DataFrame(df2)
df2 = df2['Categorical'].replace(1, 'Ti64_3Fe')
df2 = pd.DataFrame(df2)
df2 = df2['Categorical'].replace(2, 'Ti64_6Fe')
df2 = pd.DataFrame(df2)
group = pd.DataFrame(df2)
group = group.to_numpy()
group = np.ravel(group)
x1 = embeddings[:, 0]
x2 = embeddings[:, 1]
x3 = embeddings[:, 2]
df = pd.DataFrame(dict(x=x1, y=x2, z=x3, label=group))
groups = df.groupby('label')
uniq = list(set(df['label']))
uniq = ['Ti64', 'Ti64_3Fe', 'Ti64_6Fe']
-
+ # uniq = class_names
+ # uniq=["0","1","2","3"]
+
fig = plt.figure(figsize=(12, 6), dpi=100)
fig.set_facecolor('white')
plt.rcParams["legend.markerscale"] = 2
+
ax = plt.axes(projection='3d')
+
ax.grid(False)
ax.view_init(azim=ang) # 115
marker = ["o", "s", "d", "*", ">", "X"]
color = ['cyan', 'orange', 'purple', 'blue', 'green', 'red']
ax.set_facecolor('white')
- ax.xaxis.pane.fill = False
- ax.yaxis.pane.fill = False
- ax.zaxis.pane.fill = False
+ ax.w_xaxis.pane.fill = False
+ ax.w_yaxis.pane.fill = False
+ ax.w_zaxis.pane.fill = False
ax.xaxis.set_pane_color((1.0, 1.0, 1.0, 0.0))
ax.yaxis.set_pane_color((1.0, 1.0, 1.0, 0.0))
ax.zaxis.set_pane_color((1.0, 1.0, 1.0, 0.0))
# make the grid lines transparent
ax.xaxis._axinfo["grid"]['color'] = (1, 1, 1, 0)
ax.yaxis._axinfo["grid"]['color'] = (1, 1, 1, 0)
ax.zaxis._axinfo["grid"]['color'] = (1, 1, 1, 0)
graph_title = "Feature space distribution"
j = 0
for i in uniq:
# print(i)
indx = group == i
a = x1[indx]
b = x2[indx]
c = x3[indx]
ax.plot(a, b, c, color=color[j], label=uniq[j], marker=marker[j], linestyle='', ms=7)
j = j+1
plt.xlabel('Weights_1', labelpad=10)
plt.ylabel('Weights_2', labelpad=10)
ax.set_zlabel('Weights_3', labelpad=10)
plt.title(str(graph_title), fontsize=15)
plt.legend(markerscale=20)
plt.locator_params(nbins=6)
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
#plt.zticks(fontsize = 25)
plt.legend(loc='upper left', frameon=False)
plt.savefig(graph_name, bbox_inches='tight', dpi=400)
plt.show()
return ax, fig
def latent_animation(class_names, folder_created, train_results, train_labels, test_results, test_labels):
- """
- Create animations of 3D feature space distribution.
-
- Args:
- class_names (list): A list of class names.
- folder_created (str): The path to the folder where the animations will be saved.
- train_results (numpy array): The training results in a numpy array.
- train_labels (numpy array): The training labels in a numpy array.
- test_results (numpy array): The testing results in a numpy array.
- test_labels (numpy array): The testing labels in a numpy array.
+ # Code for creating animations of 3D feature space distribution
+ # Input: class_names (list), folder_created (str), train_results (numpy array), train_labels (numpy array), test_results (numpy array), test_labels (numpy array)
+ # Output: None
- Returns:
- None
- """
+ # Code for creating animations goes here
graph_name = os.path.join(folder_created, 'Training_Feature_3D.png')
ax, fig = Three_embeddings(train_results, train_labels, graph_name, class_names, ang=35)
gif1_name = os.path.join(folder_created, 'Training_Feature_3D.gif')
def rotate(angle):
ax.view_init(azim=angle)
angle = 3
ani = animation.FuncAnimation(fig, rotate, frames=np.arange(0, 360, angle), interval=50)
ani.save(gif1_name, writer=animation.PillowWriter(fps=20))
graph_name = os.path.join(folder_created, 'Testing_Feature_3D.png')
ax, fig = Three_embeddings(test_results, test_labels, graph_name, class_names, ang=35)
gif1_name = os.path.join(folder_created, 'Testing_Feature_3D.gif')
def rotate(angle):
ax.view_init(azim=angle)
angle = 3
ani = animation.FuncAnimation(fig, rotate, frames=np.arange(0, 360, angle), interval=50)
ani.save(gif1_name, writer=animation.PillowWriter(fps=20))
diff --git a/Contrastive Loss/__pycache__/Classifier.cpython-311.pyc b/Contrastive Loss/__pycache__/Classifier.cpython-311.pyc
new file mode 100644
index 0000000..6b1d0d2
Binary files /dev/null and b/Contrastive Loss/__pycache__/Classifier.cpython-311.pyc differ
diff --git a/Contrastive Loss/__pycache__/Dataloader.cpython-311.pyc b/Contrastive Loss/__pycache__/Dataloader.cpython-311.pyc
new file mode 100644
index 0000000..cd4fd5e
Binary files /dev/null and b/Contrastive Loss/__pycache__/Dataloader.cpython-311.pyc differ
diff --git a/Contrastive Loss/__pycache__/Loss.cpython-311.pyc b/Contrastive Loss/__pycache__/Loss.cpython-311.pyc
new file mode 100644
index 0000000..653bfaa
Binary files /dev/null and b/Contrastive Loss/__pycache__/Loss.cpython-311.pyc differ
diff --git a/Contrastive Loss/__pycache__/Network.cpython-311.pyc b/Contrastive Loss/__pycache__/Network.cpython-311.pyc
new file mode 100644
index 0000000..7a8f01a
Binary files /dev/null and b/Contrastive Loss/__pycache__/Network.cpython-311.pyc differ
diff --git a/Contrastive Loss/__pycache__/Trainer.cpython-311.pyc b/Contrastive Loss/__pycache__/Trainer.cpython-311.pyc
new file mode 100644
index 0000000..df9048f
Binary files /dev/null and b/Contrastive Loss/__pycache__/Trainer.cpython-311.pyc differ
diff --git a/Contrastive Loss/__pycache__/Utils.cpython-311.pyc b/Contrastive Loss/__pycache__/Utils.cpython-311.pyc
new file mode 100644
index 0000000..f858d57
Binary files /dev/null and b/Contrastive Loss/__pycache__/Utils.cpython-311.pyc differ
diff --git a/Data_preprocessing/CM_Rawspace.npy b/Crack dynamics/Data/KM_20_Ti64_6Fe/channel_0/File_108.bin
similarity index 56%
copy from Data_preprocessing/CM_Rawspace.npy
copy to Crack dynamics/Data/KM_20_Ti64_6Fe/channel_0/File_108.bin
index d9b6797..8094a45 100644
Binary files a/Data_preprocessing/CM_Rawspace.npy and b/Crack dynamics/Data/KM_20_Ti64_6Fe/channel_0/File_108.bin differ
diff --git a/Data_preprocessing/CM_Rawspace.npy b/Crack dynamics/Data/KM_20_Ti64_6Fe/channel_1/File_108.bin
similarity index 56%
copy from Data_preprocessing/CM_Rawspace.npy
copy to Crack dynamics/Data/KM_20_Ti64_6Fe/channel_1/File_108.bin
index d9b6797..33e8948 100644
Binary files a/Data_preprocessing/CM_Rawspace.npy and b/Crack dynamics/Data/KM_20_Ti64_6Fe/channel_1/File_108.bin differ
diff --git a/Crack dynamics/Main_Visualize.py b/Crack dynamics/Main_Visualize.py
index 3fc2fd8..11899f6 100644
--- a/Crack dynamics/Main_Visualize.py
+++ b/Crack dynamics/Main_Visualize.py
@@ -1,167 +1,175 @@
# -*- coding: utf-8 -*-
"""
@author: srpv
contact: vigneashwara.solairajapandiyan@empa.ch, vigneashpandiyan@gmail.com
The codes in this following script will be used for the publication of the following work
-"Acoustic emission signature of martensitic phase transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction "
+"Acoustic emission signature of martensitic transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction"
@any reuse of this code should be authorized by the first owner, code author
"""
# libraries to import
import random
+from matplotlib import cm
+import pywt
+from operator import itemgetter
+from itertools import groupby
+from scipy import signal
import os
+from pathlib import Path
import ntpath
import re
import matplotlib.pyplot as plt
import pandas as pd
import glob
import numpy as np
+
import glob
+import seaborn as sns
import matplotlib.pyplot as mpl
from Utils import *
mpl.rcParams['agg.path.chunksize'] = 1000000
def path_leaf(path):
"""
Extracts the file name from a given path.
Args:
path (str): The path from which to extract the file name.
Returns:
str: The file name extracted from the path.
"""
head, tail = ntpath.split(path)
return tail
# return tail or ntpath.basename(head)
# %%
# path to the data
-path = r'C:\Users\vigneashwara.p\Desktop\C4Science\lpbf-acoustic-dynamics-of-in-situ-alloying-of-titanium-fe\Crack dynamics\Data\Ti64_6Fe_KM_23'
+path = r'C:\Users\srpv\Desktop\Git\Additive-Manufacturing-Acoustic-Dynamics-of-in-situ-alloying-of-Titanium-Fe\ML classifier\Crack dynamics\Data\KM_20_Ti64_6Fe'
sample_rate = 1500000 # sample rate of the data
isDirectory = os.path.isdir(path)
file = os.path.join(os.getcwd(), os.listdir(os.getcwd())[0])
total_path = os.path.dirname(file)
print(total_path)
if isDirectory:
print(path)
print(isDirectory)
Channel0 = (os.path.join(path, 'channel_0_decoded'))
print(Channel0)
Channel1 = (os.path.join(path, 'channel_1_decoded'))
print(Channel1)
all_files = glob.glob(Channel0 + "/*.csv")
for filename in all_files:
tail = path_leaf(filename)
A = tail.split('.')
print(A)
data_file_name = A[0]
print(data_file_name)
# Create a folder to save the data
folder_created = os.path.join(total_path, data_file_name)
print(folder_created)
try:
os.makedirs(folder_created, exist_ok=True)
print("Directory created....")
except OSError as error:
print("Directory already exists....")
Channel_0 = (os.path.join(Channel0, tail))
print(Channel0)
Channel_1 = (os.path.join(Channel1, tail))
print(Channel1)
data1 = pd.read_csv(Channel_0)
data1 = data1.to_numpy()
data1 = np.ravel(data1)
data2 = pd.read_csv(Channel_1)
data2 = data2.to_numpy()
data2 = np.ravel(data2)
length = len(data1)
N = length
t0 = 0
dt = 1/sample_rate
time = np.arange(0, N) * dt + t0
# Plot the raw data
uniq = str(random.randint(0, 9999))
plt.rcParams.update(plt.rcParamsDefault)
fig = plt.figure(figsize=(10, 6), dpi=800)
fig, ((ax1, ax2)) = plt.subplots(2, 1)
title = ' AE '+'Signal'
fig.suptitle(title)
plt.rc("font", size=8)
ax1.plot(time[0:800000], data1[0:800000], 'red', linewidth=2.5, label='Laser')
ax1.legend(fontsize=15)
ax1.set_ylabel('Voltage (V)')
ax2.plot(time[0:800000], data2[0:800000], 'blue', linewidth=1, label='AE')
ax2.legend(fontsize=15)
# ax1.set_xlim(0,0.3)
ax2.set_xlabel('Time (sec)', labelpad=5)
ax2.set_ylabel('Voltage (V)')
# ax2.set_xlim(0,0.3)
# Plot the raw data
for ax in fig.get_axes():
ax.label_outer()
# plt.xlim((0, 40e5))
tail = path_leaf(path)
A = tail.split('.')
data_file_name = A[0]
graph_2 = str(data_file_name)+'_'+uniq+'_'+'RawAE'+'signal'+'.png'
plt.savefig(os.path.join(folder_created, graph_2), bbox_inches='tight', dpi=200)
plt.show()
- data1_ = data1[570000:750000] # truncating from whole data
- data2_ = data2[570000:750000] # truncating from whole data
+ data1_ = data1[240000:320000] # truncating from whole data
+ data2_ = data2[240000:320000] # truncating from whole data
length = len(data1_)
N = length
t0 = 0
dt = 1/sample_rate
time_ = np.arange(0, N) * dt + t0
plt.rcParams.update(plt.rcParamsDefault)
fig = plt.figure(figsize=(10, 6), dpi=800)
fig, ((ax1, ax2)) = plt.subplots(2, 1)
title = ' AE '+'Signal'
fig.suptitle(title)
plt.rc("font", size=8)
ax1.plot(time_, data1_, 'red', linewidth=2.5, label='Laser')
ax1.legend(fontsize=15)
ax1.set_ylabel('Voltage (V)')
ax2.plot(time_, data2_, 'blue', linewidth=1, label='AE')
ax2.legend(fontsize=15)
# ax1.set_xlim(0,0.3)
ax2.set_xlabel('Time (sec)', labelpad=5)
ax2.set_ylabel('Voltage (V)')
# ax2.set_xlim(0,0.3)
for ax in fig.get_axes():
ax.label_outer()
# plt.xlim((0, 40e5))
tail = path_leaf(path)
A = tail.split('.')
data_file_name = A[0]
graph_2 = str(data_file_name)+'_'+uniq+'_'+'AE'+'signal'+'.png'
plt.savefig(os.path.join(folder_created, graph_2), bbox_inches='tight', dpi=200)
plt.show()
# Plot the spectrogram
specgram3d(data_file_name, uniq, data2_, sample_rate, time_, folder_created)
diff --git a/Crack dynamics/Utils.py b/Crack dynamics/Utils.py
index 949fe10..ff16851 100644
--- a/Crack dynamics/Utils.py
+++ b/Crack dynamics/Utils.py
@@ -1,112 +1,98 @@
# -*- coding: utf-8 -*-
"""
@author: srpv
contact: vigneashwara.solairajapandiyan@empa.ch, vigneashpandiyan@gmail.com
The codes in this following script will be used for the publication of the following work
-"Acoustic emission signature of martensitic phase transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction "
+"Acoustic emission signature of martensitic transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction"
@any reuse of this code should be authorized by the first owner, code author
"""
# libraries to import
from matplotlib import mlab
+import pandas as pd
import numpy as np
+import seaborn as sns
import matplotlib.pyplot as plt
from scipy import signal
+# import pywt
+import pywt
import os
from matplotlib import cm
-from matplotlib.ticker import FormatStrFormatter
+
def filter(signal_window, sample_rate):
"""
Applies a low-pass filter to the given signal window.
Args:
signal_window (array-like): The input signal window to be filtered.
sample_rate (float): The sample rate of the signal.
Returns:
array-like: The filtered signal window.
"""
lowpass = 0.49 * sample_rate # Cut-off frequency of the filter
lowpass_freq = lowpass / (sample_rate / 2) # Normalize the frequency
b, a = signal.butter(5, lowpass_freq, 'low')
signal_window = signal.filtfilt(b, a, signal_window)
-
-
- highpass = 0.05 * sample_rate # Normalize the frequency
- highpass_freq = highpass / (sample_rate / 2)
- b, a = signal.butter(5, highpass_freq, 'high')
- signal_window = signal.filtfilt(b, a, signal_window)
#plt.plot(t, lowpassfilter, label='Low-Pass')
return signal_window
def specgram3d(Material, uniq, data, sample_rate, time, folder_created, ax=None, title=None):
"""
Generate a 3D spectrogram plot.
Parameters:
- Material (str): The material name.
- uniq (str): The unique identifier.
- data (ndarray): The input data.
- sample_rate (int): The sample rate of the data.
- time (float): The time duration of the data.
- folder_created (str): The path of the folder where the plot will be saved.
- ax (Axes3D, optional): The 3D axes object. Defaults to None.
- title (str, optional): The title of the plot. Defaults to None.
Returns:
- X (ndarray): The time values.
- Y (ndarray): The frequency values.
- Z (ndarray): The amplitude values in dB.
"""
- fig = plt.figure(figsize=(6, 12))
+ fig = plt.figure(figsize=(7, 12))
ax = plt.axes(projection='3d')
- ax.view_init(azim=40, elev=60)
-
- data=filter(data, sample_rate)
+ ax.view_init(azim=40, elev=70)
spec, freqs, t = mlab.specgram(data, Fs=sample_rate)
- # X, Y, Z = t[None, :], freqs[:, None], 20*np.log10(spec)
- X, Y, Z = t[None, :], freqs[:, None], spec
-
- X= X+0.37
+ X, Y, Z = t[None, :], freqs[:, None], 20*np.log10(spec)
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm,
- linewidth=0, rstride=2, cstride=1, alpha=None, antialiased=True)
+ linewidth=0, antialiased=False)
ax.set_xlabel('Time (sec)', labelpad=7)
ax.set_ylabel('Frequency (Hz)', labelpad=9)
ax.set_zlabel('Amplitude (dB)', labelpad=9)
+
+ # ax.set_title('Surface plot')
ax.grid(False)
ax.set_facecolor('white')
-
-
- num_ticks = 6
- z_min, z_max = ax.get_zlim()
- v = np.linspace(z_min, z_max, num_ticks)
-
- print(z_min, z_max)
- ax.set_zticks(np.linspace(z_min, z_max, num_ticks))
- ax.zaxis.set_major_formatter(FormatStrFormatter('%.2e'))
-
- plt.gca().invert_xaxis()
-
- ax.xaxis.pane.fill = False
- ax.yaxis.pane.fill = False
- ax.zaxis.pane.fill = False
-
- fig.colorbar(surf, ax=ax,ticks=v,
+
+ ax.w_xaxis.pane.fill = False
+ ax.w_yaxis.pane.fill = False
+ ax.w_zaxis.pane.fill = False
+
+ ax.yaxis.set_major_formatter('{x:0.2f}')
+
+ fig.colorbar(surf, ax=ax,
shrink=0.1, aspect=4)
graphname = str(Material)+'_'+str(uniq)+'_2D_Wavelet.png'
plt.savefig(os.path.join(folder_created, graphname), bbox_inches='tight', dpi=800)
plt.show()
return X, Y, Z
diff --git a/Data_preprocessing/CM_Rawspace.npy b/Data_preprocessing/CM_Rawspace.npy
index d9b6797..9c9e5a6 100644
Binary files a/Data_preprocessing/CM_Rawspace.npy and b/Data_preprocessing/CM_Rawspace.npy differ
diff --git a/Data_preprocessing/Data_prep.py b/Data_preprocessing/Data_prep.py
index be84d72..0f22cff 100644
--- a/Data_preprocessing/Data_prep.py
+++ b/Data_preprocessing/Data_prep.py
@@ -1,113 +1,121 @@
# -*- coding: utf-8 -*-
"""
@author: srpv
contact: vigneashwara.solairajapandiyan@empa.ch, vigneashpandiyan@gmail.com
The codes in this following script will be used for the publication of the following work
-"Acoustic emission signature of martensitic phase transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction "
+"Acoustic emission signature of martensitic transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction"
@any reuse of this code should be authorized by the first owner, code author
"""
# libraries to import
from sklearn.model_selection import train_test_split # implementing train-test-split
import numpy as np
import pandas as pd
+from mpl_toolkits.mplot3d import Axes3D
+from matplotlib import animation
+import matplotlib.pyplot as plt
from matplotlib.pyplot import specgram
import seaborn as sns
from scipy.stats import norm
+# import joypy
import pandas as pd
+from matplotlib import cm
+from scipy import signal
+import pywt
+import matplotlib.patches as mpatches
import os
# %%
-datapath = r'C:\Users\vigneashwara.p\Desktop\C4Science\lpbf-acoustic-dynamics-of-in-situ-alloying-of-titanium-fe\Rawdata'
+datapath = r'C:\Users\srpv\Desktop\Git\Additive-Manufacturing-Acoustic-Dynamics-of-in-situ-alloying-of-Titanium-Fe\ML classifier\Rawdata'
# %%
sns.set(font_scale=1.5)
sns.set_style("whitegrid", {'axes.grid': False})
sns.set_style("ticks", {"xtick.major.size": 8, "ytick.major.size": 8})
sample_rate = 1500000
windowsize = 5000
def dataprocessing(df):
"""
Preprocesses the given dataframe by standardizing the values of each column.
Args:
df (pandas.DataFrame): The input dataframe.
Returns:
pandas.DataFrame: The preprocessed dataframe with standardized values.
"""
database = df
print(database.shape)
database = database.apply(lambda x: (x - np.mean(x))/np.std(x), axis=1)
# anomaly_database=anomaly_database.to_numpy().astype(np.float64)
return database
def data(case, exp):
"""
Process the data for a given case and experiment.
Args:
case (int): The case number.
exp (int): The experiment number.
Returns:
tuple: A tuple containing the processed rawspace data and the classspace data.
- rawspace (numpy.ndarray): The processed rawspace data.
- classspace (numpy.ndarray): The classspace data.
"""
rawfile = str(case)+'-'+str(exp)+'.npy'
classfile = 'Classlabel_'+str(case)+'-'+str(exp)+'.npy'
rawspace = os.path.join(datapath, rawfile)
print(rawspace)
rawspace = np.load(rawspace).astype(np.float64)
classspace = os.path.join(datapath, classfile)
print(classspace)
classspace = np.load(classspace)
rawspace = pd.DataFrame(rawspace)
rawspace = dataprocessing(rawspace)
rawspace = rawspace.to_numpy()
return rawspace, classspace
Datasets = ['CM', 'KM']
for Exptype in Datasets:
Raw_1, label_1 = data(Exptype, 'Ti64')
Raw_2, label_2 = data(Exptype, 'Ti64_3Fe')
Raw_3, label_3 = data(Exptype, 'Ti64_6Fe')
rawspace = np.concatenate((Raw_1, Raw_2, Raw_3), axis=0)
classspace = np.concatenate((label_1, label_2, label_3), axis=0)
classspace = np.expand_dims(classspace, axis=1)
# %%
# Visualization
X_train, X_test, y_train, y_test = train_test_split(
rawspace, classspace, test_size=0.25, random_state=66)
Training = np.concatenate((y_train, X_train), axis=1)
Testing = np.concatenate((y_test, X_test), axis=1)
classfile = Exptype+'_Class'+'_' + 'label'+'.npy'
classspace = classspace.astype(np.float64)
np.save(classfile, classspace, allow_pickle=True)
rawfile = Exptype+'_Rawspace'+'.npy'
rawspace = rawspace.astype(np.float64)
np.save(rawfile, rawspace, allow_pickle=True)
diff --git a/Data_preprocessing/KM_Rawspace.npy b/Data_preprocessing/KM_Rawspace.npy
index cb60bf7..5497372 100644
Binary files a/Data_preprocessing/KM_Rawspace.npy and b/Data_preprocessing/KM_Rawspace.npy differ
diff --git a/Feature extraction/CM_Class_label.npy b/Feature extraction/CM_Class_label.npy
new file mode 100644
index 0000000..57863b1
Binary files /dev/null and b/Feature extraction/CM_Class_label.npy differ
diff --git a/Feature extraction/CM_PSD.npy b/Feature extraction/CM_PSD.npy
new file mode 100644
index 0000000..aeca3cc
Binary files /dev/null and b/Feature extraction/CM_PSD.npy differ
diff --git a/Feature extraction/KM_Class_label.npy b/Feature extraction/KM_Class_label.npy
new file mode 100644
index 0000000..3557f45
Binary files /dev/null and b/Feature extraction/KM_Class_label.npy differ
diff --git a/Feature extraction/KM_PSD.npy b/Feature extraction/KM_PSD.npy
new file mode 100644
index 0000000..8de98db
Binary files /dev/null and b/Feature extraction/KM_PSD.npy differ
diff --git a/Feature extraction/Main_Features PSD.py b/Feature extraction/Main_Features PSD.py
index 72daec0..de4620b 100644
--- a/Feature extraction/Main_Features PSD.py
+++ b/Feature extraction/Main_Features PSD.py
@@ -1,194 +1,172 @@
# -*- coding: utf-8 -*-
"""
@author: srpv
contact: vigneashwara.solairajapandiyan@empa.ch, vigneashpandiyan@gmail.com
The codes in this following script will be used for the publication of the following work
-"Acoustic emission signature of martensitic phase transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction "
+"Acoustic emission signature of martensitic transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction"
@any reuse of this code should be authorized by the first owner, code author
"""
# libraries to import
import numpy as np
+import pandas as pd
+import matplotlib.pyplot as plt
+import pywt
import scipy.signal as signal
-from numpy.fft import fftshift, fft
+
from scipy.stats import kurtosis, skew
from scipy.signal import welch, periodogram
+from numpy.fft import fftshift, fft
from scipy.signal import find_peaks
+import statistics
from scipy import stats
+from collections import Counter
from scipy.stats import entropy
from scipy.signal import hilbert, chirp
from scipy.stats import entropy
import os
+#import librosa
print(np.__version__)
# %%
sample_rate = 1500000
windowsize = 5000
t0 = 0
dt = 1/sample_rate
time = np.arange(0, windowsize) * dt + t0
+
band_size = 6
peaks_to_count = 7
count = 0
-path = r'C:\Users\vigneashwara.p\Desktop\C4Science\lpbf-acoustic-dynamics-of-in-situ-alloying-of-titanium-fe\Data_preprocessing'
-# %%
+path = r'C:\Users\srpv\Desktop\Git\Additive-Manufacturing-Acoustic-Dynamics-of-in-situ-alloying-of-Titanium-Fe\ML classifier\Data_preprocessing'
+
+# %%
def filter(signal_window):
- """
- Applies a low-pass filter to the input signal window.
-
- Args:
- signal_window (array-like): The input signal window to be filtered.
-
- Returns:
- array-like: The filtered signal window.
- """
lowpass = 0.49*sample_rate # Cut-off frequency of the filter
lowpass_freq = lowpass / (sample_rate / 2) # Normalize the frequency
b, a = signal.butter(5, lowpass_freq, 'low')
lowpassfilter = signal.filtfilt(b, a, signal_window)
#plt.plot(t, lowpassfilter, label='Low-Pass')
return lowpassfilter
-def get_band(band_size, band_max_size):
- """
- Generate a list of frequency bands based on the given band size and maximum band size.
-
- Parameters:
- band_size (int): The number of frequency bands to generate.
- band_max_size (float): The maximum size of the frequency band.
-
- Returns:
- list: A list of frequency bands.
-
- Example:
- >>> get_band(5, 100)
- [0, 20, 40, 60, 80]
- """
+# %%
+def get_band(band_size, band_max_size):
band_window = 0
band = []
for y in range(band_size):
band.append(band_window)
band_window += band_max_size / band_size
return band
def spectrumpower(psd, band, freqs):
- """
- Calculate the delta power for each frequency band in the given power spectral density (psd).
-
- Args:
- psd (list): The power spectral density values.
- band (list): The frequency bands.
- freqs (list): The frequencies corresponding to the power spectral density.
-
- Returns:
- list: The delta power for each frequency band.
-
- """
length = len(band)
+ # print(length)
Feature_deltapower = []
Feature_relativepower = []
for i in range(band_size-1):
if i <= (len(band)):
ii = i
+ # print('low frequencies :',band[ii])
low = band[ii]
+
ii = i+1
high = band[ii]
+ # print('high frequencies :',band[ii])
+ #freq_res = freqs[high] - freqs[low]
idx_delta = np.logical_and(freqs >= low, freqs <= high)
total_power = sum(psd)
delta_power = sum(psd[idx_delta])
delta_rel_power = delta_power / total_power
Feature_deltapower.append(delta_power)
return Feature_deltapower
-def function_features(val):
- """
- Extracts features from the input signal window.
- Args:
- val (array-like): The input signal window.
+# %%
- Returns:
- numpy.ndarray: The extracted feature vectors.
- """
+def function(val):
i = 0
+
signal_window = filter(val)
win = 4 * sample_rate
freqs, psd = periodogram(signal_window, sample_rate, window='hamming')
band_max_size = 900000
band = get_band(band_size, band_max_size)
-
- # print(band)
- # PSD absolute and relative power in each band 10 Features
+
+ # print(band)
+ # PSD absolute and relative power in each band 10 Features
Feature1 = spectrumpower(psd, band, freqs)
Feature = np.asarray(Feature1)
-
+
if i == 0:
- # print("--reached")
+ # print("--reached")
size_of_Feature_vectors = int(len(Feature))
size_of_dataset = int(len(signal_window))
-
+
Feature_vectors = np.empty((0, size_of_Feature_vectors))
rawdataset = np.empty((0, size_of_dataset))
-
- # print(label)
+
+ # print(label)
Feature_vectors = np.append(Feature_vectors, [Feature], axis=0)
rawdataset = np.append(rawdataset, [signal_window], axis=0)
-
return Feature_vectors
+# %%
+
+
def spectro(data_new):
- """
- This function takes in a numpy array of data and extracts features from each column.
-
- Parameters:
- data_new (numpy.ndarray): The input data array.
-
- Returns:
- list: A list of extracted features from each column of the input data.
- """
-
- featurelist = []
columnsdata = data_new.transpose()
columns = np.atleast_2d(columnsdata).shape[1]
+ featurelist = []
+ classlist = []
+ rawlist = []
+ # for row in loop:
for k in range(columns):
-
- print(k)
+
val = columnsdata[:, k]
- Feature_vectors = function_features(val)
+ # totaldatapoints= val.size
+ # window=round(totaldatapoints/windowsize)
+ Feature_vectors = function(val)
+
+ print(k)
+ # print(Feature_vectors)
+
for item in Feature_vectors:
+
featurelist.append(item)
return featurelist
+# %%
+
M = ['KM', 'CM']
for Materials in M:
class_filename = str(Materials)+'_'+'Class_label'+'.npy'
path_ = os.path.join(path, class_filename)
Class_label = np.load(path_).astype(np.float64)
np.save(class_filename, Class_label, allow_pickle=True)
path_ = str(Materials)+'_'+'Rawspace'+'.npy'
path_ = os.path.join(path, path_)
data = np.load(path_).astype(np.float64)
featurelist = spectro(data)
Featurespace = np.asarray(featurelist)
Featurespace = Featurespace.astype(np.float64)
data_filename = str(Materials)+'_PSD'+'.npy'
np.save(data_filename, Featurespace, allow_pickle=True)
diff --git a/Feature extraction/Main_features.py b/Feature extraction/Main_features.py
index 2fc98c3..786631c 100644
--- a/Feature extraction/Main_features.py
+++ b/Feature extraction/Main_features.py
@@ -1,67 +1,67 @@
# -*- coding: utf-8 -*-
"""
@author: srpv
contact: vigneashwara.solairajapandiyan@empa.ch, vigneashpandiyan@gmail.com
The codes in this following script will be used for the publication of the following work
-"Acoustic emission signature of martensitic phase transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction "
+"Acoustic emission signature of martensitic transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction "
@any reuse of this code should be authorized by the first owner, code author
"""
# libraries to import
import os
import numpy as np
from Utils_Featureextraction import *
print(np.__version__)
# %%
sample_rate = 1500000
windowsize = 5000
t0 = 0
dt = 1/sample_rate
time = np.arange(0, windowsize) * dt + t0
band_size = 6
peaks_to_count = 7
count = 0
-path = r'C:\Users\vigneashwara.p\Desktop\C4Science\lpbf-acoustic-dynamics-of-in-situ-alloying-of-titanium-fe\Data_preprocessing'
+path = r'C:\Users\srpv\Desktop\Git\Additive-Manufacturing-Acoustic-Dynamics-of-in-situ-alloying-of-Titanium-Fe\ML classifier\Data_preprocessing'
# %%
-def Timeseries_feature(Materials, sample_rate, band_size, peaks_to_count):
+def Time_frequency_feature(Materials, sample_rate, band_size, peaks_to_count):
"""
This function calculates the time-frequency features of the given materials.
Parameters:
Materials (str): The name of the materials.
sample_rate (int): The sample rate of the data.
band_size (int): The size of the frequency bands.
peaks_to_count (int): The number of peaks to count.
Returns:
Feature_vectors (numpy.ndarray): The calculated time-frequency feature vectors.
"""
path_ = str(Materials)+'_'+'Rawspace'+'.npy'
path_ = os.path.join(path, path_)
data = np.load(path_).astype(np.float64)
- Feature_vectors = Time_frequency_feature(data, sample_rate, band_size, peaks_to_count)
+ Feature_vectors = feature_extraction(data, sample_rate, band_size, peaks_to_count)
return Feature_vectors
M = ['KM', 'CM']
for Materials in M:
featurelist = Timeseries_feature(Materials, sample_rate, band_size, peaks_to_count)
Featurespace = np.asarray(featurelist)
Featurespace = Featurespace.astype(np.float64)
featurefile = str(Materials)+'_Featurespace'+'_' + str(windowsize)+'.npy'
np.save(featurefile, Featurespace, allow_pickle=True)
# %%
diff --git a/Feature extraction/Utils_Featureextraction.py b/Feature extraction/Utils_Featureextraction.py
index 483eb9e..bcd268e 100644
--- a/Feature extraction/Utils_Featureextraction.py
+++ b/Feature extraction/Utils_Featureextraction.py
@@ -1,599 +1,458 @@
# -*- coding: utf-8 -*-
"""
@author: srpv
contact: vigneashwara.solairajapandiyan@empa.ch, vigneashpandiyan@gmail.com
The codes in this following script will be used for the publication of the following work
-"Acoustic emission signature of martensitic phase transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction "
+"Acoustic emission signature of martensitic transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction"
@any reuse of this code should be authorized by the first owner, code author
"""
# libraries to import
import numpy as np
import scipy.signal as signal
from scipy.stats import kurtosis, skew
from scipy.signal import welch, periodogram
from numpy.fft import fftshift, fft
from scipy.signal import find_peaks
import statistics
from scipy import stats
from collections import Counter
from scipy.stats import entropy
from scipy.signal import hilbert, chirp
from scipy.stats import entropy
import pywt
import numpy as np
import scipy.signal as signal
from scipy.stats import kurtosis, skew
from scipy.signal import welch, periodogram
from numpy.fft import fftshift, fft
from scipy.signal import find_peaks
import statistics
from scipy import stats
from collections import Counter
from scipy.stats import entropy
from scipy.signal import hilbert, chirp
from scipy.stats import entropy
import pywt
-import scipy.signal
# %%
def filter(signal_window, sample_rate):
- """
- Applies a low-pass filter to the input signal window.
-
- Args:
- signal_window (array-like): The input signal window to be filtered.
- sample_rate (float): The sample rate of the input signal.
-
- Returns:
- array-like: The filtered signal window.
-
- """
lowpass = 0.49 * sample_rate # Cut-off frequency of the filter
lowpass_freq = lowpass / (sample_rate / 2) # Normalize the frequency
b, a = signal.butter(5, lowpass_freq, 'low')
lowpassfilter = signal.filtfilt(b, a, signal_window)
#plt.plot(t, lowpassfilter, label='Low-Pass')
return lowpassfilter
+# %%
def Zerocross(a):
- """
- Calculates the number of zero crossings in the input array.
-
- Parameters:
- a (array-like): Input array.
-
- Returns:
- int: Number of zero crossings in the input array.
- """
zero_crossings = np.where(np.diff(np.signbit(a)))[0]
cross = zero_crossings.size
#print (cross)
return cross
+# %%
-def meanfrequency(y, fs):
- """
- Calculates the mean frequency of a given signal.
- Parameters:
- y (array-like): The input signal.
- fs (float): The sampling frequency of the signal.
+def meanfrequency(y, fs):
- Returns:
- float: The mean frequency of the signal.
- """
spec = np.abs(np.fft.rfft(y))
freq = np.fft.rfftfreq(len(y), d=1/fs)
amp = spec / spec.sum()
mean = (freq * amp).sum()
return mean
+# %%
def get_band(band_size, band_max_size):
- """
- Returns a list of values representing a band.
-
- Parameters:
- band_size (int): The number of elements in the band.
- band_max_size (float): The maximum value of the band.
-
- Returns:
- list: A list of values representing the band.
- """
-
band_window = 0
band = []
for y in range(band_size):
band.append(band_window)
band_window += band_max_size / band_size
return band
def spectrumpower(psd, band, freqs):
- """
- Calculate the delta power and relative power of a given power spectral density (psd) within specified frequency bands.
-
- Parameters:
- psd (array-like): The power spectral density values.
- band (array-like): The frequency bands.
- freqs (array-like): The frequencies corresponding to the power spectral density values.
-
- Returns:
- tuple: A tuple containing two lists - Feature_deltapower and Feature_relativepower.
- Feature_deltapower (list): The delta power values for each frequency band.
- Feature_relativepower (list): The relative power values for each frequency band.
- """
length = len(band)
Feature_deltapower = []
Feature_relativepower = []
for i in range(5):
if i <= (len(band)):
ii = i
low = band[ii]
ii = i+1
high = band[ii]
#freq_res = freqs[high] - freqs[low]
idx_delta = np.logical_and(freqs >= low, freqs <= high)
total_power = sum(psd)
delta_power = sum(psd[idx_delta])
delta_rel_power = delta_power / total_power
Feature_deltapower.append(delta_power)
Feature_relativepower.append(delta_rel_power)
return Feature_deltapower, Feature_relativepower
def spectrumpeaks(psd):
- """
- Finds the top 7 peaks in the given power spectral density (psd) array.
-
- Parameters:
- psd (array-like): The power spectral density array.
-
- Returns:
- list: A list containing the top 7 peaks in the psd array. If there are less than 7 peaks, the missing values are filled with zeros.
- """
-
+ import scipy.signal
indexes, value = scipy.signal.find_peaks(psd, height=0, distance=5)
a = value['peak_heights']
sorted_list = sorted(a, reverse=True)
b = sorted_list[0:7]
b_size = int(len(b))
if b_size < 7:
# calc missing values
b_missing = 7 - b_size
for x in range(b_missing):
b.append(0)
# b = [0,0,0]
return b
-def autopeaks(psd):
- """
- Finds the highest peaks in a given power spectral density (PSD) array.
-
- Args:
- psd (array-like): The power spectral density array.
+# %%
- Returns:
- list: A list of the highest peaks in the PSD array. The list contains
- the top 4 peak heights, or 0 if there are less than 4 peaks.
- """
+def autopeaks(psd):
import scipy.signal
indexes, value = scipy.signal.find_peaks(psd, height=0, distance=None)
a = value['peak_heights']
sorted_list = sorted(a, reverse=True)
b = sorted_list[0:4]
b_size = int(len(b))
if b_size < 4:
# replace missing values with zeros
b_missing = 4 - b_size
for x in range(b_missing):
b.append(0)
return b
def autocorr(x):
- """
- Calculates the autocorrelation of a given input signal.
-
- Parameters:
- x (array-like): Input signal for autocorrelation calculation.
-
- Returns:
- array-like: Autocorrelation of the input signal.
- """
result = np.correlate(x, x, mode='full')
return result[len(result)//2:]
def get_autocorr_values(y_values):
- """
- Calculates the autocorrelation values of the given input signal.
-
- Parameters:
- y_values (array-like): The input signal values.
-
- Returns:
- array-like: The autocorrelation peaks of the input signal.
- """
autocorr_values = autocorr(y_values)
peaks = autopeaks(autocorr_values)
peaks = np.asarray(peaks)
return peaks
+# %%
-def wavelet_features(a, w, mode, num_steps):
- """
- Extracts wavelet features from a given signal.
-
- Parameters:
- a (array-like): Input signal.
- w (str or pywt.Wavelet): Wavelet to be used.
- mode (str): Mode of wavelet decomposition.
- num_steps (int): Number of decomposition steps.
-
- Returns:
- numpy.ndarray: Array of wavelet features.
- """
+def wavelet_features(a, w, mode, num_steps):
ca = []
cd = []
for i in range(num_steps + 1):
(a, d) = pywt.dwt(a, w, mode)
ca.append(a)
cd.append(d)
length = len(ca)
Wavelet_vectors = np.empty((0, 22))
for i in range(len(ca)):
signal = ca[i]
W_1 = max(signal)
W_2 = min(signal)
W_3 = np.std(signal)
W_4 = np.mean(signal)
W_5 = np.mean(abs(signal))
W_6 = statistics.harmonic_mean(abs(signal))
W_7 = statistics.median(signal)
W_8 = skew(signal)
W_9 = kurtosis(signal)
W_10 = statistics.variance(signal)
W_11 = np.sqrt(np.mean(np.square(signal)))
signal1 = cd[i]
W_12 = max(signal1)
W_13 = min(signal1)
W_14 = np.std(signal1)
W_15 = np.mean(signal1)
W_16 = np.mean(abs(signal1))
W_17 = statistics.harmonic_mean(abs(signal1))
W_18 = statistics.median(signal1)
W_19 = skew(signal1)
W_20 = kurtosis(signal1)
W_21 = statistics.variance(signal1)
W_22 = np.sqrt(np.mean(np.square(signal1)))
Wavelets = [W_1, W_2, W_3, W_4, W_5, W_6, W_7, W_8, W_9, W_10, W_11,
W_12, W_13, W_14, W_15, W_16, W_17, W_18, W_19, W_20, W_21, W_22]
Wavelet_vectors = np.append(Wavelet_vectors, [Wavelets], axis=0)
return Wavelet_vectors
+# %%
-def waveletenergy(ampl1):
- """
- Calculate the wavelet energy of a given signal.
-
- Parameters:
- ampl1 (array-like): The input signal.
-
- Returns:
- enLev1 (list): List of energies for each level.
- maxDecLev1 (int): Maximum decomposition level.
- wpt1 (ndarray): Array of wavelet packet coefficients.
- """
+def waveletenergy(ampl1):
WAVELET = 'db4'
MODE = 'symmetric'
-
# signal 1
wp = pywt.WaveletPacket(data=ampl1, wavelet=WAVELET, mode=MODE)
maxDecLev1 = wp.maxlevel
#print("Signal length: ",np.shape(ampl1),"Max dec.lev for sign. 1:",maxDecLev1)
wpt = []
nodes = []
nodes.append("a")
nodes.append("d")
sumEnerg = 0
countDecLev = 1
# this are the rel.energies for each level
RelEnrgLev1 = []
while(countDecLev <= maxDecLev1):
#print("Dec.lev:",countDecLev," ,nodes: ",nodes)
tmpNodesLev = []
for n in range(0, len(nodes)):
a = wp[nodes[n]].data # approximations and details
sumCoef = 0
for n in range(0, len(a)):
curValue = abs(a[n])*abs(a[n])
sumCoef = sumCoef+curValue
sumEnerg = sumEnerg+sumCoef
wpt.append(sumCoef)
tmpNodesLev.append(sumCoef) # this we leave for further
RelEnrgLev1.append(tmpNodesLev)
# remake the nodes lists
tmpnodes = []
for b in range(0, len(nodes)):
curNodeApproximations = nodes[b]+"a"
curNodeDetails = nodes[b]+"d"
tmpnodes.append(curNodeApproximations)
tmpnodes.append(curNodeDetails)
nodes = []
nodes = tmpnodes
tmpnodes = []
# increment the decomposition level
countDecLev = countDecLev+1
# this is relative enerrgy bands
for i in range(0, len(wpt)):
wpt[i] = wpt[i]/sumEnerg
wpt1 = np.asarray(wpt)
#print("Size of WPT array for sign.1: ",np.shape(wpt1))
#print("Total energy sign.1: ",sumEnerg," ,max energ.: ",np.max(wpt1)," ,min energ.: ",np.min(wpt1))
# energies for each leveö
enLev1 = []
for k1 in range(0, len(RelEnrgLev1)):
sumedLevEn = 0
for k2 in range(0, len(RelEnrgLev1[k1])):
sumedLevEn = sumedLevEn+RelEnrgLev1[k1][k2]
enLev1.append(sumedLevEn)
return enLev1, maxDecLev1, wpt1
+# %%
+
def waveletpower(psd, band, freqs):
- """
- Calculate the delta power and relative power using wavelet analysis.
-
- Args:
- psd (list): List of power spectral density values.
- band (list): List of frequency bands.
- freqs (list): List of frequencies.
-
- Returns:
- tuple: A tuple containing two lists - waveletpower_deltapower and waveletpower_relativepower.
- - waveletpower_deltapower (list): List of delta power values.
- - waveletpower_relativepower (list): List of relative power values.
- """
-
length = len(band)
waveletpower_deltapower = []
waveletpower_relativepower = []
for i in range(10):
if i <= (len(band)):
ii = i
low = band[ii]
ii = i+1
high = band[ii]
#freq_res = freqs[high] - freqs[low]
idx_delta = np.logical_and(freqs >= low, freqs <= high)
total_power = sum(psd)
delta_power = sum(psd[idx_delta])
delta_rel_power = delta_power / total_power
waveletpower_deltapower.append(delta_power)
waveletpower_relativepower.append(delta_rel_power)
return waveletpower_deltapower, waveletpower_relativepower
+# %%
-def CWTwaveletplot(rawspace, band, sample_rate):
- """
- Perform continuous wavelet transform on the given rawspace signal and extract features.
-
- Parameters:
- rawspace (array-like): The input signal data.
- band (tuple): The frequency band of interest.
- sample_rate (int): The sample rate of the input signal.
- Returns:
- tuple: A tuple containing the extracted features, w1, and w2.
+def CWTwaveletplot(rawspace, band, sample_rate):
- """
windowsize = len(rawspace)
t0 = 0
dt = 1/sample_rate
time = np.arange(0, windowsize) * dt + t0
scales = np.arange(1, 50)
waveletname = 'morl'
data_new = rawspace
data_new = filter(data_new, sample_rate)
dt = time[1] - time[0]
[coefficients, frequencies] = pywt.cwt(data_new, scales, waveletname, dt)
wavenergy = (abs(coefficients)) ** 2
wavefrequencyenergy = wavenergy.sum(axis=1)
w1, w2 = waveletpower(wavefrequencyenergy, band, frequencies)
w1 = np.ravel(w1)
w2 = np.ravel(w2)
wavenergy = np.ravel(wavenergy)
Feature1 = wavenergy.min()
Feature2 = wavenergy.max()
Feature3 = Feature2-Feature1
Feature4 = np.sqrt(np.mean(wavenergy**2))
Feature5 = wavenergy.mean()
Feature6 = wavenergy.std()
Feature7 = statistics.variance(wavenergy)
Feature8 = statistics.harmonic_mean(abs(wavenergy))
Feature9 = statistics.median(wavenergy)
power = [Feature1, Feature2, Feature3, Feature4,
Feature5, Feature6, Feature7, Feature8, Feature9]
power = np.ravel(power)
return power, w1, w2
def Time_frequency_feature(data, sample_rate, band_size, peaks_to_count):
- """
- Extracts time and frequency domain features from the input data.
-
- Args:
- data (numpy.ndarray): Input data array.
- sample_rate (int): Sample rate of the input data.
- band_size (int): Size of the frequency band.
- peaks_to_count (int): Number of peaks to count.
-
- Returns:
- numpy.ndarray: Array of feature vectors.
-
- """
Feature_vectors = []
columns = np.atleast_2d(data).shape[0]
for i in range(columns):
signal_window = data[i, :]
signal_window = filter(signal_window, sample_rate)
# minimum
Feature1 = signal_window.min()
# maximum
Feature2 = signal_window.max()
# difference
Feature3 = Feature2+Feature1
# difference
Feature4 = Feature2+abs(Feature1)
# RMS
Feature5 = np.sqrt(np.mean(signal_window**2))
# print(Feature5)
# STD
Feature6 = statistics.stdev(signal_window)
# Variance
Feature7 = statistics.variance(signal_window)
# Skewness
Feature8 = skew(signal_window)
# Kurtosis
Feature9 = kurtosis(signal_window)
# Mean
Feature10 = statistics.mean(signal_window)
# Harmonic Mean
Feature11 = statistics.harmonic_mean(abs(signal_window))
# Median
Feature12 = statistics.median(signal_window)
# Median_1
Feature13 = Feature12-Feature11
# Zerocrossing
Feature14 = Zerocross(signal_window)
# Mean Absolute Deviation
Feature15 = stats.median_abs_deviation(signal_window)
# Absolute Mean
Feature16 = statistics.mean(abs(signal_window))
# Absolute RMS
Feature17 = np.sqrt(np.mean(abs(signal_window)**2))
# Absolute Max
Feature18 = max(abs(signal_window))
# Absolute Min
Feature19 = min(abs(signal_window))
# Absolute Mean - Mean
Feature20 = ((abs(signal_window)).mean())-(signal_window.mean())
# difference+Median
Feature21 = Feature3+Feature12
# Crest factor - peak/ rms
Feature22 = Feature2/Feature5
# Auto correlation 4 peaks
Feature23 = get_autocorr_values(signal_window)
# Frequency Domain Features
win = 4 * sample_rate
freqs, psd = periodogram(signal_window, sample_rate, window='hamming')
band_max_size = 900000
band = get_band(band_size, band_max_size)
# print(band)
# PSD power in the signal periodgram
Feature27 = sum(psd)
# PSD absolute and relative power in each band 10 Features
Feature28, Feature33 = spectrumpower(psd, band, freqs)
Feature28 = np.asarray(Feature28)
Feature33 = np.asarray(Feature33)
win = 0.0001 * sample_rate
freqs, psd = signal.welch(signal_window, sample_rate, nperseg=win)
# PSD power in the signal Welch
Feature38 = sum(psd)
# Spectral peaks
Feature39 = spectrumpeaks(psd)
Feature39 = np.asarray(Feature39)
# MeanFrequency
Feature40 = meanfrequency(signal_window, sample_rate)
# Wavelet Domain features
enLev1, maxDecLev1, wpt1 = waveletenergy(signal_window)
wav_energy = np.ravel(enLev1)
# DWT statistical features
w = 'db4'
mode = pywt.Modes.smooth
Wavelet_vectors = wavelet_features(signal_window, w, mode, maxDecLev1)
wav_features = np.ravel(Wavelet_vectors)
# CWT statistical features
band = get_band(11, band_max_size)
wav_power, w1, w2 = CWTwaveletplot(signal_window, band, sample_rate)
Feature = [Feature1, Feature2, Feature3, Feature4, Feature5,
Feature6, Feature7, Feature8, Feature9, Feature10, Feature11, Feature12, Feature13, Feature14, Feature15,
Feature16, Feature17, Feature18, Feature19, Feature20, Feature21, Feature22, Feature27, Feature38, Feature40]
Feature_1 = np.concatenate((Feature, Feature23, Feature28, Feature33,
Feature39, wav_features, wav_energy, wav_power, w1, w2))
# Create the size of numpy array, by checking the size of "Feature_1" and creating "Feature_vectors" with the required shape on first run
if i == 0:
# print("--reached")
size_of_Feature_vectors = int(len(Feature_1))
Feature_vectors = np.empty((0, size_of_Feature_vectors))
Feature_vectors = np.append(Feature_vectors, [Feature_1], axis=0)
if i % 10 == 0:
print("Feature_vectors.shape", Feature_vectors.shape)
return Feature_vectors
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0d7c1f6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,57 @@
+# Acoustic emission signature of martensitic transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction
+
+![image](https://github.com/user-attachments/assets/7dbf50af-a350-43b0-b7ca-3c1d1129b30a)
+
+# Journal link
+
+[https://doi.org/10.1016/j.addma.2024.104562](https://doi.org/10.1016/j.addma.2024.104562)
+
+# Overview
+
+This study focuses on investigating Acoustic Emission (AE) monitoring in the Laser Powder Bed Fusion (LPBF) process. Operando X-ray diffraction was conducted to reveal the microstructure changes associated with phase transformations using premixed Ti6Al4V-(x wt%) Fe, where x = 0, 3, and 6. Taking this as a base-line and by employing a structure-borne AE sensor in off-line experiments, we analyze AE data statistically, uncovering notable discrepancies within the 50–750 kHz frequency range. Leveraging Machine Learning (ML) methodologies, we accurately predict composition for particular processing conditions. These fluctuations in AE signals primarily arise from unique microstructural alterations linked to martensitic phase transformation, corroborated by operando synchrotron X-ray diffraction and post-mortem SEM and EBSD analysis. Moreover, cracks are evident at the periphery of the printed parts, stemming from local inadequate heat input during the blending of Ti6Al4V with added Fe powder. These cracks are discerned via AE signals subsequent to the cessation of the laser beam, correlating with the presence of brittle intermetallics at their junction. This study highlights for the first time the potential of AE monitoring in reliably detecting footprints of martensitic transformations during the LPBF process. Additionally, AE is shown to prove valuable for assessing crack formations, particularly in scenarios involving premixed powders and necessitating precise selection of processing parameters, notably at part edges.
+
+![Experimental setup](https://github.com/user-attachments/assets/9ee5e9fa-97bb-4033-b069-ccba22565d0f)
+![Xray](https://github.com/user-attachments/assets/08493967-153b-433f-820e-a8f83a1987a5)
+
+# Experimental procedures
+
+Commercial-grade Ti-6Al-4V ELI (Ti64) powder, sourced from AP&C in Canada, was used as the initial material. The Ti64 powder had a particle size distribution characterized by D90 = 47 µm, D50 = 35 µm, and D10 = 21 µm. To create the pre-mixtures of Ti64–3Fe and Ti64–6Fe, the Ti64 powder was mixed with 3 wt% and 6 wt% of high-purity (99 %) fine Fe particles, respectively. The mixing was carried out in an Ar-sealed tubular mixer for a duration of 2 hr. The Fe powder, sourced from Goodfellow Cambridge Limited, possessed a particle size distribution with D90 = 12.4 µm, D50 = 5.94 µm, and D10 = 2.88 µm. The printing process was performed using a miniaturized LPBF machine under Ar environment with oxygen levels maintained below 0.1 %. Three sets of powder were used for the LPBF process, namely Ti64, Ti64–3Fe, and Ti64–6Fe. For each set, two cuboid geometries (length: 4 mm, and width: 2 mm) were printed with distinct energy densities and their corresponding AE was recorded simultaneously. The energy densities corresponded to CM and KM regimes. The process parameters were chosen to obtain nearly full dense parts and the prints were done on grade 5 Ti64 base plates without any support structures. The selection of the processing parameters was chosen based on the notion of Normalized Enthalpy (NE). A 4 mm unidirectional scanning vector was employed throughout the whole print.The as-built samples were sectioned perpendicular to the laser scanning path along the building direction for microscopy analysis. Electron Backscatter Diffraction (EBSD) characterization was performed using the already described SEM via the Aztec (Oxford Instrument Nanoanalysis) software. EBSD data were taken with a step size of 0.59 µm at 25 kV and 10 nA operation condition. The maps were subsequently processed using an AZtec Crystal (Oxford) plug-in. The parent grains were reconstructed based on the Burgers Orientation Relationships (BORs).
+
+![Microstructure1](https://github.com/user-attachments/assets/a86a46ad-f25a-4c80-8fc3-a45ca7e644db)
+
+It is well-established that solid-state phase transformations generate AE due to the energy release associated with microstructural changes and the related mechanical eventsappropriate representations that could be used for in-situ process monitoring. In displacive transformations, such as martensite formation, the rapid rearrangement of atoms results in a significant energy release, producing intense acoustic signals. Thus, acoustic sensors can detect these waves, offering valuable insights into transformation kinetics by analyzing signal characteristics like amplitude, frequency, and timing. The build plate within the process chamber underwent customization to effectively accommodate the structure-borne AE sensor. Our investigation involves using Convolutional Neural Network (CNN) architectures as automatic feature extractors. This method combines AE data processing with ML algorithms for feature extraction. Contrastive learning is a method employed to train neural networks in discerning positive and negative pairs, with the goal of constructing a feature embedding space where similar instances are grouped closely together while dissimilar ones are positioned farther apart. This approach provides an effective means of automatically deriving feature vectors, which can subsequently be utilized in tandem with ML classifiers. Statistical analysis revealed distinct characteristics for each powder composition and processing parameters. Employing a ML algorithm facilitated the high-accuracy classification of AE signals. Operando X-ray diffraction and post-mortem EBSD characterizations confirmed the effectiveness of in-situ alloying, preventing α’-martensite in KM printing of Ti64–3wt%Fe and Ti64–6wt%Fe. Medium energy density in CM printing was partly effective in Fe mixing, resulting in a mixture of α’ and β phases with varying pro-
+portions for each powder composition. Quasi-correlation between operando X-ray diffraction, EBSD, and wavelet analysis demonstrated the existence of high-frequency components up to 380 kHz in samples undergoing martensitic transformation. Examination of raw AE data revealed events occurring when the
+laser is switched off, with microscopy confirming crack formation at sample edges. These AE signatures, linked to insufficient heat input and brittle intermetallic formation, underscore the importance of careful processing parameter selection for LPBF contours and complex geometries, especially when using premixed powders.
+
+![Spectrum](https://github.com/user-attachments/assets/861a70d7-d44d-42ab-9b25-fa5ea47da7a4)
+
+# Results
+
+![Crack](https://github.com/user-attachments/assets/4b992c5a-9613-4fb2-a629-65c0719d12d5)
+
+
+# Code
+```bash
+git clone https://github.com/vigneashpandiyan/Additive-Manufacturing-Acoustic-Dynamics-of-in-situ-alloying-of-Titanium-Fe
+cd Additive-Manufacturing-Acoustic-Dynamics-of-in-situ-alloying-of-Titanium-Fe
+
+python ../Data_preprocessing/Data_prep.py
+python ../Feature extraction/Main_features.py
+python ../Feature extraction/Main_Features PSD.py
+python ../Crack dynamics/Main_Visualize.py
+python ../Contrastive Loss/Main.py
+
+```
+
+# Citation
+```
+@article{esmaeilzadeh2024acoustic,
+ title={Acoustic emission signature of martensitic transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction},
+ author={Esmaeilzadeh, Reza and Pandiyan, Vigneashwara and Van Petegem, Steven and Van der Meer, Mathijs and Nasab, Milad Hamidi and de Formanoir, Charlotte and Jhabvala, Jamasp and Navarre, Claire and Schlenger, Lucas and Richter, Roland and others},
+ journal={Additive Manufacturing},
+ pages={104562},
+ year={2024},
+ publisher={Elsevier}
+}
+```
+
diff --git a/Visualization/2D Wavelet/2D Wavelets.py b/Visualization/2D Wavelet/2D Wavelets.py
index e0044f7..b3e5f76 100644
--- a/Visualization/2D Wavelet/2D Wavelets.py
+++ b/Visualization/2D Wavelet/2D Wavelets.py
@@ -1,57 +1,57 @@
# -*- coding: utf-8 -*-
"""
@author: srpv
contact: vigneashwara.solairajapandiyan@empa.ch, vigneashpandiyan@gmail.com
The codes in this following script will be used for the publication of the following work
-"Acoustic emission signature of martensitic phase transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction "
+"Acoustic emission signature of martensitic transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction"
@any reuse of this code should be authorized by the first owner, code author
"""
# libraries to import
-
+# libraries to import
import numpy as np
import pandas as pd
import seaborn as sns
from scipy import signal
import os
from Utils import *
# %%
file = os.path.join(os.getcwd(), os.listdir(os.getcwd())[0])
total_path = os.path.dirname(file)
print(total_path)
# %%
sns.set(font_scale=1.5)
sns.set_style("whitegrid", {'axes.grid': False})
sns.set_style("ticks", {"xtick.major.size": 8, "ytick.major.size": 8})
sample_rate = 1500000
windowsize = 5000
t0 = 0
dt = 1/sample_rate
time = np.arange(0, windowsize) * dt + t0
-'''Kindly run the script inside the Dataprep folder'''
-path = r'C:\Users\vigneashwara.p\Desktop\C4Science\lpbf-acoustic-dynamics-of-in-situ-alloying-of-titanium-fe\Data_preprocessing'
+
+path = r'C:\Users\srpv\Desktop\Git\Additive-Manufacturing-Acoustic-Dynamics-of-in-situ-alloying-of-Titanium-Fe\ML classifier\Data_preprocessing'
M = ['KM', 'CM']
for Material in M:
classfile = str(Material)+'_Class_label'+'.npy'
rawfile = str(Material)+'_Rawspace'+'.npy'
classfile = os.path.join(path, classfile)
rawfile = os.path.join(path, rawfile)
classspace = np.load(classfile).astype(np.float64)
classspace = pd.DataFrame(classspace)
rawspace = np.load(rawfile).astype(np.float64)
rawspace = pd.DataFrame(rawspace)
# Frequencyplot(rawspace, classspace, Material)
Wavelet2D(Material, sample_rate, rawspace, classspace, time, total_path)
diff --git a/Visualization/2D Wavelet/Utils.py b/Visualization/2D Wavelet/Utils.py
index eaa9542..e1e1a0c 100644
--- a/Visualization/2D Wavelet/Utils.py
+++ b/Visualization/2D Wavelet/Utils.py
@@ -1,143 +1,153 @@
# -*- coding: utf-8 -*-
"""
@author: srpv
contact: vigneashwara.solairajapandiyan@empa.ch, vigneashpandiyan@gmail.com
The codes in this following script will be used for the publication of the following work
-"Acoustic emission signature of martensitic phase transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction "
+"Acoustic emission signature of martensitic transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction"
@any reuse of this code should be authorized by the first owner, code author
"""
# libraries to import
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from scipy import signal
# import pywt
import pywt
import os
def filter(signal_window, sample_rate):
"""
Applies a low-pass filter to the input signal window.
Args:
signal_window (array-like): The input signal window to be filtered.
sample_rate (float): The sample rate of the input signal.
Returns:
array-like: The filtered signal window.
"""
lowpass = 0.49*sample_rate # Cut-off frequency of the filter
lowpass_freq = lowpass / (sample_rate / 2) # Normalize the frequency
b, a = signal.butter(5, lowpass_freq, 'low')
signal_window = signal.filtfilt(b, a, signal_window)
#plt.plot(t, lowpassfilter, label='Low-Pass')
return signal_window
def Wavelet2D(Material, sample_rate, rawspace, classspace, time, total_path):
"""
Perform 2D wavelet analysis on the given data.
Args:
Material (str): The material name.
sample_rate (int): The sample rate of the data.
rawspace (pd.DataFrame): The raw data.
classspace (pd.DataFrame): The class labels.
time (np.array): The time values.
total_path (str): The path to save the generated plots.
Returns:
None
"""
classspace.columns = ['Categorical']
data = pd.concat([rawspace, classspace], axis=1)
# data = data.sample(frac=1, axis=0).reset_index(drop=True)
print("Respective windows per category", data.Categorical.value_counts())
# minval = min(data.Categorical.value_counts())
minval = 1
print("windows of the class: ", minval)
data = pd.concat([data[data.Categorical == cat].head(minval)
for cat in data.Categorical.unique()])
print("Balanced dataset: ", data.Categorical.value_counts())
rawspace = data.iloc[:, :-1]
rawspace = rawspace.to_numpy()
classspace = data.iloc[:, -1]
classspace = classspace.to_numpy()
waveletname = 'morl'
dt = time[1] - time[0]
- scale = 512
+ scale = 128
scales = np.arange(1, scale)
- Datasets = ['Ti64', 'Ti64-3Fe', 'Ti64-6Fe']
for i in range(len(classspace)):
-
- vmax=12
- vmin=0
- levels = np.linspace(vmin,vmax,50)
print(i)
data = rawspace[i]
data = filter(data, sample_rate)
category = int(classspace[i])
print(category)
plt.rcParams.update(plt.rcParamsDefault)
plt.rcParams['agg.path.chunksize'] = len(data)
# NFFT = 5000
# dt = time[1] - time[0]
[coefficients, frequencies] = pywt.cwt(data, scales, waveletname, dt)
power = (abs(coefficients))
lenthA = len(frequencies)
# frequencies= frequencies[frequencies < lowpass]
lenthB = len(frequencies)
trimlenth = lenthA - lenthB
power = np.delete(power, np.s_[0:trimlenth], axis=0)
# power=np.log2(power)
print(np.min(power))
print(np.max(power))
fig, ax = plt.subplots(figsize=(12, 7))
fig.patch.set_visible(True)
- im = plt.contourf(time, frequencies, power,levels=levels,vmax=vmax,vmin=vmin,cmap=plt.cm.rainbow)
+ im = plt.contourf(time, frequencies, power, cmap=plt.cm.rainbow)
ax.axis('on')
ax.tick_params(axis='both', which='major', labelsize=20)
ax.tick_params(axis='both', which='minor', labelsize=20)
ax.ticklabel_format(axis='x', style='sci', scilimits=(0, 0))
ax.ticklabel_format(axis='y', style='sci', scilimits=(0, 0))
ax.yaxis.offsetText.set_fontsize(20)
ax.xaxis.offsetText.set_fontsize(20)
ax.set_ylim(0, sample_rate/2)
cb = plt.colorbar(im)
- cb.set_label(label='Intensity (a.u)', fontsize=20)
+ cb.set_label(label='Intensity', fontsize=20)
cb.ax.tick_params(labelsize=20)
- plottitle = str(Datasets[i])
+ plottitle = str(Material)+'_'+str(category)
plt.suptitle(plottitle, fontsize=20)
plt.xlabel('Time(sec)', fontsize=20)
plt.ylabel('Frequency(Hz)', fontsize=20)
graphname = str(Material)+'_'+str(category)+'_2D_Wavelet.png'
plt.savefig(os.path.join(total_path, graphname), bbox_inches='tight', dpi=800)
plt.show()
plt.clf()
-
+ plt.figure(figsize=(6, 4))
+ cwtmatr = signal.cwt(data, signal.ricker, scales)
+
+ cwtmatr, _ = pywt.cwt(data, scales, waveletname, dt)
+ plt.ylabel('Scales')
+ plt.xlabel('Time(s)')
+ plottitle = str(Material)+'_'+str(category)
+ plt.suptitle(plottitle, fontsize=10)
+ plt.imshow(cwtmatr, extent=[0, np.max(time), scale, 1], cmap='PRGn', aspect='auto',
+ vmax=abs(cwtmatr).max(), vmin=-abs(cwtmatr).max())
+ ax = plt.gca()
+ ax.invert_yaxis()
+ plt.colorbar()
+ graphname = str(Material)+'_'+str(category)+'_2D_CWT_Wavelet.png'
+ plt.savefig(graphname, bbox_inches='tight', dpi=800)
+ plt.show()
diff --git a/Visualization/Energy Bands/EnergyBands.py b/Visualization/Energy Bands/EnergyBands.py
index 5139f20..17d9ea0 100644
--- a/Visualization/Energy Bands/EnergyBands.py
+++ b/Visualization/Energy Bands/EnergyBands.py
@@ -1,156 +1,149 @@
# -*- coding: utf-8 -*-
"""
@author: srpv
contact: vigneashwara.solairajapandiyan@empa.ch, vigneashpandiyan@gmail.com
The codes in this following script will be used for the publication of the following work
-"Acoustic emission signature of martensitic phase transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction "
+"Acoustic emission signature of martensitic transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction"
@any reuse of this code should be authorized by the first owner, code author
"""
# libraries to import
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import os
import seaborn as sns
from matplotlib import cm
file = os.path.join(os.getcwd(), os.listdir(os.getcwd())[0])
total_path = os.path.dirname(file)
print(total_path)
+
+# %%
sns.set(font_scale=1.5)
sns.set_style("whitegrid", {'axes.grid': False})
sns.set_style("ticks", {"xtick.major.size": 8, "ytick.major.size": 8})
sample_rate = 1500000
windowsize = 5000
t0 = 0
dt = 1/sample_rate
time = np.arange(0, windowsize) * dt + t0
+
path = r'C:\Users\srpv\Desktop\Git\Additive-Manufacturing-Acoustic-Dynamics-of-in-situ-alloying-of-Titanium-Fe\ML classifier\Feature extraction'
# %%
-def boxcomparisonplots(Featurespace, classspace, Material):
- """
- Function to generate box comparison plots.
-
- Parameters:
- - Featurespace (pandas DataFrame): Input feature space.
- - classspace (pandas DataFrame): Input class space.
- - Material (string): Material name.
- Returns:
- - None (plots are generated and saved as images).
- """
+def boxcomparisonplots(Featurespace, classspace, Material):
df2 = pd.DataFrame(classspace)
df2.columns = ['Categorical']
df2 = df2['Categorical'].replace(0, 'Ti64')
df2 = pd.DataFrame(df2)
df2 = df2['Categorical'].replace(1, 'Ti64_3Fe')
df2 = pd.DataFrame(df2)
df2 = df2['Categorical'].replace(2, 'Ti64_6Fe')
classspace = pd.DataFrame(df2)
classspace.columns = ['Categorical']
data = pd.concat([Featurespace, classspace], axis=1)
print("Respective windows per category", data.Categorical.value_counts())
minval = min(data.Categorical.value_counts())
print("windows of the class: ", minval)
data = pd.concat([data[data.Categorical == cat].head(minval)
for cat in data.Categorical.unique()])
print("Balanced dataset: ", data.Categorical.value_counts())
Featurespace = data.iloc[:, :-1]
classspace = data.iloc[:, -1]
Featurespace = Featurespace.to_numpy()
Featurespace = (Featurespace[:, 0:5]).astype(np.float64)
classspace = classspace.to_numpy()
classes = np.unique(classspace)
color = iter(cm.rainbow(np.linspace(0, 1, len(classes))))
values, counts = np.unique(classspace, return_counts=True)
print(values, counts)
c = len(Featurespace)
df1 = pd.DataFrame(Featurespace)
df1 = np.ravel(df1, order='F')
df1 = pd.DataFrame(df1)
df2 = pd.DataFrame(classspace)
df2.columns = ['Categorical']
filename = '0-150 kHZ'
numbers = np.random.randn(c)
df3 = pd.DataFrame({'labels': filename, 'numbers': numbers})
df3 = df3.drop(['numbers'], axis=1)
filename = '150-300 kHZ'
numbers = np.random.randn(c)
df4 = pd.DataFrame({'labels': filename, 'numbers': numbers})
df4 = df4.drop(['numbers'], axis=1)
filename = '300-450 kHZ'
numbers = np.random.randn(c)
df5 = pd.DataFrame({'labels': filename, 'numbers': numbers})
df5 = df5.drop(['numbers'], axis=1)
filename = '450-600 kHZ'
numbers = np.random.randn(c)
df6 = pd.DataFrame({'labels': filename, 'numbers': numbers})
df6 = df6.drop(['numbers'], axis=1)
filename = '600-750 kHZ'
numbers = np.random.randn(c)
df7 = pd.DataFrame({'labels': filename, 'numbers': numbers})
df7 = df7.drop(['numbers'], axis=1)
Energyband = np.concatenate((df3, df4, df5, df6, df7), axis=0)
Modes = np.concatenate((df2, df2, df2, df2, df2), axis=0)
Energybands = np.concatenate((df1, Energyband, Modes), axis=1)
Energybands = pd.DataFrame(Energybands)
Energybands.columns = ['Frequency distribution', 'Frequency levels', 'Categorical']
plt.rcParams.update(plt.rcParamsDefault)
plt.figure(figsize=(7, 5))
sns.set(font_scale=3.5)
sns.set_style("whitegrid", {'axes.grid': False})
ax = sns.catplot(y="Frequency levels", x="Frequency distribution", hue="Categorical", kind="bar", data=Energybands, height=12,
aspect=1.8, palette=color)
ax.set_xticklabels(rotation=0)
ax.tick_params(axis='both', which='major', labelsize=25)
ax.tick_params(axis='both', which='minor', labelsize=25)
ax._legend.remove()
plt.legend(loc='lower right', frameon=False, fontsize=40)
# plt.ticklabel_format(axis='y', style='sci',scilimits=(0,0))
plt.title('Frequency distribution', fontsize=50)
plotname = str(Material)+"_Frequency distribution"+'.png'
plt.savefig(plotname, dpi=800, bbox_inches='tight')
plt.show()
# %%
M = ['KM', 'CM']
for Material in M:
classfile = str(Material)+'_Class_label'+'.npy'
featurefile = str(Material)+'_PSD'+'.npy'
classfile = os.path.join(path, classfile)
featurefile = os.path.join(path, featurefile)
classspace = np.load(classfile).astype(np.float64)
classspace = pd.DataFrame(classspace)
featurespace = np.load(featurefile).astype(np.float64)
featurespace = pd.DataFrame(featurespace)
boxcomparisonplots(featurespace, classspace, Material)
diff --git a/Visualization/Energy Bands/Visualization.py b/Visualization/Energy Bands/Visualization.py
index 81dd5cf..8ae4562 100644
--- a/Visualization/Energy Bands/Visualization.py
+++ b/Visualization/Energy Bands/Visualization.py
@@ -1,164 +1,153 @@
# -*- coding: utf-8 -*-
"""
@author: srpv
contact: vigneashwara.solairajapandiyan@empa.ch, vigneashpandiyan@gmail.com
The codes in this following script will be used for the publication of the following work
-"Acoustic emission signature of martensitic phase transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction "
+"Acoustic emission signature of martensitic transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction"
@any reuse of this code should be authorized by the first owner, code author
"""
# libraries to import
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import os
+from IPython.display import Image
+
+from mpl_toolkits.mplot3d import Axes3D
+from matplotlib import animation
from matplotlib.pyplot import specgram
import seaborn as sns
from scipy.stats import norm
-#%%
+
+from matplotlib import cm
+from scipy import signal
+import pywt
+import matplotlib.patches as mpatches
+from matplotlib import colors
+# %%
sns.set(font_scale=1.3)
sns.set_style("whitegrid", {'axes.grid': False})
sns.set_style("ticks", {"xtick.major.size": 8, "ytick.major.size": 8})
sample_rate = 1500000
windowsize = 5000
+
+
total_path = r'C:\Users\srpv\Desktop\Git\Additive-Manufacturing-Acoustic-Dynamics-of-in-situ-alloying-of-Titanium-Fe\ML classifier\Feature extraction'
print(total_path)
-# %%
-
-def Histplotsplit(data, Material, name):
- """
- Function to plot histogram and density plots for different categories in the data.
+# %%
- Parameters:
- - data (pandas DataFrame): Input data containing the feature values and categorical labels.
- - Material (str): Material name.
- - name (str): Name of the feature.
- Returns:
- - None
- """
+def Histplotsplit(data, Material, name):
df = data
fig, axes = plt.subplots(1, 3, figsize=(20, 6), dpi=800, sharex=False, sharey=True)
sns.set(font_scale=1.5)
colors = ['#2171b5', '#980043', '#006d2c']
plt.rcParams.update(plt.rcParamsDefault)
for i in range(3):
print(i)
if i == 0:
j = 0
elif i == 1:
j = 1
elif i == 2:
j = 2
print("new value: ", j)
x = df.loc[df.categorical == df.categorical.unique()[i], 'Feature']
p = sns.kdeplot(x, shade=True, alpha=.5, color=colors[i], label=str(
df.categorical.unique()[i]), ax=axes[j], edgecolors="k")
p.set_xlabel(str(name)+" (AE energy)", fontsize=20)
p.set_ylabel("Relative density", fontsize=20)
ax = axes[j]
ax.tick_params(axis='both', which='major', labelsize=25)
ax.tick_params(axis='both', which='minor', labelsize=25)
ax.xaxis.set_major_locator(plt.MaxNLocator(3))
ax.yaxis.set_major_locator(plt.MaxNLocator(3))
# ax.legend(fontsize=30,bbox_to_anchor=(0.95, 1.50))
plt.ylabel("Relative frequency")
plt.rc("font", size=23)
plt.tight_layout()
plot = str(Material)+'_'+str(name)+'_Histplotsplit.jpg'
plt.savefig(plot, bbox_inches='tight', dpi=800)
plt.show()
# %%
-def plots(i, Featurespace, classspace, Material, name):
- """
- This function generates plots based on the input data.
-
- Parameters:
- i (int): The index of the data to be plotted.
- Featurespace (numpy.ndarray): The feature space data.
- classspace (numpy.ndarray): The class space data.
- Material (str): The material name.
- name (str): The name of the plot.
-
- Returns:
- pandas.DataFrame: The concatenated data used for plotting.
- """
+def plots(i, Featurespace, classspace, Material, name):
Featurespace = Featurespace.transpose()
data = (Featurespace[i])
data = data.astype(np.float64)
#data= abs(data)
df1 = pd.DataFrame(data)
df1.rename(columns={df1.columns[0]: "Feature"}, inplace=True)
df2 = pd.DataFrame(classspace)
df2 = pd.DataFrame(classspace)
df2.columns = ['Categorical']
df2 = df2['Categorical'].replace(0, 'Ti64')
df2 = pd.DataFrame(df2)
df2 = df2['Categorical'].replace(1, 'Ti64_3Fe')
df2 = pd.DataFrame(df2)
df2 = df2['Categorical'].replace(2, 'Ti64_6Fe')
df2 = pd.DataFrame(df2)
df2.rename(columns={df2.columns[0]: "categorical"}, inplace=True)
data = pd.concat([df1, df2], axis=1)
Histplotsplit(data, Material, name)
return data
# %%
# 4#7
M = ['KM', 'CM']
for Material in M:
classfile = str(Material)+'_Class_label'+'.npy'
featurefile = str(Material)+'_PSD'+'.npy'
featurefile_1 = (os.path.join(total_path, featurefile))
classfile_1 = (os.path.join(total_path, classfile))
Featurespace = np.load(featurefile_1).astype(np.float64)
classspace = np.load(classfile_1).astype(np.float64)
Featurespace = pd.DataFrame(Featurespace)
classspace = pd.DataFrame(classspace)
classspace.columns = ['Categorical']
data = pd.concat([Featurespace, classspace], axis=1)
minval = min(data.Categorical.value_counts())
data = pd.concat([data[data.Categorical == cat].head(minval)
for cat in data.Categorical.unique()])
Featurespace = data.iloc[:, :-1]
classspace = data.iloc[:, -1]
values, counts = np.unique(classspace, return_counts=True)
print(values, counts)
classspace = classspace.to_numpy()
Featurespace = Featurespace.to_numpy()
data = plots(0, Featurespace, classspace, Material, "0-150 kHz")
data = plots(1, Featurespace, classspace, Material, "150-300 kHz")
data = plots(2, Featurespace, classspace, Material, "300-450 kHz")
data = plots(3, Featurespace, classspace, Material, "450-600 kHz")
data = plots(4, Featurespace, classspace, Material, "600-750 kHz")
diff --git a/Visualization/FFT/FFT.py b/Visualization/FFT/FFT.py
index 03a90e2..14ae250 100644
--- a/Visualization/FFT/FFT.py
+++ b/Visualization/FFT/FFT.py
@@ -1,180 +1,152 @@
# -*- coding: utf-8 -*-
"""
@author: srpv
contact: vigneashwara.solairajapandiyan@empa.ch, vigneashpandiyan@gmail.com
The codes in this following script will be used for the publication of the following work
-"Acoustic emission signature of martensitic phase transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction "
+"Acoustic emission signature of martensitic transformation in Laser Powder Bed Fusion of Ti6Al4V-Fe, supported by operando X-ray diffraction"
@any reuse of this code should be authorized by the first owner, code author
"""
# libraries to import
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib import cm
from scipy import signal
import matplotlib.patches as mpatches
import os
# %%
+
file = os.path.join(os.getcwd(), os.listdir(os.getcwd())[0])
total_path = os.path.dirname(file)
print(total_path)
+
+# %%
sns.set(font_scale=1.5)
sns.set_style("whitegrid", {'axes.grid': False})
sns.set_style("ticks", {"xtick.major.size": 8, "ytick.major.size": 8})
sample_rate = 1500000
windowsize = 5000
t0 = 0
dt = 1/sample_rate
time = np.arange(0, windowsize) * dt + t0
Material = "KM"
+path = r'C:\Users\srpv\Desktop\Git\Additive-Manufacturing-Acoustic-Dynamics-of-in-situ-alloying-of-Titanium-Fe\ML classifier\Data_preprocessing'
-'''Kindly run the script inside the Dataprep folder'''
-
-path = r'C:\Users\vigneashwara.p\Desktop\C4Science\lpbf-acoustic-dynamics-of-in-situ-alloying-of-titanium-fe\Data_preprocessing'
-
-#%%
def filter(signal_window):
- """
- Apply a lowpass filter to the input signal window.
-
- Args:
- signal_window (array-like): The input signal window to be filtered.
-
- Returns:
- array-like: The filtered signal window.
- """
-
+
lowpass = sample_rate*0.49 # Cut-off frequency of the filter
lowpass_freq = lowpass / (sample_rate / 2) # Normalize the frequency
b, a = signal.butter(5, lowpass_freq, 'low')
signal_window = signal.filtfilt(b, a, signal_window)
return signal_window
+# %%
+
def Frequencyplot(rawspace, classspace, Material):
- """
- This function generates frequency plots for each category in the dataset.
-
- Parameters:
- rawspace (pandas.DataFrame): The raw data.
- classspace (pandas.DataFrame): The categorical data.
- Material (str): The material name.
-
- Returns:
- pandas.DataFrame: The balanced dataset.
- """
classspace.columns = ['Categorical']
data = pd.concat([rawspace, classspace], axis=1)
- data = data.sample(frac=1)
print("Respective windows per category", data.Categorical.value_counts())
# minval = min(data.Categorical.value_counts())
minval = 1
print("windows of the class: ", minval)
data = pd.concat([data[data.Categorical == cat].head(minval)
for cat in data.Categorical.unique()])
print("Balanced dataset: ", data.Categorical.value_counts())
rawspace = data.iloc[:, :-1]
rawspace = rawspace.to_numpy()
classspace = data.iloc[:, -1]
classspace = classspace.to_numpy()
-
- Datasets = ['Ti64', 'Ti64-3Fe', 'Ti64-6Fe']
for i in range(len(classspace)):
- # print(i)
+ print(i)
- data_ = rawspace[i]
- data_ = filter(data_)
+ data = rawspace[i]
+ data = filter(data)
category = int(classspace[i])
print(category)
# Define window length (4 seconds)
win = 0.1 * sample_rate
- freqs, psd = signal.welch(data_, sample_rate, nperseg=win)
+ freqs, psd = signal.welch(data, sample_rate, nperseg=win)
# Plot the power spectrum
- sns.set(font_scale=1.2, style='white')
- plt.figure(figsize=(7, 4), dpi=400)
- plt.plot(freqs, psd, color='k', lw=0.1)
+ sns.set(font_scale=1.5, style='white')
+ plt.figure(figsize=(7, 4), dpi=200)
+ plt.plot(freqs, psd, color='k', lw=0.2)
sec1, sec2 = 0, 150000
sec3, sec4 = 150000, 300000
sec5, sec6 = 300000, 450000
sec7, sec8 = 450000, 600000
sec9, sec10 = 600000, 750000
# Find intersecting values in frequency vector
idx_delta1 = np.logical_and(freqs >= sec1, freqs <= sec2)
idx_delta2 = np.logical_and(freqs >= sec3, freqs <= sec4)
idx_delta3 = np.logical_and(freqs >= sec5, freqs <= sec6)
idx_delta4 = np.logical_and(freqs >= sec7, freqs <= sec8)
idx_delta5 = np.logical_and(freqs >= sec9, freqs <= sec10)
plt.xlabel('Frequency (Hz)')
plt.ylabel('Power spectral density')
ynumber = max(psd) + (0.1*max(psd))
-
- print("ylimit",ynumber)
-
+ plt.ylim([0, ynumber])
plt.fill_between(freqs, psd, where=idx_delta1, color='blue')
plt.fill_between(freqs, psd, where=idx_delta2, color='red')
plt.fill_between(freqs, psd, where=idx_delta3, color='yellow')
plt.fill_between(freqs, psd, where=idx_delta4, color='green')
plt.fill_between(freqs, psd, where=idx_delta5, color='orange')
#plottitle='Tribo'+'_'+'Welchs periodogram'+'_'+State
- plottitle = str(Datasets[i])
+ plottitle = str(Material)+'_'+str(category)
plt.title(plottitle)
- # plt.xlim([0, freqs.max()])
+ plt.xlim([0, freqs.max()])
plt.xlim([0, sample_rate*0.49])
- plt.ylim([0, 0.0004])
skyblue = mpatches.Patch(color='blue', label='0-150 kHz')
plt.legend(handles=[skyblue])
red = mpatches.Patch(color='red', label='150-300 kHz')
plt.legend(handles=[red])
yellow = mpatches.Patch(color='yellow', label='300-450 kHz')
plt.legend(handles=[yellow])
green = mpatches.Patch(color='green', label='450-600 kHz')
plt.legend(handles=[green])
cyan = mpatches.Patch(color='orange', label='600-750 kHz')
# plt.legend(handles=[skyblue, red, yellow, green, cyan], prop={'size': 20})
plt.legend(handles=[skyblue, red, yellow, green, cyan])
plt.ticklabel_format(axis='x', style='sci', scilimits=(0, 0))
plt.ticklabel_format(axis='y', style='sci', scilimits=(0, 0))
graph_1 = str(Material)+'_'+str(category)+'.png'
plt.savefig(graph_1, bbox_inches='tight', dpi=800)
plt.show()
-
- return data
# %%
M = ['KM', 'CM']
-
for Material in M:
classfile = str(Material)+'_Class_label'+'.npy'
rawfile = str(Material)+'_Rawspace'+'.npy'
classfile = os.path.join(path, classfile)
rawfile = os.path.join(path, rawfile)
classspace = np.load(classfile).astype(np.float64)
classspace = pd.DataFrame(classspace)
rawspace = np.load(rawfile).astype(np.float64)
rawspace = pd.DataFrame(rawspace)
- data=Frequencyplot(rawspace, classspace, Material)
+ Frequencyplot(rawspace, classspace, Material)

Event Timeline