Commit b0be70b7 authored by Michael Rudolf's avatar Michael Rudolf
Browse files

Finish Restructuring.

- Transferred most code from `RST_pick_GUI` into separate files.
- Fixed a minor bug in the mutual linear regression.
- Instead of a `Var`dictionary we now use the full configparser.
- Moved nested functions into dedicated sections to make things easier.
- If TDMS files are analysed, they are now first checked for size and downsampled if necessary.
- Added a few test scripts for testing code.
parent a811c580
# -*- coding: utf-8 -*-
"""
Converts all *.tdms files in the folder given in `path_in` and creates a
velocity stepping plot for each of them in the folder `path_out`. This script
requires `rstevaluation` to be installed on the system.
@author: M.Rudolf
"""
import os
import numpy as np
import rstevaluation.tools.analysis as rstanalysis
import rstevaluation.tools.files as rstfiles
import rstevaluation.tools.plots as rstplots
def main():
projectname = '393-01_01_Foamglass100'
path_in = 'Legacy/FileConversion/input'
path_out = 'Legacy/FileConversion/output'
# =========== PARAMETERS & VARIABLES ============
var = {
'A': 0.022619, # area of shear zone (= surface area of lid) (m^2)
'li': 0.0776, # inner lever (center cell to center of shear zone)
'lo': 0.1250, # outer lever (center cell to hinge point)
'le': 1.611655493, # load point level
'v': 30, # shear velocity (mm/min)
'g': 9.81, # acceleration due to gravity
'prec': 125, # rounding precision for normal load in file names
'velnoise': 1e-2,
'stressnoise': 0.025,
'smoothwindow': 51, # define window for smooth shear stress curve
'nsamples': 10**6 # Maximum number of samples for downsampling
}
exp_data = [
rstfiles.convert(path_in, f, var)
for f in os.listdir(path_in)
if f.endswith('.tdms')
]
normal_stresses = [
'%i' % (np.floor(np.mean(d['normalstress'])/var['prec'])*var['prec'])
for d in exp_data
]
for exp in exp_data:
pfit, perr, name_fit = rstanalysis.vst_analysis(exp)
rstplots.plotVST(path_out, exp, pfit, perr, name_fit)
rstfiles.saveTS(path_out, projectname, exp_data, normal_stresses)
if __name__ == '__main__':
main()
# -*- coding: utf-8 -*-
"""
Converts all *.tdms files in the folder given in `path_in` and creates a
velocity stepping plot for each of them in the folder `path_out`. This script
requires `rstevaluation` to be installed on the system.
@author: M.Rudolf
"""
import os
import numpy as np
import rstevaluation.tools.analysis as rstanalysis
import rstevaluation.tools.files as rstfiles
import rstevaluation.tools.plots as rstplots
def main():
projectname = '393-01_01_Foamglass100'
path_in = 'Legacy/FileConversion/input'
path_out = 'Legacy/FileConversion/output'
# =========== PARAMETERS & VARIABLES ============
var = {
'A': 0.022619, # area of shear zone (= surface area of lid) (m^2)
'li': 0.0776, # inner lever (center cell to center of shear zone)
'lo': 0.1250, # outer lever (center cell to hinge point)
'le': 1.611655493, # load point level
'v': 30, # shear velocity (mm/min)
'g': 9.81, # acceleration due to gravity
'prec': 125, # rounding precision for normal load in file names
'velnoise': 1e-2,
'stressnoise': 0.025,
'smoothwindow': 51, # define window for smooth shear stress curve
'nsamples': 10**6 # Maximum number of samples for downsampling
}
exp_data = [
rstfiles.convert(path_in, f, var)
for f in os.listdir(path_in)
if f.endswith('.tdms')
]
normal_stresses = [
'%i' % (np.floor(np.mean(d['normalstress'])/var['prec'])*var['prec'])
for d in exp_data
]
for exp in exp_data:
pfit, perr, name_fit = rstanalysis.vst_analysis(exp)
rstplots.plotVST(path_out, exp, pfit, perr, name_fit)
rstfiles.saveTS(path_out, projectname, exp_data, normal_stresses)
if __name__ == '__main__':
main()
......@@ -12,549 +12,18 @@ GUI based approach for RSTpicking
"""
import configparser
import json
import logging
import os
import re
import subprocess
import sys
import tkinter as tk
import warnings
from pathlib import Path
from tkinter import filedialog, messagebox
import rstevaluation.tools.analysis as rstanalysis
import rstevaluation.tools.files as rstfiles
import rstevaluation.tools.picking as rstpicking
import rstevaluation.tools.plots as rstplots
warnings.filterwarnings("ignore")
log = logging.getLogger()
log.setLevel(logging.INFO)
class RST_pick_GUI(tk.Tk):
"""Main Window for the RST Picking"""
def __init__(self, parent):
tk.Tk.__init__(self, parent)
# == VARIABLES
self.parent = parent
self.projectname = tk.StringVar()
self.path_in = tk.StringVar()
self.path_out = tk.StringVar()
self.path_cfg = tk.StringVar()
self.proc = tk.StringVar()
self.plot_ts = tk.IntVar()
self.save_ts = tk.IntVar()
self.rev_pick = tk.IntVar()
self.is_VST = tk.IntVar()
self.pred_norm = tk.IntVar()
self.rst = tk.IntVar()
self.cfg = configparser.ConfigParser()
self.cfg.optionxform = str
self.path_cfg.set('rst_evaluation_')
self.params = dict()
self.protocol('WM_DELETE_WINDOW', self.closing_menu)
# Initialize Object
self.initalize()
def initalize(self):
# Read or create config and set entries from config
self.refresh_entries_from_cfg(init=True)
# == WIDGETS and INTERFACE ==
self.grid()
# Project name
self.label_projname = tk.Label(self,
text='Project:',
anchor='e',
width=20)
self.label_projname.grid(column=0, row=0, sticky='EW')
self.entry_projname = tk.Entry(self,
textvariable=self.projectname)
self.entry_projname.grid(column=1, row=0, sticky='EW')
# Input path
self.label_input = tk.Label(self,
text='Input Folder:',
anchor='e')
self.label_input.grid(column=0, row=1, sticky='EW')
self.entry_input = tk.Entry(self,
width=80,
textvariable=self.path_in)
self.entry_input.grid(column=1, row=1, sticky='EW')
self.button_input = tk.Button(self,
text='...')
self.button_input.grid(column=2, row=1)
self.button_input.bind('<ButtonRelease-1>', self.folder_browser)
# Output path
self.label_output = tk.Label(self,
text='Output Folder:',
anchor='e')
self.label_output.grid(column=0, row=2, sticky='EW')
self.entry_output = tk.Entry(self,
textvariable=self.path_out)
self.entry_output.grid(column=1, row=2, sticky='EW')
self.button_output = tk.Button(self,
text='...')
self.button_output.grid(column=2, row=2)
self.button_output.bind('<ButtonRelease-1>', self.folder_browser)
# Options
self.label_options = tk.Label(self,
text='Options:',
anchor='w',
font=('Helvetica', 12, 'bold'))
self.label_options.grid(column=0, row=3, columnspan=3, sticky='EW')
# Pick Data
self.opt_pick = tk.Checkbutton(self,
text='Automatic Picking',
anchor='w',
variable=self.rst)
self.opt_pick.grid(column=0, row=4, sticky='EW')
# Save Data
self.opt_save = tk.Checkbutton(self,
text='Save Data',
anchor='w',
variable=self.save_ts)
self.opt_save.grid(column=0, row=5, sticky='EW')
# Plot Data
self.opt_plot = tk.Checkbutton(self,
text='Plot time series',
anchor='w',
variable=self.plot_ts)
self.opt_plot.grid(column=0, row=6, sticky='EW')
# Review Picking
self.opt_revpick = tk.Checkbutton(self,
text='Review picking during run',
anchor='w',
variable=self.rev_pick)
self.opt_revpick.grid(column=1, row=4, sticky='EW')
# Use predefined normal stress
self.opt_pred = tk.Checkbutton(self,
text='Use predefined normal stress',
anchor='w',
variable=self.pred_norm)
self.opt_pred.grid(column=1, row=5, sticky='EW')
# Is a VST-Test
self.opt_revpick = tk.Checkbutton(self,
text='VST-Test',
anchor='w',
variable=self.is_VST)
self.opt_revpick.grid(column=1, row=6, sticky='EW')
# More Options
self.button_options = tk.Button(self,
text='More options...')
self.button_options.grid(column=1, row=7, sticky='w')
self.button_options.bind('<ButtonRelease-1>', self.more_options)
# Start processing
self.button_start = tk.Button(self,
text='Start Processing',
padx=20)
self.button_start.grid(column=0, row=7, columnspan=3, sticky='w')
self.button_start.bind('<ButtonRelease-1>', self.start_processing)
self.make_dpi_aware()
def get_HWND_dpi(self):
"""Detects high dpi displays and rescales gui in Windows
Adapted from the user 'dingles at stack-overflow"""
if os.name == "nt":
from ctypes import pointer, windll, wintypes
try:
windll.shcore.SetProcessDpiAwareness(1)
except Exception:
logging.info('High-DPI scaling failed')
pass # this will fail on Win Server and maybe early Windows
DPI100pc = 96 # DPI 96 is 100% scaling
DPI_type = 0
# MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2
winH = wintypes.HWND(self.winfo_id())
monitorhandle = windll.user32.MonitorFromWindow(winH,
wintypes.DWORD(2))
# MONITOR_DEFAULTTONEAREST = 2
X = wintypes.UINT()
Y = wintypes.UINT()
try:
windll.shcore.GetDpiForMonitor(monitorhandle,
DPI_type,
pointer(X),
pointer(Y))
return X.value, Y.value, (X.value + Y.value) / (2 * DPI100pc)
except Exception:
logging.info('Assuming standard dpi scaling.')
return 96, 96, 1 # Assume standard Windows DPI & scaling
else:
return None, None, 1 # What to do for other OSs?
def make_dpi_aware(self):
"""Adapted from the user 'dingles at stack-overflow"""
def TkGeometryScale(s, cvtfunc):
# format "WxH+X+Y"
patt = r"(?P<W>\d+)x(?P<H>\d+)\+(?P<X>\d+)\+(?P<Y>\d+)"
R = re.compile(patt).search(s)
G = str(cvtfunc(R.group("W"))) + "x"
G += str(cvtfunc(R.group("H"))) + "+"
G += str(cvtfunc(R.group("X"))) + "+"
G += str(cvtfunc(R.group("Y")))
return G
self.DPI_X, self.DPI_Y, self.DPI_scaling = self.get_HWND_dpi()
self.TkScale = lambda v: int(float(v) * self.DPI_scaling)
self.TkGeometryScale = lambda s: TkGeometryScale(s, self.TkScale)
def resize_all_elements(self):
"""
Resizes all elements according to the DPI scaling of the window
(WIP)
-----
"""
pass
def plot_window(self, event):
"""Shows a plot window"""
plot_win = tk.Toplevel(self)
plot_win.wm_title('Plot Window')
return
# == CALLBACKS ==
def folder_browser(self, event):
"""Callback for folderbrowser."""
f_path = filedialog.askdirectory()
if event.widget._name == '!button': # Input path
self.entry_input.delete(0, 'end')
self.entry_input.insert(0, f_path + '/')
self.entry_output.delete(0, 'end')
self.entry_output.insert(0, f_path + '/')
self.entry_projname.delete(0, 'end')
pname = f_path.split('/')[-1]
self.entry_projname.insert(0, pname)
self.path_cfg.set(f_path + '/' + pname + '.cfg')
self.refresh_entries_from_cfg(path=f_path + '/' + pname + '.cfg')
elif event.widget._name == '!button2': # Output path
self.entry_output.delete(0, 'end')
self.entry_output.insert(0, f_path + '/')
else:
print(event.widget._name)
def start_processing(self, event):
"""Initiates Processing of data"""
# Create Var dictionary and save to config:
Vars = dict()
for item in self.params:
try:
Vars[item] = float(self.params[item].get())
except ValueError:
Vars[item] = json.loads(self.params[item].get())
self.cfg['parameters'][item] = self.params[item].get()
self.cfg['options']['plot_ts'] = str(self.plot_ts.get())
self.cfg['options']['save_ts'] = str(self.save_ts.get())
self.cfg['options']['rst'] = str(self.rst.get())
self.cfg['options']['rev_pick'] = str(self.rev_pick.get())
self.cfg['options']['is_VST'] = str(self.is_VST.get())
self.cfg['options']['pred_norm'] = str(self.pred_norm.get())
Vars['options'] = dict()
for opt in self.cfg['options'].keys():
Vars['options'][opt] = self.cfg['options'][opt]
with open(self.path_cfg.get(), 'w') as f:
self.cfg.write(f)
# Other Variables
path_in = self.path_in.get()
path_out = self.path_out.get()
projectname = self.projectname.get()
# Generate a list containing all files
supported_filetypes = ['.asc', '.dat', '.tdms']
file_list = [
f for f in os.listdir(path_in)
if os.path.splitext(f)[-1] in supported_filetypes
]
# Data is stored as a list of dictionaries containing the data
exp_data = [rstfiles.convert(path_in, f, Vars) for f in file_list]
if self.is_VST.get() == 0:
if self.rst.get() == 1:
# Evaluate all data sets and store results in a list
if self.rev_pick.get() == 1:
eval_data = [
rstpicking.eval_shearstress(
cur_dat,
Vars,
review='auto'
)
for cur_dat in exp_data
]
else:
eval_data = [
rstpicking.eval_shearstress(cur_dat, Vars)
for cur_dat in exp_data
]
print('Automatic analysis successful')
else:
print('Automatic analysis skipped, initiating manual picking')
eval_data = [
rstpicking.eval_shearstress(cur_dat, Vars, review='manual')
for cur_dat in exp_data
]
# Create lists of parameters for analysis
normal_stress = [ev[0] for ev in eval_data]
peak_stress = [ev[1][1] for ev in eval_data]
dyn_stress = [ev[2][1] for ev in eval_data]
stat_stress = [ev[3][1] for ev in eval_data]
# dilation = [ev[4] for ev in eval_data]
weak = [ev[5] for ev in eval_data]
weak_p = [ev[6] for ev in eval_data]
p_ind = [ev[7] for ev in eval_data]
# ==========ANALYSIS OF FRICTION COEFFICIENTS AND COHESIONS=======
# ===========Analysis 1: Mutual linear regression=================
peakfric_mut, peakdata_mut = rstanalysis.rst_analmut(
normal_stress,
peak_stress
)
dynfric_mut, dyndata_mut = rstanalysis.rst_analmut(
normal_stress,
dyn_stress
)
reactfric_mut, reactdata_mut = rstanalysis.rst_analmut(
normal_stress,
stat_stress
)
# ===========ANALYSIS 2: Standard regression of all data pairs ==
peakfric_std = rstanalysis.rst_analstd(normal_stress, peak_stress)
dynfric_std = rstanalysis.rst_analstd(normal_stress, dyn_stress)
reactfric_std = rstanalysis.rst_analstd(normal_stress, stat_stress)
# ==========MERGE DATA===========================================
strength = (normal_stress, peak_stress, dyn_stress,
stat_stress, weak, weak_p)
fric_mut = (peakfric_mut, dynfric_mut, reactfric_mut)
data_mut = (peakdata_mut, dyndata_mut, reactdata_mut)
fric_std = (peakfric_std, dynfric_std, reactfric_std)
# ===================PLOT DATA=====================================
rstplots.plotstd(path_out, projectname, strength, fric_std)
rstplots.plothist(path_out, projectname, strength, data_mut)
print('>>> Friction data plotted')
if self.plot_ts.get() == 1:
rstplots.plotts(path_out, projectname, exp_data, normal_stress)
print('>>> Time series data plotted')
# ====================Save DATA===================================
if self.rst.get() == 1:
rstfiles.saveStrength(path_out, projectname, strength)
rstfiles.saveFric(path_out, projectname, fric_mut, fric_std)
# lid_pos = rstfiles.savelidpos(
# path_out,
# projectname,
# p_ind,
# exp_data
# )
print('>>> Friction data saved')
# prop_file = [
# path_in + f
# for f in os.listdir(path_in)
# if f.endswith('csv')
# ]
# if prop_file:
# print(
# '>>> Prop file detected! Starting dilation analysis...'
# )
# rfnc.dilation(
# path_out, projectname,
# lid_pos, exp_data,
# prop_file[0], self.cfg
# )
# else:
# print('>>> There is no prop file! Going on as usual.')
if self.save_ts.get() == 1:
rstfiles.saveTS(path_out, projectname, exp_data, normal_stress)
print('>>> Time series data saved')
else:
print('Velocity Stepping Test')
for exp in exp_data:
pfit, perr, name_fit = rstanalysis.vst_analysis(exp)
rstplots.plotVST(path_out, exp, pfit, perr, name_fit)
if sys.platform == "linux" or sys.platform == "linux2":
p = subprocess.Popen(
["xdg-open", "%s" % path_out],
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
p.communicate()
elif sys.platform == "darwin":
p = subprocess.Popen(
["open", "%s" % path_out],
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
p.communicate()
elif sys.platform == "win32":
os.startfile(path_out) # pylint: disable=no-member
def closing_menu(self):
"""Shows a confirmation dialogue for closing"""
if messagebox.askyesno('Close Program', 'Quit?'):
sys.exit(0)
def more_options(self, event):
"""Shows all additional options for the processing"""
opt_dlg = tk.Toplevel(self)
opt_dlg.wm_title('More Options')
opt_dlg.grid()
opt_dlg.label_options = tk.Label(opt_dlg,
text='Options:')
opt_dlg.label_options.grid(column=0, row=0, sticky='EW')
# Dynamically generate options based on config entries
for (i, item) in enumerate(self.cfg['parameters'].items()):
opt_dlg.label_item = tk.Label(opt_dlg,
text=item[0] + ':')
opt_dlg.label_item.grid(column=0, row=i + 1, sticky='e')
opt_dlg.entry_item = tk.Entry(opt_dlg,
textvariable=self.params[item[0]],
justify='right',
width=45)
opt_dlg.entry_item.grid(column=1, row=i + 1, sticky='EW')
opt_dlg.label_unit = tk.Label(opt_dlg,
text=self.cfg['units'][item[0]])
opt_dlg.label_unit.grid(column=2, row=i + 1, sticky='w')
def set_data(self):
''' Set contents of fields and variables according to config '''
self.projectname.set(self.cfg['paths']['projectname'])
self.path_in.set(self.cfg['paths']['path_in'])
self.path_out.set(self.cfg['paths']['path_out'])
self.path_cfg.set(self.cfg['paths']['path_cfg'])
self.plot_ts.set(self.cfg.getint('options', 'plot_ts'))
self.save_ts.set(self.cfg.getint('options', 'save_ts'))
self.rst.set(self.cfg.getint('options', 'rst'))
self.rev_pick.set(self.cfg.getint('options', 'rev_pick'))
self.is_VST.set(self.cfg.getint('options', 'is_VST'))
self.pred_norm.set(self.cfg.getint('options', 'pred_norm'))
# Read parameters and store them as a dictionary of StringVars
# This automatically links them to changes made in the Options dialog.
for item in self.cfg['parameters'].items():
self.params[item[0]] = tk.StringVar()
self.params[item[0]].set(item[1])
def refresh_entries_from_cfg(self, path=None, init=False):
"""Refresh entries from config file"""
if init:
try:
c = self.cfg.read(self.path_cfg.get())
if not(c):
self.create_default_config()
self.cfg.read(self.path_cfg.get())
print('created default config')
print('read default config')
except:
self.create_default_config()
self.cfg.read(self.path_cfg.get())
print('created default config')
if path:
try:
c = self.cfg.read(self.path_cfg.get())
if not(c):
self.create_default_config(path=path)
self.cfg.read(self.path_cfg.get())
print('created project config')
print('read project config')
except:
self.create_default_config(path=path)
self.cfg.read(self.path_cfg.get())
print('created project config')
self.set_data()
def create_default_config(self, path=None):
"""Creates default config in the given path"""
config = configparser.ConfigParser()
config.optionxform = str
if not(path):
path = os.path.join(str(Path.home()), 'rst_evaluation_default.ini')