move data formating scripts into this repo
This commit is contained in:
		
							parent
							
								
									a1cfee1955
								
							
						
					
					
						commit
						31444f919e
					
				
					 7 changed files with 490 additions and 0 deletions
				
			
		
							
								
								
									
										138
									
								
								scripts/cellmeta.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								scripts/cellmeta.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,138 @@
 | 
			
		|||
from chargefile import ChargeFile, SearchDirection
 | 
			
		||||
 | 
			
		||||
CYCLES_PER_STEP = 4
 | 
			
		||||
STEP_COUNT = 12
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def charge_cylces_in_step(globalstep: int):
 | 
			
		||||
	cyclepoint = globalstep % STEP_COUNT
 | 
			
		||||
	if cyclepoint == 0:
 | 
			
		||||
		if (globalstep / STEP_COUNT) % 10 == 0:
 | 
			
		||||
			return 1
 | 
			
		||||
		else:
 | 
			
		||||
			return 0
 | 
			
		||||
	if cyclepoint == 9:
 | 
			
		||||
		return 1
 | 
			
		||||
	if cyclepoint == 11:
 | 
			
		||||
		return CYCLES_PER_STEP
 | 
			
		||||
	return 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def charge_cycles_at_step(globalstep: int,):
 | 
			
		||||
	count = 0
 | 
			
		||||
	for i in range(globalstep):
 | 
			
		||||
		count += charge_cylces_in_step(i)
 | 
			
		||||
	return count
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def thermal_cylces_in_step(globalstep: int, substep: int = -1):
 | 
			
		||||
	cyclepoint = globalstep % STEP_COUNT
 | 
			
		||||
	if cyclepoint == 0:
 | 
			
		||||
		if (globalstep / STEP_COUNT) % 10 == 0:
 | 
			
		||||
			return 0
 | 
			
		||||
		else:
 | 
			
		||||
			return CYCLES_PER_STEP
 | 
			
		||||
	if cyclepoint == 2:
 | 
			
		||||
		return CYCLES_PER_STEP
 | 
			
		||||
	if cyclepoint == 4:
 | 
			
		||||
		return CYCLES_PER_STEP
 | 
			
		||||
	if cyclepoint == 6:
 | 
			
		||||
		return CYCLES_PER_STEP
 | 
			
		||||
	if cyclepoint == 8:
 | 
			
		||||
		return CYCLES_PER_STEP
 | 
			
		||||
	if cyclepoint == 10:
 | 
			
		||||
		return CYCLES_PER_STEP
 | 
			
		||||
	if cyclepoint == 11:
 | 
			
		||||
		return 1
 | 
			
		||||
	return 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def thermal_cycles_at_step(globalstep: int, substep: int):
 | 
			
		||||
	count = 0
 | 
			
		||||
	for i in range(globalstep - 1):
 | 
			
		||||
		count += thermal_cylces_in_step(globalstep)
 | 
			
		||||
	count += thermal_cylces_in_step(globalstep, substep)
 | 
			
		||||
	return count
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
non_charge_cycle_cell = list(range(4, 7))
 | 
			
		||||
non_thermal_cycle_cell = list(range(11, 21))
 | 
			
		||||
