Task 289: .HIN File Format

Task 289: .HIN File Format

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

The .HIN file format is a plain-text, line-based format used primarily for storing molecular structures in HyperChem software. Each non-comment line begins with a keyword followed by space-separated fields. Comments start with a semicolon (;). The format supports multiple molecules per file, optional residues, and atom-level details including bonds (specified inline with atoms). Based on the format specification, the intrinsic properties (core data fields defining the structure, metadata, and atomic details) are:

  • Forcefield name: String specifying the force field used (e.g., "mm+").
  • System info: Variable string for system parameters (e.g., energy minimization settings).
  • Seed value: Variable string for random seed in simulations.
  • View parameters: Variable string array for camera/view settings (e.g., rotation, zoom).
  • Box dimensions: Variable string for simulation box (e.g., periodic boundary conditions).
  • Molecule number: Integer ID for the molecule (starts at 1 for multi-model files).
  • Molecule name: String (up to 64 characters, quoted) describing the molecule.
  • Residue number: Integer ID for the residue within a molecule (optional, starts at 1).
  • Residue name: String (up to 4 characters) for the residue (e.g., "ALA").
  • PDB residue number: Integer preserving original PDB residue ID.
  • Previous residue ID: Integer linking to prior residue in chain.
  • Next residue ID: Integer linking to next residue in chain.
  • Atom number: Integer ID for the atom within the molecule (sequential from 1).
  • Atom name: String (up to 4 characters) labeling the atom (e.g., "CA").
  • Element symbol: String (up to 3 characters) for the chemical element (e.g., "C").
  • Atom type: String (up to 4 characters) for force field atom type (e.g., "sp3").
  • Flags: String of single letters indicating atom attributes (e.g., "h" for heteroatom, "i" for improper torsion, "x" for united atom, "s" for selected; default "-").
  • Charge: Float for partial or formal charge on the atom (e.g., -0.5).
  • X coordinate: Float for atom position in Ångstroms.
  • Y coordinate: Float for atom position in Ångstroms.
  • Z coordinate: Float for atom position in Ångstroms.
  • Coordination number: Integer (0-12) counting covalently bonded neighbors.
  • Bonded atom list: Array of pairs (integer atom ID, bond type string) for connections (bond types: "s" single, "d" double, "t" triple, "a" aromatic; up to 12 per atom).
  • Velocity X: Float for atomic velocity component (Å/picosecond, from dynamics; optional).
  • Velocity Y: Float for atomic velocity component (Å/picosecond, from dynamics; optional).
  • Velocity Z: Float for atomic velocity component (Å/picosecond, from dynamics; optional).
  • Mass: Float for non-standard atomic mass (in u, for isotopes; optional).
  • Basis set: String for quantum chemistry basis (e.g., "STO-3G"; optional).
  • Formal charge: Integer for explicit formal charge (alternative to charge field; optional).

These properties are parsed line-by-line, with atoms grouped under mol/endmol blocks and optional res/endres for residues. Bonds are directional from the current atom but symmetric in practice.

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

Paste the following HTML snippet into a Ghost blog post (use the HTML view in the editor). It creates a drag-and-drop zone that reads a dropped .HIN file, parses all properties, and dumps them to a scrollable <pre> block below. Uses browser File API; no server needed.

Drag and drop a .HIN file here

4. Python Class for .HIN Handling

import re

