Task 219: .FCHK File Format

Task 219: .FCHK File Format

1. List of All Properties Intrinsic to the .FCHK File Format

The .FCHK (Gaussian Formatted Checkpoint) file format is a plain-text, machine-independent structure for storing quantum chemistry calculation results from Gaussian software. It consists of a header (two lines: title and calculation metadata) followed by labeled sections. Each section starts with a descriptive keyword/label followed by data type indicators ('I' for integer, 'R' for real/Fortran double precision) and, for arrays, 'N=<number_of_elements>'. Data for scalars is on the same line; for arrays, subsequent lines contain 5 (for integers) or 6 (for reals) values per line, in free-format Fortran style (atomic units, standard orientation).

The intrinsic properties (fields/keywords) are extensible but include the following standard ones, grouped by category for clarity. This list is compiled from the Gaussian 09/16 formatted checkpoint specifications:

Header Properties

  • Title: String (first 72 characters of line 1).
  • Calculation Type: String (e.g., 'SP', 'FOPT', 'FREQ'; indicates job type like single point, optimization, frequency calculation).
  • Method: String (e.g., 'RHF', 'B3LYP', 'MP2'; computational method).
  • Basis Set: String (e.g., 'STO-3G', '6-31G*'; basis set used).

Molecular System Properties (Scalars)

  • Number of atoms (I).
  • Charge (I).
  • Multiplicity (I).
  • Number of electrons (I).
  • Number of alpha electrons (I).
  • Number of beta electrons (I).
  • Number of basis functions (I).
  • Number of independent functions (I).

Energy and Thermodynamic Properties (Reals)

  • Virial ratio (R).
  • SCF energy (R).
  • MP2 energy (R).
  • Total energy (R).
  • Zero-point energy (R, if available from frequency calc).
  • Thermal energy corrections (R, e.g., translational, rotational, vibrational).

Atomic Properties (Arrays, N=number of atoms)

  • Atomic numbers (I, N=).
  • Nuclear charges (R, N=).
  • Current Cartesian coordinates (R, N=3*natoms; x,y,z flattened).
  • Mulliken charges (R, N=).
  • Natural population analysis (NPA) charges (R, N=; if computed).
  • Electrostatic potential (ESP) charges (R, N=; if computed).

Orbital and Wavefunction Properties (Arrays)

  • Alpha orbital energies (R, N=number of alpha orbitals).
  • Alpha MO coefficients (R, N=number of basis functions * number of alpha orbitals; column-major).
  • Beta orbital energies (R, N=number of beta orbitals; unrestricted cases).
  • Beta MO coefficients (R, N=number of basis functions * number of beta orbitals; column-major).

Density Matrix Properties (Arrays)

  • Total SCF density (R, N=nbasis*(nbasis+1)/2; upper triangular).
  • Spin SCF density (R, N=nbasis*(nbasis+1)/2; if unrestricted).
  • MP2 density (full/spin, R, N=; if MP2 computed).
  • CCSD density (full/spin, R, N=; if coupled-cluster).

Gradient and Force Properties (Arrays)

  • Cartesian gradient (R, N=3*natoms).
  • Internal forces (R, N=; if internal coordinates used).

Hessian and Frequency Properties (Arrays, if frequency calculation)

  • Force constants (R, N=3natoms3*natoms; Cartesian Hessian).

Basis Set Properties (Scalars and Arrays)

  • Number of shells (I).
  • Largest degree of contraction (I).
  • Highest angular momentum present (I).
  • Number of primitive shells (I).
  • Shell types (I, N=number of shells; e.g., 1=S, 3=P, 4=SP).
  • Number of primitives per shell (I, N=).
  • Shell to atom map (I, N=).
  • Primitive exponents (R, N=total primitives).
  • Contraction coefficients (R, N=total contractions).
  • P(S=P) contraction coefficients (R, N=; for SP shells).
  • Shell coordinates (R, N=3*number of shells; center of each shell).

These properties are not all present in every file (depends on calculation type), but they define the format's structure. The format is forward-compatible: unknown fields can be skipped.

3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .FCHK Parsing

