Skip to content

Commit

Permalink
Add wrapAtoms option to address Issue #770
Browse files Browse the repository at this point in the history
  • Loading branch information
dkoes committed Mar 9, 2024
1 parent a2bc383 commit 37bd71b
Show file tree
Hide file tree
Showing 4 changed files with 285 additions and 29 deletions.
2 changes: 2 additions & 0 deletions src/parsers/ParserOptionsSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ import { AtomStyleSpec } from "GLModel";
doAssembly?: boolean;
/** Set to true if you wish to duplicate assembly atoms otherwise false ; supported by all formats with symmetries. Not duplicating will result in faster rendering but it will not be possible to individually style symmetries. */
duplicateAssemblyAtoms?: boolean;
/** Set to true with duplicateAssemblyAtoms to individually wrap atoms (from symmetries) into unit cell */
wrapAtoms?: boolean;
/** shift symmetry mates so their centroid is in the unit cell */
normalizeAssembly?: boolean;
/** do not detect bonds between symmetries generated with duplicateAssemblyAtoms (cif only - other formats never make bonds between symmetries) */
Expand Down
76 changes: 47 additions & 29 deletions src/parsers/utils/processSymmetries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,53 +15,65 @@ export function processSymmetries(
let offset = end;

let modifiedIdentity = -1;
if (options.normalizeAssembly && cryst) {
//to normalize, translate every symmetry so that the centroid is
//in the unit cell. To do this, convert back to fractional coordinates,
//compute the centroid, calculate any adjustment needed to get it in [0,1],
//convert the adjustment to a cartesian translation, and then add it to
//the symmetry matrix
const conversionMatrix = conversionMatrix3(
let conversionMatrix = null;
let toFrac = null;

if ((options.normalizeAssembly || options.wrapAtoms) && cryst) {
conversionMatrix = conversionMatrix3(
cryst.a,
cryst.b,
cryst.c,
cryst.alpha,
cryst.beta,
cryst.gamma
);
const toFrac = new Matrix3();
toFrac = new Matrix3();
toFrac.getInverse3(conversionMatrix);
}

let getAdjustment = function(v: Vector3) {
let c = v.clone().applyMatrix3(toFrac);
const coord = [c.x, c.y, c.z];
const adjustment = [0.0, 0.0, 0.0];
for (let i = 0; i < 3; i++) {
while (coord[i] < -0.001) {
coord[i] += 1.0;
adjustment[i] += 1.0;
}
while (coord[i] > 1.001) {
coord[i] -= 1.0;
adjustment[i] -= 1.0;
}
}
//convert adjustment to non-fractional
const adjustmentVec = new Vector3(
adjustment[0],
adjustment[1],
adjustment[2]
);
adjustmentVec.applyMatrix3(conversionMatrix);
return adjustmentVec;
};

if (options.normalizeAssembly && cryst) {
//to normalize, translate every symmetry so that the centroid is
//in the unit cell. To do this, convert back to fractional coordinates,
//compute the centroid, calculate any adjustment needed to get it in [0,1],
//convert the adjustment to a cartesian translation, and then add it to
//the symmetry matrix

for (let t = 0; t < copyMatrices.length; t++) {
//transform with the symmetry, and then back to fractional coordinates
const center = new Vector3(0, 0, 0);
for (let n = 0; n < end; n++) {
const xyz = new Vector3(atoms[n].x, atoms[n].y, atoms[n].z);
xyz.applyMatrix4(copyMatrices[t]);
xyz.applyMatrix3(toFrac);
//figure out
center.add(xyz);
}
center.divideScalar(end);
const centerCoord = [center.x, center.y, center.z];
const adjustment = [0.0, 0.0, 0.0];
for (let i = 0; i < 3; i++) {
while (centerCoord[i] < -0.001) {
centerCoord[i] += 1.0;
adjustment[i] += 1.0;
}
while (centerCoord[i] > 1.001) {
centerCoord[i] -= 1.0;
adjustment[i] -= 1.0;
}
}
//convert adjustment to non-fractional
const adjustmentVec = new Vector3(
adjustment[0],
adjustment[1],
adjustment[2]
);
adjustmentVec.applyMatrix3(conversionMatrix);

const adjustmentVec = getAdjustment(center);
//modify symmetry matrix to include translation
if (
copyMatrices[t].isNearlyIdentity() &&
Expand All @@ -79,7 +91,7 @@ export function processSymmetries(
}
for (let t = 0; t < copyMatrices.length; t++) {
if (!copyMatrices[t].isNearlyIdentity() && modifiedIdentity != t) {
const xyz = new Vector3();
let xyz = new Vector3();
for (let n = 0; n < end; n++) {
const bondsArr: number[] = [];
for (let l = 0; l < atoms[n].bonds.length; l++) {
Expand All @@ -88,6 +100,12 @@ export function processSymmetries(
xyz.set(atoms[n].x, atoms[n].y, atoms[n].z);
xyz.applyMatrix4(copyMatrices[t]);

if (options.wrapAtoms && cryst) {
//wrap per-atom instead of per matrix using the centroid
let adjustment = getAdjustment(xyz);
xyz.add(adjustment);
}

const newAtom: Record<string, unknown> = {};
for (const i in atoms[n]) {
newAtom[i] = atoms[n][i];
Expand Down
222 changes: 222 additions & 0 deletions tests/auto/data/HKUST-1.cif
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
data_HKUST-1Fm-3m
_audit_creation_method 'generated by CrystalMaker X for macOS'
_cell_length_a 26.343000
_cell_length_b 26.343000
_cell_length_c 26.343000
_cell_angle_alpha 90.000000
_cell_angle_beta 90.000000
_cell_angle_gamma 90.000000

_symmetry_space_group_name_H-M 'F m -3 m'

loop_
_symmetry_equiv_pos_as_xyz
'+x,+y,+z'
'+z,+x,+y'
'+y,+z,+x'
'+x,+y,-z'
'+z,+x,-y'
'+y,+z,-x'
'-x,+y,+z'
'-z,+x,+y'
'-y,+z,+x'
'-x,+y,-z'
'-z,+x,-y'
'-y,+z,-x'
'+y,+x,+z'
'+x,+z,+y'
'+z,+y,+x'
'+y,+x,-z'
'+x,+z,-y'
'+z,+y,-x'
'+y,-x,+z'
'+x,-z,+y'
'+z,-y,+x'
'+y,-x,-z'
'+x,-z,-y'
'+z,-y,-x'
'-x,-y,-z'
'-z,-x,-y'
'-y,-z,-x'
'-x,-y,+z'
'-z,-x,+y'
'-y,-z,+x'
'+x,-y,-z'
'+z,-x,-y'
'+y,-z,-x'
'+x,-y,+z'
'+z,-x,+y'
'+y,-z,+x'
'-y,-x,-z'
'-x,-z,-y'
'-z,-y,-x'
'-y,-x,+z'
'-x,-z,+y'
'-z,-y,+x'
'-y,+x,-z'
'-x,+z,-y'
'-z,+y,-x'
'-y,+x,+z'
'-x,+z,+y'
'-z,+y,+x'
'+x,1/2+y,1/2+z'
'1/2+x,1/2+y,+z'
'1/2+x,+y,1/2+z'
'+z,1/2+x,1/2+y'
'1/2+z,1/2+x,+y'
'1/2+z,+x,1/2+y'
'+y,1/2+z,1/2+x'
'1/2+y,1/2+z,+x'
'1/2+y,+z,1/2+x'
'+x,1/2+y,1/2-z'
'1/2+x,1/2+y,-z'
'1/2+x,+y,1/2-z'
'+z,1/2+x,1/2-y'
'1/2+z,1/2+x,-y'
'1/2+z,+x,1/2-y'
'+y,1/2+z,1/2-x'
'1/2+y,1/2+z,-x'
'1/2+y,+z,1/2-x'
'-x,1/2+y,1/2+z'
'1/2-x,1/2+y,+z'
'1/2-x,+y,1/2+z'
'-z,1/2+x,1/2+y'
'1/2-z,1/2+x,+y'
'1/2-z,+x,1/2+y'
'-y,1/2+z,1/2+x'
'1/2-y,1/2+z,+x'
'1/2-y,+z,1/2+x'
'-x,1/2+y,1/2-z'
'1/2-x,1/2+y,-z'
'1/2-x,+y,1/2-z'
'-z,1/2+x,1/2-y'
'1/2-z,1/2+x,-y'
'1/2-z,+x,1/2-y'
'-y,1/2+z,1/2-x'
'1/2-y,1/2+z,-x'
'1/2-y,+z,1/2-x'
'+y,1/2+x,1/2+z'
'1/2+y,1/2+x,+z'
'1/2+y,+x,1/2+z'
'+x,1/2+z,1/2+y'
'1/2+x,1/2+z,+y'
'1/2+x,+z,1/2+y'
'+z,1/2+y,1/2+x'
'1/2+z,1/2+y,+x'
'1/2+z,+y,1/2+x'
'+y,1/2+x,1/2-z'
'1/2+y,1/2+x,-z'
'1/2+y,+x,1/2-z'
'+x,1/2+z,1/2-y'
'1/2+x,1/2+z,-y'
'1/2+x,+z,1/2-y'
'+z,1/2+y,1/2-x'
'1/2+z,1/2+y,-x'
'1/2+z,+y,1/2-x'
'+y,1/2-x,1/2+z'
'1/2+y,1/2-x,+z'
'1/2+y,-x,1/2+z'
'+x,1/2-z,1/2+y'
'1/2+x,1/2-z,+y'
'1/2+x,-z,1/2+y'
'+z,1/2-y,1/2+x'
'1/2+z,1/2-y,+x'
'1/2+z,-y,1/2+x'
'+y,1/2-x,1/2-z'
'1/2+y,1/2-x,-z'
'1/2+y,-x,1/2-z'
'+x,1/2-z,1/2-y'
'1/2+x,1/2-z,-y'
'1/2+x,-z,1/2-y'
'+z,1/2-y,1/2-x'
'1/2+z,1/2-y,-x'
'1/2+z,-y,1/2-x'
'-x,1/2-y,1/2-z'
'1/2-x,1/2-y,-z'
'1/2-x,-y,1/2-z'
'-z,1/2-x,1/2-y'
'1/2-z,1/2-x,-y'
'1/2-z,-x,1/2-y'
'-y,1/2-z,1/2-x'
'1/2-y,1/2-z,-x'
'1/2-y,-z,1/2-x'
'-x,1/2-y,1/2+z'
'1/2-x,1/2-y,+z'
'1/2-x,-y,1/2+z'
'-z,1/2-x,1/2+y'
'1/2-z,1/2-x,+y'
'1/2-z,-x,1/2+y'
'-y,1/2-z,1/2+x'
'1/2-y,1/2-z,+x'
'1/2-y,-z,1/2+x'
'+x,1/2-y,1/2-z'
'1/2+x,1/2-y,-z'
'1/2+x,-y,1/2-z'
'+z,1/2-x,1/2-y'
'1/2+z,1/2-x,-y'
'1/2+z,-x,1/2-y'
'+y,1/2-z,1/2-x'
'1/2+y,1/2-z,-x'
'1/2+y,-z,1/2-x'
'+x,1/2-y,1/2+z'
'1/2+x,1/2-y,+z'
'1/2+x,-y,1/2+z'
'+z,1/2-x,1/2+y'
'1/2+z,1/2-x,+y'
'1/2+z,-x,1/2+y'
'+y,1/2-z,1/2+x'
'1/2+y,1/2-z,+x'
'1/2+y,-z,1/2+x'
'-y,1/2-x,1/2-z'
'1/2-y,1/2-x,-z'
'1/2-y,-x,1/2-z'
'-x,1/2-z,1/2-y'
'1/2-x,1/2-z,-y'
'1/2-x,-z,1/2-y'
'-z,1/2-y,1/2-x'
'1/2-z,1/2-y,-x'
'1/2-z,-y,1/2-x'
'-y,1/2-x,1/2+z'
'1/2-y,1/2-x,+z'
'1/2-y,-x,1/2+z'
'-x,1/2-z,1/2+y'
'1/2-x,1/2-z,+y'
'1/2-x,-z,1/2+y'
'-z,1/2-y,1/2+x'
'1/2-z,1/2-y,+x'
'1/2-z,-y,1/2+x'
'-y,1/2+x,1/2-z'
'1/2-y,1/2+x,-z'
'1/2-y,+x,1/2-z'
'-x,1/2+z,1/2-y'
'1/2-x,1/2+z,-y'
'1/2-x,+z,1/2-y'
'-z,1/2+y,1/2-x'
'1/2-z,1/2+y,-x'
'1/2-z,+y,1/2-x'
'-y,1/2+x,1/2+z'
'1/2-y,1/2+x,+z'
'1/2-y,+x,1/2+z'
'-x,1/2+z,1/2+y'
'1/2-x,1/2+z,+y'
'1/2-x,+z,1/2+y'
'-z,1/2+y,1/2+x'
'1/2-z,1/2+y,+x'
'1/2-z,+y,1/2+x'

loop_
_atom_site_label
_atom_site_type_symbol
_atom_site_occupancy
_atom_site_fract_x
_atom_site_fract_y
_atom_site_fract_z
C C 1.0 0.3219926 0.1780074 0.8870003
C_1 C 1.0 0.2968056 0.2031944 0.9313006
C_2 C 1.0 0.3654956 0.1993983 0.8654956
Cu Cu 1.0 0.2852655 0.7852655 0.5
H H 1.0 0.036243 0.1414797 0.8585203
H_1 H 1.0 0.3802054 0.2280018 0.8802054
O O 1.0 0.3166401 0.2431291 0.9477565
O_1 O 1.0 0.3434024 0.8434024 0.5

14 changes: 14 additions & 0 deletions tests/auto/tests/wrapatoms.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

//Issue #770
//normalize per-atom when duplicate is true
$.get('data/HKUST-1.cif', function(cif) {
let m = viewer.addModel(cif, 'cif',
{doAssembly: true, duplicateAssemblyAtoms: true,
wrapAtoms: true});

viewer.addStyle('stick');
viewer.addUnitCell(m);

viewer.zoomTo();
viewer.render();
});

0 comments on commit 37bd71b

Please sign in to comment.