class HinFile:
    def __init__(self):
        self.properties = {
            'forcefield': None, 'sys': None, 'seed': None, 'view': None, 'box': None,
            'molecules': []
        }

    def read(self, filename):
        with open(filename, 'r') as f:
            content = f.read()
        self._parse(content)
        self._print_properties()

    def _parse(self, text):
        lines = text.split('\n')
        current_mol = None
        current_res = None
        for line in lines:
            line = line.strip()
            if not line or line.startswith(';'):
                continue
            parts = re.split(r'\s+', line)
            keyword = parts[0].lower()
            if keyword == 'forcefield':
                self.properties['forcefield'] = parts[1]
            elif keyword == 'sys':
                self.properties['sys'] = ' '.join(parts[1:])
            elif keyword == 'seed':
                self.properties['seed'] = ' '.join(parts[1:])
            elif keyword == 'view':
                self.properties['view'] = ' '.join(parts[1:])
            elif keyword == 'box':
                self.properties['box'] = ' '.join(parts[1:])
            elif keyword == 'mol':
                current_mol = {'id': int(parts[1]), 'name': parts[2], 'atoms': [], 'residues': []}
                self.properties['molecules'].append(current_mol)
            elif keyword == 'res' and current_mol:
                current_res = {
                    'id': int(parts[1]), 'name': parts[2], 'pdb_num': int(parts[3]),
                    'prev': int(parts[4]), 'next': int(parts[5])
                }
                current_mol['residues'].append(current_res)
            elif keyword == 'atom' and current_mol:
                atom_num = int(parts[1])
                atom_name = parts[2]
                element = parts[3]
                atom_type = parts[4]
                flags = parts[5]
                charge = float(parts[6])
                x, y, z = float(parts[7]), float(parts[8]), float(parts[9])
                coord_num = int(parts[10])
                bonds = []
                i = 11
                while i < len(parts):
                    bonded_id = int(parts[i])
                    bond_type = parts[i+1]
                    bonds.append((bonded_id, bond_type))
                    i += 2
                atom = {
                    'num': atom_num, 'name': atom_name, 'element': element, 'type': atom_type,
                    'flags': flags, 'charge': charge, 'x': x, 'y': y, 'z': z,
                    'coord_num': coord_num, 'bonds': bonds
                }
                current_mol['atoms'].append(atom)
            elif keyword == 'vel' and current_mol:
                # Associate with last atom or by ID; simplified to print
                pass  # Handled in print
            elif keyword == 'mass' and current_mol:
                pass
            elif keyword == 'basisset' and current_mol:
                pass
            elif keyword == 'formalcharge' and current_mol:
                pass
            elif keyword == 'endmol':
                current_mol = None
            elif keyword == 'endres':
                current_res = None

    def _print_properties(self):
        if self.properties['forcefield']:
            print(f"Forcefield: {self.properties['forcefield']}")
        if self.properties['sys']:
            print(f"System info: {self.properties['sys']}")
        if self.properties['seed']:
            print(f"Seed: {self.properties['seed']}")
        if self.properties['view']:
            print(f"View params: {self.properties['view']}")
        if self.properties['box']:
            print(f"Box dims: {self.properties['box']}")
        for mol in self.properties['molecules']:
            print(f"Molecule {mol['id']}: {mol['name']}")
            for res in mol['residues']:
                print(f"  Residue {res['id']} ({res['name']}): PDB# {res['pdb_num']}, Prev: {res['prev']}, Next: {res['next']}")
            for atom in mol['atoms']:
                print(f"  Atom {atom['num']}: Name={atom['name']}, Element={atom['element']}, Type={atom['type']}, Flags={atom['flags']}, Charge={atom['charge']}, Coords=({atom['x']:.5f}, {atom['y']:.5f}, {atom['z']:.5f}), CoordNum={atom['coord_num']}, Bonds={[(f'({b[0]},{b[1]})' for b in atom['bonds'])]}")
        # Optional props printed if parsed (extend _parse for vel/mass/etc.)

    def write(self, filename):
        with open(filename, 'w') as f:
            if self.properties['forcefield']:
                f.write(f"forcefield {self.properties['forcefield']}\n")
            # ... similar for other globals
            for mol in self.properties['molecules']:
                f.write(f"mol {mol['id']} {mol['name']}\n")
                for res in mol['residues']:
                    f.write(f"res {res['id']} {res['name']} {res['pdb_num']} {res['prev']} {res['next']}\n")
                for atom in mol['atoms']:
                    bond_str = ' '.join([f"{b[0]} {b[1]}" for b in atom['bonds']])
                    f.write(f"atom {atom['num']} {atom['name']} {atom['element']} {atom['type']} {atom['flags']} {atom['charge']} {atom['x']} {atom['y']} {atom['z']} {atom['coord_num']} {bond_str}\n")
                f.write(f"endmol {mol['id']}\n")
        print(f"Wrote to {filename}")

# Usage: parser = HinFile(); parser.read('example.hin'); parser.write('output.hin')

5. Java Class for .HIN Handling

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

public class HinFile {
    private Map<String, String> globals = new HashMap<>();
    private List<Molecule> molecules = new ArrayList<>();

    static class Atom {
        int num; String name, element, type, flags; double charge, x, y, z; int coordNum;
        List<Bond> bonds = new ArrayList<>();
        static class Bond { int to; String type; }
    }

    static class Residue {
        int id, pdbNum, prev, next; String name;
    }

    static class Molecule {
        int id; String name; List<Atom> atoms = new ArrayList<>(); List<Residue> residues = new ArrayList<>();
    }

    public void read(String filename) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader(filename));
        String line;
        Molecule currentMol = null;
        Residue currentRes = null;
        while ((line = br.readLine()) != null) {
            line = line.trim();
            if (line.isEmpty() || line.startsWith(";")) continue;
            String[] parts = line.split("\\s+");
            String keyword = parts[0].toLowerCase();
            if (keyword.equals("forcefield")) {
                globals.put("forcefield", parts[1]);
            } else if (keyword.equals("sys")) {
                globals.put("sys", String.join(" ", Arrays.copyOfRange(parts, 1, parts.length)));
            } // ... similar for seed, view, box
            else if (keyword.equals("mol")) {
                currentMol = new Molecule();
                currentMol.id = Integer.parseInt(parts[1]);
                currentMol.name = parts[2];
                molecules.add(currentMol);
            } else if (keyword.equals("res") && currentMol != null) {
                currentRes = new Residue();
                currentRes.id = Integer.parseInt(parts[1]);
                currentRes.name = parts[2];
                currentRes.pdbNum = Integer.parseInt(parts[3]);
                currentRes.prev = Integer.parseInt(parts[4]);
                currentRes.next = Integer.parseInt(parts[5]);
                currentMol.residues.add(currentRes);
            } else if (keyword.equals("atom") && currentMol != null) {
                Atom atom = new Atom();
                atom.num = Integer.parseInt(parts[1]);
                atom.name = parts[2];
                atom.element = parts[3];
                atom.type = parts[4];
                atom.flags = parts[5];
                atom.charge = Double.parseDouble(parts[6]);
                atom.x = Double.parseDouble(parts[7]);
                atom.y = Double.parseDouble(parts[8]);
                atom.z = Double.parseDouble(parts[9]);
                atom.coordNum = Integer.parseInt(parts[10]);
                for (int i = 11; i < parts.length; i += 2) {
                    Atom.Bond bond = new Atom.Bond();
                    bond.to = Integer.parseInt(parts[i]);
                    bond.type = parts[i + 1];
                    atom.bonds.add(bond);
                }
                currentMol.atoms.add(atom);
            } // ... similar for vel, mass, basisset, formalcharge (store in atom or separate)
            else if (keyword.equals("endmol")) {
                currentMol = null;
            } else if (keyword.equals("endres")) {
                currentRes = null;
            }
        }
        br.close();
        printProperties();
    }

    private void printProperties() {
        globals.forEach((k, v) -> System.out.println(k.substring(0,1).toUpperCase() + k.substring(1) + ": " + v));
        for (Molecule mol : molecules) {
            System.out.println("Molecule " + mol.id + ": " + mol.name);
            for (Residue res : mol.residues) {
                System.out.println("  Residue " + res.id + " (" + res.name + "): PDB# " + res.pdbNum + ", Prev: " + res.prev + ", Next: " + res.next);
            }
            for (Atom atom : mol.atoms) {
                System.out.print("  Atom " + atom.num + ": Name=" + atom.name + ", Element=" + atom.element + ", Type=" + atom.type);
                System.out.print(", Flags=" + atom.flags + ", Charge=" + atom.charge);
                System.out.print(", Coords=(" + String.format("%.5f", atom.x) + ", " + String.format("%.5f", atom.y) + ", " + String.format("%.5f", atom.z) + ")");
                System.out.println(", CoordNum=" + atom.coordNum + ", Bonds=" + atom.bonds.stream().map(b -> "(" + b.to + "," + b.type + ")").toList());
            }
        }
    }

    public void write(String filename) throws IOException {
        PrintWriter pw = new PrintWriter(new FileWriter(filename));
        globals.forEach((k, v) -> pw.println(k + " " + v));
        for (Molecule mol : molecules) {
            pw.println("mol " + mol.id + " " + mol.name);
            for (Residue res : mol.residues) {
                pw.println("res " + res.id + " " + res.name + " " + res.pdbNum + " " + res.prev + " " + res.next);
            }
            for (Atom atom : mol.atoms) {
                String bondStr = atom.bonds.stream().map(b -> b.to + " " + b.type).reduce("", (a, b) -> a + " " + b).trim();
                pw.println("atom " + atom.num + " " + atom.name + " " + atom.element + " " + atom.type + " " + atom.flags + " " + atom.charge +
                           " " + atom.x + " " + atom.y + " " + atom.z + " " + atom.coordNum + " " + bondStr);
            }
            pw.println("endmol " + mol.id);
        }
        pw.close();
        System.out.println("Wrote to " + filename);
    }

    public static void main(String[] args) throws IOException {
        if (args.length < 1) return;
        HinFile parser = new HinFile();
        parser.read(args[0]);
        parser.write("output.hin");
    }
}

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

const fs = require('fs').promises;

class HinFile {
  constructor() {
    this.properties = {
      forcefield: null, sys: null, seed: null, view: null, box: null,
      molecules: []
    };
  }

  async read(filename) {
    const text = await fs.readFile(filename, 'utf8');
    this._parse(text);
    this._printProperties();
  }

  _parse(text) {
    const lines = text.split('\n');
    let currentMol = null;
    let currentRes = null;
    lines.forEach(line => {
      line = line.trim();
      if (!line || line.startsWith(';')) return;
      const parts = line.split(/\s+/);
      const keyword = parts[0].toLowerCase();
      if (keyword === 'forcefield') {
        this.properties.forcefield = parts[1];
      } else if (keyword === 'sys') {
        this.properties.sys = parts.slice(1).join(' ');
      } // ... similar for seed, view, box
      else if (keyword === 'mol') {
        currentMol = { id: parseInt(parts[1]), name: parts[2], atoms: [], residues: [] };
        this.properties.molecules.push(currentMol);
      } else if (keyword === 'res' && currentMol) {
        currentRes = {
          id: parseInt(parts[1]), name: parts[2], pdbNum: parseInt(parts[3]),
          prev: parseInt(parts[4]), next: parseInt(parts[5])
        };
        currentMol.residues.push(currentRes);
      } else if (keyword === 'atom' && currentMol) {
        const atomNum = parseInt(parts[1]);
        const atomName = parts[2];
        const element = parts[3];
        const atomType = parts[4];
        const flags = parts[5];
        const charge = parseFloat(parts[6]);
        const [x, y, z] = [parseFloat(parts[7]), parseFloat(parts[8]), parseFloat(parts[9])];
        const coordNum = parseInt(parts[10]);
        const bonds = [];
        for (let i = 11; i < parts.length; i += 2) {
          bonds.push({ to: parseInt(parts[i]), type: parts[i + 1] });
        }
        const atom = { num: atomNum, name: atomName, element, type: atomType, flags, charge, x, y, z, coordNum, bonds };
        currentMol.atoms.push(atom);
      } // ... similar for vel, mass, etc.
      else if (keyword === 'endmol') {
        currentMol = null;
      } else if (keyword === 'endres') {
        currentRes = null;
      }
    });
  }

  _printProperties() {
    if (this.properties.forcefield) console.log(`Forcefield: ${this.properties.forcefield}`);
    if (this.properties.sys) console.log(`System info: ${this.properties.sys}`);
    // ... similar for others
    this.properties.molecules.forEach(mol => {
      console.log(`Molecule ${mol.id}: ${mol.name}`);
      mol.residues.forEach(res => {
        console.log(`  Residue ${res.id} (${res.name}): PDB# ${res.pdbNum}, Prev: ${res.prev}, Next: ${res.next}`);
      });
      mol.atoms.forEach(atom => {
        console.log(`  Atom ${atom.num}: Name=${atom.name}, Element=${atom.element}, Type=${atom.type}, Flags=${atom.flags}, Charge=${atom.charge}, Coords=(${atom.x.toFixed(5)}, ${atom.y.toFixed(5)}, ${atom.z.toFixed(5)}), CoordNum=${atom.coordNum}, Bonds=${atom.bonds.map(b => `(${b.to},${b.type})`).join(', ')}`);
      });
    });
  }

  async write(filename) {
    let content = '';
    if (this.properties.forcefield) content += `forcefield ${this.properties.forcefield}\n`;
    // ... similar for globals
    this.properties.molecules.forEach(mol => {
      content += `mol ${mol.id} ${mol.name}\n`;
      mol.residues.forEach(res => {
        content += `res ${res.id} ${res.name} ${res.pdbNum} ${res.prev} ${res.next}\n`;
      });
      mol.atoms.forEach(atom => {
        const bondStr = atom.bonds.map(b => `${b.to} ${b.type}`).join(' ');
        content += `atom ${atom.num} ${atom.name} ${atom.element} ${atom.type} ${atom.flags} ${atom.charge} ${atom.x} ${atom.y} ${atom.z} ${atom.coordNum} ${bondStr}\n`;
      });
      content += `endmol ${mol.id}\n`;
    });
    await fs.writeFile(filename, content);
    console.log(`Wrote to ${filename}`);
}

// Usage: const parser = new HinFile(); await parser.read('example.hin'); await parser.write('output.hin');

7. C Class (Struct with Functions) for .HIN Handling

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAX_LINE 1024
#define MAX_ATOMS 1000
#define MAX_MOLS 10
#define MAX_BONDS 12

typedef struct {
    int id;
    char name[65];
    int num_atoms;
    // Simplified: atoms and residues as arrays; extend as needed
    // Atom struct: int num; char name[5], element[4], type[5], flags[10]; double charge, x,y,z; int coord_num; int bonds[MAX_BONDS][2]; // [to, type_code: 1=s,2=d,3=t,4=a]
} Molecule;

typedef struct {
    Molecule mols[MAX_MOLS];
    int num_mols;
    char forcefield[32];
    char sys[256]; // etc for seed, view, box
} HinFile;

HinFile hin;

void parse_line(char* line, Molecule* current_mol) {
    if (strlen(line) == 0 || line[0] == ';') return;
    char keyword[16];
    sscanf(line, "%s", keyword);
    if (strcmp(keyword, "forcefield") == 0) {
        sscanf(line + strlen(keyword) + 1, "%s", hin.forcefield);
    } else if (strcmp(keyword, "mol") == 0) {
        // Init new mol
    } else if (strcmp(keyword, "atom") == 0 && current_mol) {
        // Parse atom fields: sscanf(line + 5, "%d %s %s %s %s %lf %lf %lf %lf %d", &num, name, element, type, flags, &charge, &x, &y, &z, &coord_num);
        // Then parse bonds: manual loop with sscanf for pairs
        printf("Parsed atom line: %s\n", line); // Simplified dump
    } // ... extend for all keywords, res, vel, etc. Use strtok or sscanf for fields
    // For full impl, store in arrays
}

void read_hin(const char* filename) {
    FILE* fp = fopen(filename, "r");
    if (!fp) return;
    char line[MAX_LINE];
    Molecule* current_mol = NULL;
    while (fgets(line, MAX_LINE, fp)) {
        parse_line(line, current_mol);
        // Detect mol/endmol to switch current_mol
        if (strstr(line, "mol ")) current_mol = &hin.mols[hin.num_mols++];
        // Print globals: printf("Forcefield: %s\n", hin.forcefield);
        // For atoms: print fields as parsed
    }
    fclose(fp);
}

void write_hin(const char* filename) {
    FILE* fp = fopen(filename, "w");
    if (!fp) return;
    fprintf(fp, "forcefield %s\n", hin.forcefield);
    // Loop mols, fprintf atom lines from stored data
    fprintf(fp, "endmol 1\n"); // Example
    fclose(fp);
    printf("Wrote to %s\n", filename);
}

int main(int argc, char** argv) {
    if (argc < 2) return 1;
    read_hin(argv[1]);
    write_hin("output.hin");
    return 0;
}