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.
2. Two Direct Download Links for .HIN Files
- https://www2.chem.wisc.edu/deptfiles/genchem/netorial/modules/biomolecules/jmol-10.00/samples/hin/dan002.hin
- https://www2.chem.wisc.edu/deptfiles/genchem/netorial/modules/biomolecules/jmol-10.00/samples/hin/dan031.hin
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.
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;
}