This is a self-contained HTML snippet embeddable in a Ghost blog post (use in an HTML card). It allows drag-and-drop of a .FCHK file, parses it client-side, and dumps all detected properties to a <pre> element on the page. Parsing logic reads lines, identifies labels/types/N, and extracts data (scalars inline, arrays by consuming subsequent lines with 5-6 values).

FCHK Parser
Drag and drop a .FCHK file here
Drop a file to see properties...

4. Python Class for .FCHK Handling

This class opens a .fchk file, parses all properties into a dict, supports writing back to a new .fchk file (reconstructing header and sections), and prints them to console.

import re
import os

class FCHKParser:
    def __init__(self, filename):
        with open(filename, 'r') as f:
            self.content = f.read()
        self.properties = {}
        self.parse()

    def parse(self):
        lines = self.content.splitlines()
        line_idx = 0

        # Header
        self.properties['title'] = lines[line_idx].strip()[:72]
        line_idx += 1
        header = re.split(r'\s+', lines[line_idx].strip())
        self.properties['type'] = header[0]
        self.properties['method'] = header[1]
        self.properties['basis'] = header[2]
        line_idx += 1

        while line_idx < len(lines):
            line = lines[line_idx].strip()
            line_idx += 1
            if not line:
                continue
            match = re.match(r'([A-Za-z\s]+?)(I|R)(?:\s+N=(\d+))?', line)
            if not match:
                continue
            label, dtype, n_str = match.groups()
            key = re.sub(r'\s+', '_', label.strip().lower())
            is_array = n_str is not None
            n = int(n_str) if is_array else 1

            value = []
            if not is_array:
                # Scalar
                scalar_match = re.search(rf'{dtype}\s+([\-\d.eE+-]+)', line)
                value = float(scalar_match.group(1)) if scalar_match else None
            else:
                # Array
                cols = 6 if dtype == 'R' else 5
                remaining = n
                while remaining > 0 and line_idx < len(lines):
                    data_line = re.findall(r'[\-\d.eE+-]+', lines[line_idx].strip())
                    data_line = [float(x) for x in data_line[:cols]]
                    take = min(len(data_line), remaining)
                    value.extend(data_line[:take])
                    remaining -= take
                    line_idx += 1
            self.properties[key] = value

    def write(self, output_filename):
        with open(output_filename, 'w') as f:
            f.write(self.properties['title'] + '\n')
            f.write(f"{self.properties['type']:10} {self.properties['method']:30} {self.properties['basis']:30}\n")
            for key, value in self.properties.items():
                if key in ['title', 'type', 'method', 'basis']:
                    continue
                label = key.replace('_', ' ').title()
                if isinstance(value, (int, float)):
                    dtype = 'I' if isinstance(value, int) else 'R'
                    f.write(f"{label} {dtype} {value}\n")
                else:
                    n = len(value)
                    dtype = 'I' if all(isinstance(v, int) for v in value[:min(5, n)]) else 'R'
                    f.write(f"{label} {dtype} N={n}\n")
                    cols = 5 if dtype == 'I' else 6
                    for i in range(0, n, cols):
                        chunk = value[i:i+cols]
                        f.write(' '.join(f"{v:16.8E}" if dtype == 'R' else f"{int(v)}" for v in chunk) + '\n')

    def print_properties(self):
        import json
        print(json.dumps(self.properties, indent=2, default=str))

# Usage: parser = FCHKParser('sample.fchk'); parser.print_properties(); parser.write('output.fchk')

5. Java Class for .FCHK Handling

This Java class reads a .fchk file, parses properties into a Map<String, Object>, supports writing to a new file, and prints to console (System.out).

import java.io.*;
import java.util.*;
import java.util.regex.*;

public class FCHKParser {
    private Map<String, Object> properties = new HashMap<>();
    private String content;