cell_thermal_range = {
 | 
			
		||||
	0: [35, 55],
 | 
			
		||||
	1: [35, 55],
 | 
			
		||||
	2: [35, 55],
 | 
			
		||||
	3: [35, 55],
 | 
			
		||||
	4: [35, 55],
 | 
			
		||||
	5: [35, 55],
 | 
			
		||||
	6: [35, 55],
 | 
			
		||||
	7: [35, 45],
 | 
			
		||||
	8: [35, 45],
 | 
			
		||||
	9: [35, 45],
 | 
			
		||||
	10: [35, 45],
 | 
			
		||||
	11: [35, 35],
 | 
			
		||||
	12: [35, 35],
 | 
			
		||||
	13: [35, 35],
 | 
			
		||||
	14: [45, 45],
 | 
			
		||||
	15: [45, 45],
 | 
			
		||||
	16: [45, 45],
 | 
			
		||||
	17: [35, 55],
 | 
			
		||||
	18: [35, 55],
 | 
			
		||||
	19: [35, 55],
 | 
			
		||||
	20: [35, 55],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cell_group_table = {
 | 
			
		||||
	0: 0,
 | 
			
		||||
	1: 0,
 | 
			
		||||
	2: 0,
 | 
			
		||||
	3: 0,
 | 
			
		||||
	4: 1,
 | 
			
		||||
	5: 1,
 | 
			
		||||
	6: 1,
 | 
			
		||||
	7: 2,
 | 
			
		||||
	8: 2,
 | 
			
		||||
	9: 2,
 | 
			
		||||
	10: 2,
 | 
			
		||||
	11: 3,
 | 
			
		||||
	12: 3,
 | 
			
		||||
	13: 3,
 | 
			
		||||
	14: 4,
 | 
			
		||||
	15: 4,
 | 
			
		||||
	16: 4,
 | 
			
		||||
	17: 5,
 | 
			
		||||
	18: 5,
 | 
			
		||||
	19: 5,
 | 
			
		||||
	20: 5,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CellMeta:
 | 
			
		||||
	def __init__(self, cellid: int, globalstep: int, substep: int, charge_files: list[ChargeFile], total_cells: int):
 | 
			
		||||
		closest_avg = None
 | 
			
		||||
		closest_charge = None
 | 
			
		||||
		if cellid not in non_charge_cycle_cell:
 | 
			
		||||
			closest_avg = ChargeFile.FindClosest(charge_files, globalstep, -1)
 | 
			
		||||
			closest_charge = ChargeFile.FindClosest(charge_files, globalstep, cellid)
 | 
			
		||||
		if closest_charge is not None:
 | 
			
		||||
			assert closest_charge.cell == cellid
 | 
			
		||||
 | 
			
		||||
		total_charge_cells = 0
 | 
			
		||||
		for i in range(total_cells):
 | 
			
		||||
			if i not in non_charge_cycle_cell:
 | 
			
		||||
				total_charge_cells += 1
 | 
			
		||||
 | 
			
		||||
		self.cell_group = cell_group_table[cellid]
 | 
			
		||||
		self.charge_cycles = charge_cycles_at_step(globalstep) if cellid not in non_charge_cycle_cell else 0
 | 
			
		||||
		self.thermal_cycles = thermal_cycles_at_step(globalstep, substep) if cellid not in non_thermal_cycle_cell else 0
 | 
			
		||||
		self.last_avg_cap = abs(closest_avg.capacity) / total_charge_cells if closest_avg is not None else -1
 | 
			
		||||
		self.last_avg_cap_step = closest_avg.step if closest_avg is not None else -1
 | 
			
		||||
		self.last_cap = abs(closest_charge.capacity) if closest_charge is not None else -1
 | 
			
		||||
		self.last_cap_step = closest_charge.step if closest_charge is not None else -1
 | 
			
		||||
		self.thermal_range = cell_thermal_range[cellid]
 | 
			
		||||
		if cellid not in non_charge_cycle_cell:
 | 
			
		||||
			self.soc = ChargeFile.GetSoc(charge_files, globalstep, cellid, total_charge_cells)
 | 
			
		||||
			self.cap_esitmate = ChargeFile.GetCapacityEsitmate(charge_files, globalstep, cellid, total_charge_cells)
 | 
			
		||||
		else:
 | 
			
		||||
			self.soc = -1
 | 
			
		||||
			self.cap_esitmate = -1
 | 
			
		||||
		self.soc_estimate = -1
 | 
			
		||||
							
								
								
									
										177
									
								
								scripts/chargefile.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								scripts/chargefile.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,177 @@
 | 
			
		|||
import csv
 | 
			
		||||
 | 
			
		||||
from parseerror import ParseError
 | 
			
		||||
import os
 | 
			
		||||
import enum
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SearchDirection(enum.Enum):
 | 
			
		||||
	CLOSEST = 0
 | 
			
		||||
	PREVIOUS_ONLY = 1
 | 
			
		||||
	FORWARD_ONLY = 2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def calc_capacity(charge_curve: list[dict]):
 | 
			
		||||
	capacity = 0.0
 | 
			
		||||
	prev_time = -1
 | 
			
		||||
	prev_current = -1
 | 
			
		||||
	total_t = 0
 | 
			
		||||
	for entry in charge_curve:
 | 
			
		||||
		if prev_time > 0:
 | 
			
		||||
			delta_s = entry['time'] - prev_time
 | 
			
		||||
			current = (entry['current'] + prev_current) / 2
 | 
			
		||||
			capacity += current * (delta_s / (60.0 * 60.0))
 | 
			
		||||
			total_t += delta_s
 | 
			
		||||
		prev_time = entry['time']
 | 
			
		||||
		prev_current = entry['current']
 | 
			
		||||
	return capacity
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ChargeFile:
 | 
			
		||||
	def __init__(self, filename: str):
 | 
			
		||||
		self.start_voltage = 0.0
 | 
			
		||||
		self.end_voltage = 0.0
 | 
			
		||||
		self.capacity = 0.0
 | 
			
		||||
		self.cell = -1
 | 
			
		||||
		self.discharge = False
 | 
			
		||||
		self.current = 0.0
 | 
			
		||||
		self.full_cycle = False
 | 
			
		||||
		self.step = 0
 | 
			
		||||
 | 
			
		||||
		if os.path.split(filename)[1].startswith("single_cell_charge") or os.path.split(filename)[1].startswith("single_cell_discharge"):
 | 
			
		||||
			tokens = filename.split('.')[0].split('_')
 | 
			
		||||
			self.step = int(tokens[-2])
 | 
			
		||||
			self.cell = int(tokens[-1])
 | 
			
		||||
		elif os.path.split(filename)[1].startswith("charge_for"):
 | 
			
		||||
			self.step = int(filename.split('.')[0].split('_')[-1])
 | 
			
		||||
		else:
 | 
			
		||||
			raise ParseError(f"File name {os.path.split(filename)[1]} not in the expected sheme for ChargeFile")
 | 
			
		||||
 | 
			
		||||
		with open(filename, newline='') as csvfile:
 | 
			
		||||
			reader = csv.reader(csvfile, delimiter=',', quotechar='"')
 | 
			
		||||
			reader.__next__()
 | 
			
		||||
			timestr = reader.__next__()[0]
 | 
			
		||||
			if timestr != "time":
 | 
			
		||||
				raise ParseError(f"Expected time got {timestr}")
 | 
			
		||||
			charge_curve = list()
 | 
			
		||||
			for row in reader:
 | 
			
		||||
				charge_curve.append({'time': int(row[0]), 'voltage': float(row[1]), 'current': float(row[2])})
 | 
			
		||||
			self.current = charge_curve[int(len(charge_curve) / 2)]['current']
 | 
			
		||||
			self.discharge = self.current < 0
 | 
			
		||||
			self.start_voltage = charge_curve[0]['voltage']
 | 
			
		||||
			self.end_voltage = charge_curve[-1]['voltage']
 | 
			
		||||
			self.capacity = calc_capacity(charge_curve)
 | 
			
		||||
			self.full_cycle = self.start_voltage > 4.05 and self.end_voltage < 3.15 or self.start_voltage < 3.15 and self.end_voltage > 4.05
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def FindClosest(charge_files: list, step: int, cellid: int = -1, full_cycle=True, direction=SearchDirection.CLOSEST):
 | 
			
		||||
		closest_file = None
 | 
			
		||||
		for charge_file in charge_files:
 | 
			
		||||
			if charge_file.cell != cellid:
 | 
			
		||||
				continue
 | 
			
		||||
			if direction == SearchDirection.PREVIOUS_ONLY and charge_file.step > step:
 | 
			
		||||
				continue
 | 
			
		||||
			if direction == SearchDirection.FORWARD_ONLY and charge_file.step < step:
 | 
			
		||||
				continue
 | 
			
		||||
			if not full_cycle or charge_file.full_cycle:
 | 
			
		||||
				if closest_file is not None:
 | 
			
		||||
					if abs(step - closest_file.step) > abs(step - charge_file.step):
 | 
			
		||||
						closest_file = charge_file
 | 
			
		||||
					elif abs(step - closest_file.step) == abs(step - charge_file.step) and step > closest_file.step and not closest_file.discharge:
 | 
			
		||||
						if (step > closest_file.step and not closest_file.discharge) or (step < closest_file.step and closest_file.discharge):
 | 
			
		||||
							closest_file = charge_file
 | 
			
		||||
				else:
 | 
			
		||||
					closest_file = charge_file
 | 
			
		||||
		return closest_file
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def GetSoc(charge_files: list, step: int, cellid: int, cell_count: int) -> float:
 | 
			
		||||
 | 
			
		||||
		common_closest_full = ChargeFile.FindClosest(charge_files, step, -1, True, SearchDirection.PREVIOUS_ONLY)
 | 
			
		||||
		specific_closest_full = ChargeFile.FindClosest(charge_files, step, cellid, True, SearchDirection.PREVIOUS_ONLY)
 | 
			
		||||
 | 
			
		||||
		if specific_closest_full is None and common_closest_full is None:
 | 
			
		||||
			return -1.0
 | 
			
		||||
 | 
			
		||||
		if common_closest_full is None:
 | 
			
		||||
			closest_full = specific_closest_full
 | 
			
		||||
		elif specific_closest_full is None:
 | 
			
		||||
			closest_full = common_closest_full
 | 
			
		||||
		elif step - specific_closest_full.step < step - common_closest_full.step:
 | 
			
		||||
			closest_full = specific_closest_full
 | 
			
		||||
		else:
 | 
			
		||||
			closest_full = common_closest_full
 | 
			
		||||
 | 
			
		||||
		full_cap = closest_full.capacity
 | 
			
		||||
		if closest_full.cell == -1:
 | 
			
		||||
			full_cap = full_cap / cell_count
 | 
			
		||||
 | 
			
		||||
		if closest_full.discharge:
 | 
			
		||||
			charge_counter = 0.0
 | 
			
		||||
		else:
 | 
			
		||||
			charge_counter = full_cap
 | 
			
		||||
 | 
			
		||||
		accepted_count = 0
 | 
			
		||||
		end_voltage = closest_full.end_voltage
 | 
			
		||||
 | 
			
		||||
		for charge_file in charge_files:
 | 
			
		||||
			if charge_file.step <= step and charge_file.step > closest_full.step:
 | 
			
		||||
				accepted_count += 1
 | 
			
		||||
				if charge_file.cell == -1:
 | 
			
		||||
					charge_counter += charge_file.capacity / cell_count
 | 
			
		||||
				else:
 | 
			
		||||
					charge_counter += charge_file.capacity
 | 
			
		||||
				end_voltage = charge_file.end_voltage
 | 
			
		||||
				if end_voltage > 4.15:
 | 
			
		||||
					charge_counter = full_cap
 | 
			
		||||
				elif end_voltage < 3.15:
 | 
			
		||||
					charge_counter = 0
 | 
			
		||||
 | 
			
		||||
		soc = charge_counter / abs(full_cap)
 | 
			
		||||
 | 
			
		||||
		if soc > 1.05 or soc < -0.05:
 | 
			
		||||
			return -1
 | 
			
		||||
 | 
			
		||||
		assert not (end_voltage < 3.4 and soc > 0.8)
 | 
			
		||||
		assert not (end_voltage > 4.0 and soc < 0.6)
 | 
			
		||||
		assert not (soc < -0.1 or soc > 1.1)
 | 
			
		||||
 | 
			
		||||
		return soc
 | 
			
		||||
 | 
			
		||||
	def GetCommonCapacityEstimate(charge_files: list, step: int) -> tuple[float, int] | None:
 | 
			
		||||
		prev_charge = ChargeFile.FindClosest(charge_files, step, -1, True, SearchDirection.PREVIOUS_ONLY)
 | 
			
		||||
		next_charge = ChargeFile.FindClosest(charge_files, step, -1, True, SearchDirection.FORWARD_ONLY)
 | 
			
		||||
 | 
			
		||||
		if prev_charge is None and next_charge is None:
 | 
			
		||||
			return None
 | 
			
		||||
		if prev_charge is None:
 | 
			
		||||
			return (abs(next_charge.capacity), next_charge.step - step)
 | 
			
		||||
		if next_charge is None:
 | 
			
		||||
			return (abs(prev_charge.capacity), step - prev_charge.step)
 | 
			
		||||
 | 
			
		||||
		return ((abs(next_charge.capacity) - abs(prev_charge.capacity)) * ((step - prev_charge.step) / (next_charge.step - prev_charge.step)) + abs(prev_charge.capacity),
 | 
			
		||||
			min(step - prev_charge.step, next_charge.step - step))
 | 
			
		||||
 | 
			
		||||
	def GetCapacityEsitmate(charge_files: list, step: int, cellid: int, cell_count: int) -> float:
 | 
			
		||||
		prev_charge = ChargeFile.FindClosest(charge_files, step, cellid, True, SearchDirection.PREVIOUS_ONLY)
 | 
			
		||||
		next_charge = ChargeFile.FindClosest(charge_files, step, cellid, True, SearchDirection.FORWARD_ONLY)
 | 
			
		||||
 | 
			
		||||
		common_cap = ChargeFile.GetCommonCapacityEstimate(charge_files, step)
 | 
			
		||||
		if prev_charge is None and next_charge is None:
 | 
			
		||||
			if common_cap is None:
 | 
			
		||||
				return -1
 | 
			
		||||
			return common_cap[0] / cell_count
 | 
			
		||||
 | 
			
		||||
		if prev_charge is not None and next_charge is not None:
 | 
			
		||||
			single_charge_estimate = (abs(next_charge.capacity) - abs(prev_charge.capacity)) * ((step - prev_charge.step) / (next_charge.step - prev_charge.step))
 | 
			
		||||
			single_charge_estimate += abs(prev_charge.capacity)
 | 
			
		||||
			if common_cap is None or min(step - prev_charge.step, next_charge.step - step) < common_cap[1]:
 | 
			
		||||
				return single_charge_estimate
 | 
			
		||||
			common_cap_at_prev = ChargeFile.GetCommonCapacityEstimate(charge_files, prev_charge.step)
 | 
			
		||||
			common_cap_at_next = ChargeFile.GetCommonCapacityEstimate(charge_files, next_charge.step)
 | 
			
		||||
			avg_delta = ((abs(prev_charge.capacity) - common_cap_at_prev[0] / cell_count) + (abs(next_charge.capacity) - common_cap_at_next[0] / cell_count)) / 2.0
 | 
			
		||||
			return (common_cap[0] / cell_count) + avg_delta
 | 
			
		||||
 | 
			
		||||
		singe_charge = prev_charge if prev_charge is not None else next_charge
 | 
			
		||||
		common_cap_at_single = ChargeFile.GetCommonCapacityEstimate(charge_files, singe_charge.step)
 | 
			
		||||
		return (common_cap[0] / cell_count) + (abs(singe_charge.capacity) - common_cap_at_single[0] / cell_count)
 | 
			
		||||
							
								
								
									
										65
									
								
								scripts/createdataset.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								scripts/createdataset.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,65 @@
 | 
			
		|||
import argparse
 | 
			
		||||
import os
 | 
			
		||||
from tqdm import tqdm
 | 
			
		||||
import tarfile
 | 
			
		||||
 | 
			
		||||
from chargefile import ChargeFile
 | 
			
		||||
from spectrafile import SpectraFile
 | 
			
		||||
from soc_estimation import add_soc_estimate
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
	parser = argparse.ArgumentParser("KissExpiramentCreateDataset")
 | 
			
		||||
	parser.add_argument('--data', '-d', required=True, help="Data input directory")
 | 
			
		||||
	parser.add_argument('--out', '-o', required=True, help="output directory")
 | 
			
		||||
	args = parser.parse_args()
 | 
			
		||||
 | 
			
		||||
	filenames = [f for f in os.listdir(args.data) if os.path.isfile(os.path.join(args.data, f))]
 | 
			
		||||
	charge_filenames = [f for f in filenames if f.startswith("charge") or f.startswith("single_cell_")]
 | 
			
		||||
	spectra_filenames = [f for f in filenames if not f.startswith("charge") and not f.startswith("single_cell_") and not f.startswith("voltage_equlaization_") and f != "expiramentlog.csv"]
 | 
			
		||||
 | 
			
		||||
	print(f"found {len(spectra_filenames)} spectra")
 | 
			
		||||
	print(f"found {len(charge_filenames)} charge/discharge sequences")
 | 
			
		||||
 | 
			
		||||
	if not os.path.exists(args.out + ".tmp"):
 | 
			
		||||
		os.makedirs(args.out + ".tmp")
 | 
			
		||||
 | 
			
		||||
	charge_files = list()
 | 
			
		||||
	for filename in charge_filenames:
 | 
			
		||||
		charge_files.append(ChargeFile(os.path.join(args.data, filename)))
 | 
			
		||||
 | 
			
		||||
	cells = set()
 | 
			
		||||
	for filename in tqdm(spectra_filenames, desc="Finding cells"):
 | 
			
		||||
		tokens = filename.split('.')[0].split('-')
 | 
			
		||||
		cellid = int(tokens[1])
 | 
			
		||||
		cells.add(cellid)
 | 
			
		||||
 | 
			
		||||
	print(f"{len(cells)} cells where involved")
 | 
			
		||||
 | 
			
		||||
	spectras = list()
 | 
			
		||||
 | 
			
		||||
	for filename in tqdm(spectra_filenames, desc="Resolveing data"):
 | 
			
		||||
		tokens = filename.split('.')[0].split('-')
 | 
			
		||||
		step = int(tokens[0])
 | 
			
		||||
		cellid = int(tokens[1])
 | 
			
		||||
		substep = int(tokens[2])
 | 
			
		||||
		sf = SpectraFile(os.path.join(args.data, filename), cellid, step, substep, charge_files, len(cells))
 | 
			
		||||
		spectras.append(sf)
 | 
			
		||||
 | 
			
		||||
	add_soc_estimate(spectras)
 | 
			
		||||
 | 
			
		||||
	for spectra in spectras:
 | 
			
		||||
		spectra.write(args.out + ".tmp")
 | 
			
		||||
 | 
			
		||||
	try:
 | 
			
		||||
		os.remove(f"{args.out}.tar")
 | 
			
		||||
	except FileNotFoundError:
 | 
			
		||||
		pass
 | 
			
		||||
	tar = tarfile.open(f"{args.out}.tar", mode="x")
 | 
			
		||||
	for filename in tqdm(os.listdir(args.out + ".tmp"), desc="Saveing data"):
 | 
			
		||||
		path = os.path.join(args.out + ".tmp", filename)
 | 
			
		||||
		tar.add(path, arcname=os.path.split(path)[-1])
 | 
			
		||||
		os.remove(path)
 | 
			
		||||
	os.rmdir(args.out + ".tmp")
 | 
			
		||||
	tar.close()
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										35
									
								
								scripts/extractmeta.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								scripts/extractmeta.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
#!/bin/python
 | 
			
		||||
 | 
			
		||||
import tarfile
 | 
			
		||||
from tqdm import tqdm
 | 
			
		||||
from eisgenerator import EisSpectra
 | 
			
		||||
import csv
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
	parser = argparse.ArgumentParser("KissExpiramentExtractMeta")
 | 
			
		||||
	parser.add_argument('--data', '-d', required=True, help="Data input tar file")
 | 
			
		||||
	parser.add_argument('--out', '-o', required=True, help="output file")
 | 
			
		||||
	args = parser.parse_args()
 | 
			
		||||
 | 
			
		||||
	with open(args.out, 'w', newline='') as outfile:
 | 
			
		||||
		csvwriter = csv.writer(outfile, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
 | 
			
		||||
		with tarfile.open(args.data, mode="r") as tar:
 | 
			
		||||
			master_labels = None
 | 
			
		||||
			rows = list()
 | 
			
		||||
			for file_info in tqdm(tar, desc="Extracting Metadata", total=len(list(tar))):
 | 
			
		||||
				if file_info.isfile():
 | 
			
		||||
					filestr = tar.extractfile(file_info).read()
 | 
			
		||||
					spectra = EisSpectra.loadFromString(filestr)
 | 
			
		||||
					if master_labels is None:
 | 
			
		||||
						master_labels = spectra.labelNames
 | 
			
		||||
						master_labels_copy = master_labels.copy()
 | 
			
		||||
						for i in range(len(master_labels_copy)):
 | 
			
		||||
							print(master_labels_copy[i])
 | 
			
		||||
							master_labels_copy[i] = master_labels_copy[i].strip(' "')
 | 
			
		||||
						csvwriter.writerow(master_labels_copy)
 | 
			
		||||
					elif master_labels != spectra.labelNames:
 | 
			
		||||
						print(f"Error: not all files in {args.data} have the same labelNames")
 | 
			
		||||
						exit(1)
 | 
			
		||||
					csvwriter.writerow(spectra.labels)
 | 
			
		||||
			tar.close()
 | 
			
		||||
							
								
								
									
										3
									
								
								scripts/parseerror.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								scripts/parseerror.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
class ParseError(Exception):
 | 
			
		||||
	def __init__(self, message):
 | 
			
		||||
		self.message = message
 | 
			
		||||
							
								
								
									
										32
									
								
								scripts/soc_estimation.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								scripts/soc_estimation.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
from scipy.optimize import curve_fit
 | 
			
		||||
from scipy.interpolate import splrep, splev
 | 
			
		||||
import csv
 | 
			
		||||
import argparse
 | 
			
		||||
import numpy
 | 
			
		||||
import matplotlib.pyplot as plt
 | 
			
		||||
from eisgenerator import EisSpectra
 | 
			
		||||
import io
 | 
			
		||||
import tarfile
 | 
			
		||||
from tqdm import tqdm
 | 
			
		||||
from spectrafile import SpectraFile
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def add_soc_estimate(spectras: list[SpectraFile]):
 | 
			
		||||
	data = [list(), list()]
 | 
			
		||||
 | 
			
		||||
	for spectra in spectras:
 | 
			
		||||
		if not spectra.meta.soc <= 0:
 | 
			
		||||
			data[0].append(spectra.ocv)
 | 
			
		||||
			data[1].append(spectra.meta.soc)
 | 
			
		||||
 | 
			
		||||
	ndata = numpy.asarray(data)
 | 
			
		||||
	ndata.sort(1)
 | 
			
		||||
 | 
			
		||||
	knots = 9
 | 
			
		||||
	qs = numpy.linspace(0, 1, knots)[1:-1]
 | 
			
		||||
	knots = numpy.quantile(ndata[0], qs)
 | 
			
		||||
	tck = splrep(ndata[0], ndata[1], t=knots, k=3)
 | 
			
		||||
	estimates = splev(ndata[0], tck)
 | 
			
		||||
 | 
			
		||||
	for spectra in spectras:
 | 
			
		||||
		spectra.meta.soc_estimate = splev(spectra.ocv, tck)
 | 
			
		||||
							
								
								
									
										40
									
								
								scripts/spectrafile.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								scripts/spectrafile.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,40 @@
 | 
			
		|||
import os
 | 
			
		||||
 | 
			
		||||
from cellmeta import CellMeta
 | 
			
		||||
from eisgenerator import EisSpectra
 | 
			
		||||
from parseerror import ParseError
 | 
			
		||||
from chargefile import ChargeFile
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SpectraFile:
 | 
			
		||||
	def __init__(self, filename: str, cellid: int, step: int, substep: int, charge_files: list[ChargeFile], total_cells: int):
 | 
			
		||||
		self.cellid = cellid
 | 
			
		||||
		self.step = step
 | 
			
		||||
		self.substep = substep
 | 
			
		||||
		self.filename = filename
 | 
			
		||||
		self.temperature = -1.0
 | 
			
		||||
		self.ocv = -1.0
 | 
			
		||||
		self.meta = CellMeta(cellid, step, substep, charge_files, total_cells)
 | 
			
		||||
		self.filename = os.path.split(filename)[1]
 | 
			
		||||
 | 
			
		||||
		self.spectra = EisSpectra.loadFromDisk(filename)
 | 
			
		||||
		header = self.spectra.header.split('"')[1].split(',')
 | 
			
		||||
		self.temperature = float(header[2])
 | 
			
		||||
		self.ocv = float(header[3])
 | 
			
		||||
 | 
			
		||||
		if int(header[0]) != step or int(header[1]) != cellid:
 | 
			
		||||
			raise ParseError(f"file name and file content of SpectraFile {filename} do not match")
 | 
			
		||||
 | 
			
		||||
	def write(self, directory: str):
 | 
			
		||||
		metaList = [float(self.step), float(self.substep), float(self.cellid), float(self.meta.cell_group), float(self.temperature), float(self.ocv),
 | 
			
		||||
			float(self.meta.charge_cycles), float(self.meta.thermal_cycles), float(self.meta.last_avg_cap), float(self.meta.last_avg_cap_step),
 | 
			
		||||
			float(self.meta.last_cap), float(self.meta.last_cap_step), float(self.meta.cap_esitmate), float(self.meta.soc), float(self.meta.soc_estimate)]
 | 
			
		||||
		self.spectra.setLabels(metaList)
 | 
			
		||||
		self.spectra.model = "Unkown"
 | 
			
		||||
		meta_dsc_strings = ["step", "substep", "cellid", "cell_group", "temparature", "ocv", "charge_cycles", "thermal_cycles",
 | 
			
		||||
			"last_avg_cap", "last_avg_step", "last_cap", "last_cap_step", "cap_estimate", "soc", "soc_estimate"]
 | 
			
		||||
		self.spectra.headerDescription = "File origin"
 | 
			
		||||
		self.spectra.header = "CoinCellHell mesurement file"
 | 
			
		||||
		self.spectra.labelNames = meta_dsc_strings
 | 
			
		||||
		self.spectra.saveToDisk(os.path.join(directory, self.filename))
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue