GetHug
Public Script
Creators Open GetHug

Description

The script reads a file containing in the last column observations of carbon black crystallite sizes from TEM images and computes the size distributions, printing in a text file the frequency of the observations, average size and standard deviation
This public script can be saved to your dashboard or opened in GetHug to run and modify. Its raw server storage path is not exposed.
Show Code Preview
import math
import statistics

units = {}

# Known constants
BIN_MIN_NM = 0.0
units["BIN_MIN_NM"] = "nm"

BIN_MAX_NM = 25.0
units["BIN_MAX_NM"] = "nm"

BIN_WIDTH_NM = 0.25
units["BIN_WIDTH_NM"] = "nm"

OUTPUT_FILE = "crystallite_size_distribution.txt"
units["OUTPUT_FILE"] = "text filename"

# Files to analyze
INPUT_FILE_1 = "file_1.txt"

# Calculations
num_bins = int(round((BIN_MAX_NM - BIN_MIN_NM) / BIN_WIDTH_NM))
units["num_bins"] = "count"

length_values = []
bin_counts = [0] * num_bins

with open(INPUT_FILE_1, "r", encoding="utf-8") as f:
    lines = f.readlines()

# Skip the first line (weird header)
lines = lines[1:]

for line in lines:
    stripped = line.strip()

    # Skip empty or comment lines
    if not stripped or stripped.startswith("#"):
        continue

    parts = stripped.split("\t")

    # Skip malformed rows
    if len(parts) < 1:
        continue

    cleaned_parts = [p.strip() for p in parts]

    # Length values are in the last column
    length_text = cleaned_parts[-1]

    # Skip missing values
    if length_text == "" or length_text.lower() == "nan":
        continue

    try:
        length_nm = float(length_text)
        units["length_nm"] = "nm"
    except ValueError:
        continue

    # Keep only values inside the requested histogram range
    if BIN_MIN_NM <= length_nm < BIN_MAX_NM:
        length_values.append(length_nm)
        bin_index = int((length_nm - BIN_MIN_NM) / BIN_WIDTH_NM)
        if 0 <= bin_index < num_bins:
            bin_counts[bin_index] += 1

observation_count = len(length_values)
units["observation_count"] = "count"

if observation_count == 0:
    raise ValueError("No valid Length observations were found in the file within the requested range.")

average_crystallite_size = statistics.mean(length_values)
units["average_crystallite_size"] = "nm"

if observation_count > 1:
    standard_deviation = statistics.stdev(length_values)
else:
    standard_deviation = 0.0
units["standard_deviation"] = "nm"

total_counts_in_bins = sum(bin_counts)
units["total_counts_in_bins"] = "count"

with open(OUTPUT_FILE, "w", encoding="utf-8") as out_file:
    out_file.write("Summary\n")
    out_file.write(f"Average_crystallite_size_nm\t{average_crystallite_size:.6f}\n")
    out_file.write(f"Standard_deviation_nm\t{standard_deviation:.6f}\n")
    out_file.write(f"Total_observations\t{observation_count}\n")
    out_file.write("\n")
    out_file.write("Bin_start_nm\tBin_end_nm\tBin_center_nm\tCount\tFrequency\n")

    for idx in range(num_bins):
        bin_start_nm = BIN_MIN_NM + idx * BIN_WIDTH_NM
        units["bin_start_nm"] = "nm"

        bin_end_nm = bin_start_nm + BIN_WIDTH_NM
        units["bin_end_nm"] = "nm"

        bin_center_nm = (bin_start_nm + bin_end_nm) / 2.0
        units["bin_center_nm"] = "nm"

        count_in_bin = bin_counts[idx]
        units["count_in_bin"] = "count"

        frequency = count_in_bin / observation_count
        units["frequency"] = "fraction"

        out_file.write(
            f"{bin_start_nm:.2f}\t{bin_end_nm:.2f}\t{bin_center_nm:.2f}\t{count_in_bin}\t{frequency:.6f}\n"
        )

# Outputs
print(f"Processed file: {INPUT_FILE_1}")
print(f"Output written to: {OUTPUT_FILE}")
print(f"Average crystallite size: {average_crystallite_size:.6f} nm")
print(f"Standard deviation: {standard_deviation:.6f} nm")
print(f"Total observations: {observation_count}")

print("=== Variable Summary ===")
for k, v in units.items():
    try:
        if not isinstance(eval(k), (list, dict, set)):
            print(f"  {k} = {eval(k)} [{v}]")
    except:
        pass