    public FCHKParser(String filename) throws IOException {
        BufferedReader reader = new BufferedReader(new FileReader(filename));
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            sb.append(line).append("\n");
        }
        content = sb.toString();
        reader.close();
        parse();
    }

    private void parse() {
        String[] lines = content.split("\n");
        int lineIdx = 0;

        // Header
        properties.put("title", lines[lineIdx].trim().substring(0, 72));
        lineIdx++;
        String[] header = lines[lineIdx].trim().split("\\s+");
        properties.put("type", header[0]);
        properties.put("method", header[1]);
        properties.put("basis", header[2]);
        lineIdx++;

        Pattern labelPattern = Pattern.compile("([A-Za-z\\s]+?)(I|R)(?:\\s+N=(\\d+))?");
        while (lineIdx < lines.length) {
            String line = lines[lineIdx++].trim();
            if (line.isEmpty()) continue;
            Matcher matcher = labelPattern.matcher(line);
            if (!matcher.find()) continue;
            String label = matcher.group(1).trim().toLowerCase().replaceAll("\\s+", "_");
            String dtype = matcher.group(2);
            String nStr = matcher.group(3);
            boolean isArray = nStr != null;
            int n = isArray ? Integer.parseInt(nStr) : 1;

            Object value;
            if (!isArray) {
                // Scalar
                Pattern scalarPat = Pattern.compile(dtype + "\\s+([\\-\\d.eE+-]+)");
                Matcher scalarM = scalarPat.matcher(line);
                value = scalarM.find() ? Double.parseDouble(scalarM.group(1)) : null;
            } else {
                // Array
                List<Double> arr = new ArrayList<>();
                int remaining = n;
                int cols = "R".equals(dtype) ? 6 : 5;
                while (remaining > 0 && lineIdx < lines.length) {
                    String dataLine = lines[lineIdx++].trim();
                    String[] parts = dataLine.split("\\s+");
                    for (int i = 0; i < Math.min(parts.length, remaining); i++) {
                        arr.add(Double.parseDouble(parts[i]));
                    }
                    remaining -= Math.min(parts.length, remaining);
                }
                value = arr;
            }
            properties.put(label, value);
        }
    }

    public void write(String outputFilename) throws IOException {
        try (PrintWriter writer = new PrintWriter(new FileWriter(outputFilename))) {
            writer.println((String) properties.get("title"));
            writer.printf("%-10s %-30s %-30s%n", properties.get("type"), properties.get("method"), properties.get("basis"));
            for (Map.Entry<String, Object> entry : properties.entrySet()) {
                String key = entry.getKey();
                Object val = entry.getValue();
                if (key.equals("title") || key.equals("type") || key.equals("method") || key.equals("basis")) continue;
                String label = key.replace("_", " ").toUpperCase();
                if (val instanceof Number) {
                    String dtype = (val instanceof Integer) ? "I" : "R";
                    writer.printf("%s %s %s%n", label, dtype, val);
                } else if (val instanceof List) {
                    List<?> arr = (List<?>) val;
                    int n = arr.size();
                    String dtype = "R"; // Assume real for simplicity
                    writer.printf("%s %s N=%d%n", label, dtype, n);
                    int cols = 6;
                    for (int i = 0; i < n; i += cols) {
                        for (int j = 0; j < cols && i + j < n; j++) {
                            writer.printf("%16.8E ", arr.get(i + j));
                        }
                        writer.println();
                    }
                }
            }
        }
    }

    public void printProperties() {
        System.out.println(properties);
    }

    // Usage: new FCHKParser("sample.fchk").printProperties();
}

6. JavaScript Class for .FCHK Handling (Node.js)

This Node.js class uses fs to open a .fchk file, parses properties into an object, supports writing to a new file, and prints to console.

const fs = require('fs');

class FCHKParser {
    constructor(filename) {
        this.content = fs.readFileSync(filename, 'utf8');
        this.properties = {};
        this.parse();
    }

    parse() {
        const lines = this.content.split('\n');
        let lineIdx = 0;

        // Header
        this.properties.title = lines[lineIdx].trim().substring(0, 72);
        lineIdx++;
        const header = lines[lineIdx].trim().split(/\s+/);
        this.properties.type = header[0];
        this.properties.method = header[1];
        this.properties.basis = header[2];
        lineIdx++;

        const labelRegex = /^([A-Za-z\s]+?)(I|R)(?:\s+N=(\d+))?/;
        while (lineIdx < lines.length) {
            let line = lines[lineIdx++].trim();
            if (!line) continue;
            const match = line.match(labelRegex);
            if (!match) continue;
            const [, label, dtype, nStr] = match;
            const key = label.trim().toLowerCase().replace(/\s+/g, '_');
            const isArray = !!nStr;
            const n = isArray ? parseInt(nStr) : 1;

            let value;
            if (!isArray) {
                // Scalar
                const scalarMatch = line.match(new RegExp(`${dtype}\\s+([\\-\\d.eE+-]+)`));
                value = scalarMatch ? parseFloat(scalarMatch[1]) : null;
            } else {
                // Array
                value = [];
                let remaining = n;
                const cols = dtype === 'R' ? 6 : 5;
                while (remaining > 0 && lineIdx < lines.length) {
                    const dataLine = lines[lineIdx++].trim().split(/\s+/).map(v => parseFloat(v)).filter(v => !isNaN(v));
                    const take = Math.min(dataLine.length, remaining);
                    value.push(...dataLine.slice(0, take));
                    remaining -= take;
                }
            }
            this.properties[key] = value;
        }
    }

    write(outputFilename) {
        let output = this.properties.title + '\n';
        output += `${this.properties.type.padEnd(10)}${this.properties.method.padEnd(30)}${this.properties.basis.padEnd(30)}\n`;
        for (const [key, value] of Object.entries(this.properties)) {
            if (['title', 'type', 'method', 'basis'].includes(key)) continue;
            const label = key.replace(/_/g, ' ').toUpperCase();
            if (typeof value === 'number') {
                const dtype = Number.isInteger(value) ? 'I' : 'R';
                output += `${label} ${dtype} ${value}\n`;
            } else if (Array.isArray(value)) {
                const n = value.length;
                const dtype = 'R'; // Assume real
                output += `${label} ${dtype} N=${n}\n`;
                const cols = 6;
                for (let i = 0; i < n; i += cols) {
                    const chunk = value.slice(i, i + cols);
                    output += chunk.map(v => v.toExponential(8).padStart(16)).join(' ') + '\n';
                }
            }
        }
        fs.writeFileSync(outputFilename, output);
    }

    printProperties() {
        console.log(JSON.stringify(this.properties, null, 2));
    }
}

// Usage: const parser = new FCHKParser('sample.fchk'); parser.printProperties(); parser.write('output.fchk');

7. C Code for .FCHK Handling (Struct-Based "Class")

This C implementation uses a struct for properties (simplified dict as key-value pairs), functions to open/parse/write/print. Compile with gcc fchk.c -o fchk -lm. Handles basic parsing (assumes reals for arrays).

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <regex.h>  // For regex, or implement simple parsing

// Simplified: Use arrays for properties (up to 100 keys, values as double* or int)
#define MAX_PROPS 100
#define MAX_LINE 1024
#define MAX_KEY 256

typedef struct {
    char keys[MAX_PROPS][MAX_KEY];
    void* values[MAX_PROPS];  // double* for arrays/scalars (cast)
    int types[MAX_PROPS];     // 0: scalar int, 1: scalar double, 2: array double
    int ns[MAX_PROPS];        // size for arrays, 1 for scalars
    int count;
    char title[73];
    char type[11];
    char method[31];
    char basis[31];
} FCHKProperties;

void parse_fchk(FCHKProperties* props, const char* filename) {
    FILE* fp = fopen(filename, "r");
    if (!fp) return;

    char line[MAX_LINE];
    fgets(line, sizeof(line), fp);
    strncpy(props->title, line, 72);

    fgets(line, sizeof(line), fp);
    sscanf(line, "%10s %30s %30s", props->type, props->method, props->basis);

    props->count = 0;
    regex_t regex;
    regcomp(&regex, "^([A-Za-z ]+?)(I|R)( N=([0-9]+))?$", REG_EXTENDED);
    while (fgets(line, sizeof(line), fp)) {
        size_t len = strlen(line);
        if (len < 2 || line[0] == '\n') continue;
        regmatch_t matches[5];
        if (regexec(&regex, line, 5, matches, 0) != 0) continue;

        char label[256], dtype[2], nstr[16];
        int label_start = matches[1].rm_so;
        int label_end = matches[1].rm_eo;
        strncpy(label, line + label_start, label_end - label_start);
        label[label_end - label_start] = '\0';
        strncpy(dtype, line + matches[2].rm_so, 1);
        int is_array = (matches[3].rm_so != -1);
        int n = 1;
        if (is_array) {
            strncpy(nstr, line + matches[4].rm_so, matches[4].rm_eo - matches[4].rm_so);
            n = atoi(nstr);
        }

        strncpy(props->keys[props->count], label, sizeof(label));
        for (int i = 0; i < strlen(props->keys[props->count]); i++) {
            if (props->keys[props->count][i] == ' ') props->keys[props->count][i] = '_';
        }
        tolower(props->keys[props->count]);

        double* val = malloc((is_array ? n : 1) * sizeof(double));
        if (!is_array) {
            // Scalar: parse from line
            sscanf(line + matches[2].rm_eo, "%lf", val);
            props->types[props->count] = 1;  // double scalar
        } else {
            // Array: read next lines
            int remaining = n;
            int col = strcmp(dtype, "R") == 0 ? 6 : 5;
            while (remaining > 0 && fgets(line, sizeof(line), fp)) {
                char* ptr = line;
                for (int j = 0; j < col && remaining > 0; j++) {
                    if (sscanf(ptr, "%lf", val + (n - remaining)) == 1) {
                        // Advance ptr roughly
                        ptr = strstr(ptr, " ") + 1;
                        remaining--;
                    }
                }
            }
            props->types[props->count] = 2;  // array
        }
        props->ns[props->count] = n;
        props->values[props->count] = val;
        props->count++;
        if (props->count >= MAX_PROPS) break;
    }
    fclose(fp);
    regfree(&regex);
}

void write_fchk(FCHKProperties* props, const char* output) {
    FILE* fp = fopen(output, "w");
    if (!fp) return;
    fprintf(fp, "%s\n", props->title);
    fprintf(fp, "%-10s %-30s %-30s\n", props->type, props->method, props->basis);
    for (int i = 0; i < props->count; i++) {
        if (strstr(props->keys[i], "title") || strstr(props->keys[i], "type") || strstr(props->keys[i], "method") || strstr(props->keys[i], "basis")) continue;
        char lbl[256];
        strcpy(lbl, props->keys[i]);
        for (int j = 0; lbl[j]; j++) lbl[j] = toupper(lbl[j]);
        char* underscore = strstr(lbl, "_");
        if (underscore) *underscore = ' ';
        char dtype = props->types[i] == 0 ? 'I' : 'R';
        if (props->types[i] == 2) {
            fprintf(fp, "%s %c N=%d\n", lbl, dtype, props->ns[i]);
            int col = 6;
            double* arr = (double*) props->values[i];
            for (int j = 0; j < props->ns[i]; j += col) {
                for (int k = 0; k < col && j + k < props->ns[i]; k++) {
                    fprintf(fp, "%16.8E ", arr[j + k]);
                }
                fprintf(fp, "\n");
            }
        } else {
            double v = *(double*) props->values[i];
            fprintf(fp, "%s %c %.8E\n", lbl, dtype, v);
        }
    }
    fclose(fp);
}

void print_properties(FCHKProperties* props) {
    printf("Title: %s\n", props->title);
    printf("Type: %s, Method: %s, Basis: %s\n", props->type, props->method, props->basis);
    for (int i = 0; i < props->count; i++) {
        printf("%s: ", props->keys[i]);
        if (props->types[i] == 2) {
            double* arr = (double*) props->values[i];
            printf("[");
            for (int j = 0; j < props->ns[i]; j++) {
                printf("%.3f", arr[j]);
                if (j < props->ns[i] - 1) printf(", ");
            }
            printf("]\n");
        } else {
            printf("%.8E\n", *(double*) props->values[i]);
        }
    }
}

void free_props(FCHKProperties* props) {
    for (int i = 0; i < props->count; i++) {
        free(props->values[i]);
    }
}

// Usage: FCHKProperties props = {0}; parse_fchk(&props, "sample.fchk"); print_properties(&props); write_fchk(&props, "output.fchk"); free_props(&props);