From 1f71eae77e6929c2a4d98f6b469b4e02648a3062 Mon Sep 17 00:00:00 2001 From: "stan.donarise" Date: Wed, 17 Jul 2024 11:38:20 +0300 Subject: [PATCH 1/4] coincided atoms handling fix #42 --- matinfio/matinfio.web.ts | 16 +--- matinfio/player/player.web.ts | 122 ++++++++++++++++---------- matinfio/spacegroup/spacegroup.web.ts | 11 ++- 3 files changed, 83 insertions(+), 66 deletions(-) diff --git a/matinfio/matinfio.web.ts b/matinfio/matinfio.web.ts index 4f27d12..2090f84 100644 --- a/matinfio/matinfio.web.ts +++ b/matinfio/matinfio.web.ts @@ -30,7 +30,7 @@ namespace $ { x: number, y: number, z: number, - }, + } | null, x: number, y: number, z: number, @@ -60,19 +60,7 @@ namespace $ { symlabel?: string, }, overlayed: Record< string, string >, - atoms: { - fract: { - x: number, - y: number, - z: number, - } | null, - x: number, - y: number, - z: number, - c: string, //color - r: number, //radius - overlays: Record< string, string | number >, - }[], + atoms: $optimade_cifplayer_matinfio_internal_obj_atom[] sg_name: string, ng_name: number, info: string, diff --git a/matinfio/player/player.web.ts b/matinfio/player/player.web.ts index e71612e..c955822 100644 --- a/matinfio/player/player.web.ts +++ b/matinfio/player/player.web.ts @@ -37,63 +37,87 @@ namespace $ { mpds_data: crystal.mpds_data, mpds_demo: crystal.mpds_demo } - const pos2els: any = {} - const hashes: any = {} + + const in_pos: Map< string, $optimade_cifplayer_matinfio_internal_obj_atom[] > = new Map for( let i = 0; i < crystal.atoms.length; i++ ) { - const pos = [ crystal.atoms[ i ].x, crystal.atoms[ i ].y, crystal.atoms[ i ].z ] - const hash = pos.map( function( item ) { return item.toFixed( 2 ) } ).join( ',' ) + const atom = crystal.atoms[ i ] + + const pos = [ atom.x, atom.y, atom.z ] + + // CIF has fractional positions + // OPTIMADE has cartesian positions + // POSCAR may have either of two + let cpos = pos + let fpos: number[] | null = null + if( !crystal.cartesian ) { + fpos = pos.map( fract_cord_norm ) + cpos = math.multiply( fpos, cell_matrix ) + + } else if( cell_matrix ) { + fpos = math.divide( pos, cell_matrix ).map( fract_cord_norm ) + } + + const pos_hash = ( fpos ?? cpos ).map( c => c.toFixed( 2 ) ).join( ',' ) + // make atoms unique, i.e. remove collisions; // makes special sense for partial occupancies - if( hashes.hasOwnProperty( hash ) ) { - var update = "" - for( let oprop in render.atoms[ hashes[ hash ] ].overlays ) { + if( in_pos.get( pos_hash )?.length! > 0 ) { + + const first = in_pos.get( pos_hash )![0] + + for( let oprop in first.overlays ) { + if( oprop == 'S' ) { - if( pos2els[ hash ].indexOf( crystal.atoms[ i ].symbol ) == -1 ) { - update = " " + crystal.atoms[ i ].symbol - pos2els[ hash ].push( crystal.atoms[ i ].symbol ) + if( in_pos.get( pos_hash )?.every( a => a.symbol != atom.symbol ) ) { + first.overlays[ oprop ] += ' ' + atom.symbol } + + } else if( oprop == 'N' ) { + first.overlays[ oprop ] += ', ' + ( i + 1 ) + + } else if( oprop == '_atom_site_occupancy' ) { + first.overlays[ oprop ] += '+' + atom.overlays[ oprop ] + + } else { + first.overlays[ oprop ] += ' ' + atom.overlays[ oprop ] } - else if( oprop == 'N' ) - update = ", " + ( i + 1 ) - else if( oprop == '_atom_site_occupancy' ) - update = "+" + crystal.atoms[ i ].overlays[ oprop ] - else - update = " " + crystal.atoms[ i ].overlays[ oprop ] - - render.atoms[ hashes[ hash ] ].overlays[ oprop ] += update + } - } else { - const color = ($optimade_cifplayer_matinfio_chemical_elements.JmolColors as any)[ crystal.atoms[ i ].symbol ] || '#FFFF00' - const radius = ($optimade_cifplayer_matinfio_chemical_elements.AseRadii as any)[ crystal.atoms[ i ].symbol ] || 0.66 - const overlays: Record< string, string | number > = { - "S": crystal.atoms[ i ].symbol, - "N": i + 1, - } - for( let oprop in crystal.atoms[ i ].overlays ) { - overlays[ oprop ] = crystal.atoms[ i ].overlays[ oprop ] - } + continue + } + + const overlays: Record< string, string | number > = { + "S": atom.symbol, + "N": i + 1, + } + for( let oprop in atom.overlays ) { + overlays[ oprop ] = atom.overlays[ oprop ] + } - // CIF has fractional positions - // OPTIMADE has cartesian positions - // POSCAR may have either of two - const cpos = crystal.cartesian ? pos : math.multiply( pos, cell_matrix ) - const fpos = !crystal.cartesian ? pos : cell_matrix ? math.divide( pos, cell_matrix ) : null - const fract = fpos ? { 'x': fpos[ 0 ], 'y': fpos[ 1 ], 'z': fpos[ 2 ] } : null - - render.atoms.push( { - 'fract': fract, - 'x': cpos[ 0 ], - 'y': cpos[ 1 ], - 'z': cpos[ 2 ], - 'c': color, - 'r': radius, - 'overlays': overlays - } ) - hashes[ hash ] = render.atoms.length - 1 - pos2els[ hash ] = [ crystal.atoms[ i ].symbol ] + const color = ($optimade_cifplayer_matinfio_chemical_elements.JmolColors as any)[ atom.symbol ] || '#FFFF00' + const radius = ($optimade_cifplayer_matinfio_chemical_elements.AseRadii as any)[ atom.symbol ] || 0.66 + const atom_result = { + fract: fpos ? { + x: fpos[ 0 ], + y: fpos[ 1 ], + z: fpos[ 2 ], + } : null, + x: cpos[ 0 ], + y: cpos[ 1 ], + z: cpos[ 2 ], + c: color, + r: radius, + overlays, + symbol: atom.symbol, + label: atom.label, } + + render.atoms.push( atom_result ) + + in_pos.get( pos_hash )?.push( atom_result ) ?? in_pos.set( pos_hash, [ atom_result ] ) + } for( let oprop in crystal.atoms.at(-1)!.overlays ) { @@ -102,5 +126,11 @@ namespace $ { return render } + + + function fract_cord_norm( cord: number ){ + const res = cord % 1 + return res > 0 ? res : res + 1 + } } diff --git a/matinfio/spacegroup/spacegroup.web.ts b/matinfio/spacegroup/spacegroup.web.ts index 0b360bc..47b0f3e 100644 --- a/matinfio/spacegroup/spacegroup.web.ts +++ b/matinfio/spacegroup/spacegroup.web.ts @@ -42,9 +42,9 @@ namespace $ { const spans = symmetry.split( ',' ) const fract = { - x: fract_cord_norm( calc_symmetry_span( spans[ 0 ], atom.fract ) ), - y: fract_cord_norm( calc_symmetry_span( spans[ 1 ], atom.fract ) ), - z: fract_cord_norm( calc_symmetry_span( spans[ 2 ], atom.fract ) ), + x: fract_cord_norm( calc_symmetry_span( spans[ 0 ], atom.fract! ) ), + y: fract_cord_norm( calc_symmetry_span( spans[ 1 ], atom.fract! ) ), + z: fract_cord_norm( calc_symmetry_span( spans[ 2 ], atom.fract! ) ), } const [ x, y, z ] = math.multiply( [ fract.x, fract.y, fract.z ], cell ) @@ -96,9 +96,8 @@ namespace $ { } function fract_cord_norm( cord: number ){ - let res = cord % 1 - if( res < 0 ) res = res + 1 - return res + const res = cord % 1 + return res > 0 ? res : res + 1 } } From 88933e500ed47b52c1af34254b51d3a0fe952fcf Mon Sep 17 00:00:00 2001 From: "stan.donarise" Date: Wed, 17 Jul 2024 12:13:32 +0300 Subject: [PATCH 2/4] matinfio_chemical_elements type --- matinfio/cif/cif.ts | 2 +- matinfio/matinfio.web.ts | 5 ++++- matinfio/player/player.web.ts | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/matinfio/cif/cif.ts b/matinfio/cif/cif.ts index b15e9c3..5e10fea 100644 --- a/matinfio/cif/cif.ts +++ b/matinfio/cif/cif.ts @@ -148,7 +148,7 @@ namespace $ { atom.overlays.label = atom.label if( !atom.symbol ) atom.symbol = atom.label.replace( /[0-9]/g, '' ) } - if( !( $optimade_cifplayer_matinfio_chemical_elements.JmolColors as any )[ atom.symbol ] + if( ! $optimade_cifplayer_matinfio_chemical_elements.JmolColors[ atom.symbol ] && atom.symbol && atom.symbol.length > 1 ) { diff --git a/matinfio/matinfio.web.ts b/matinfio/matinfio.web.ts index 2090f84..219911a 100644 --- a/matinfio/matinfio.web.ts +++ b/matinfio/matinfio.web.ts @@ -1,6 +1,9 @@ namespace $ { - export const $optimade_cifplayer_matinfio_chemical_elements = { + export const $optimade_cifplayer_matinfio_chemical_elements: { + JmolColors: Record< string, string >, + AseRadii: Record< string, number >, + } = { JmolColors: { "D": "#FFFFC0", "H": "#FFFFFF", "He": "#D9FFFF", "Li": "#CC80FF", "Be": "#C2FF00", "B": "#FFB5B5", "C": "#909090", "N": "#3050F8", "O": "#FF0D0D", "F": "#90E050", "Ne": "#B3E3F5", "Na": "#AB5CF2", "Mg": "#8AFF00", "Al": "#BFA6A6", "Si": "#F0C8A0", "P": "#FF8000", "S": "#FFFF30", "Cl": "#1FF01F", "Ar": "#80D1E3", "K": "#8F40D4", "Ca": "#3DFF00", "Sc": "#E6E6E6", "Ti": "#BFC2C7", "V": "#A6A6AB", "Cr": "#8A99C7", "Mn": "#9C7AC7", "Fe": "#E06633", "Co": "#F090A0", "Ni": "#50D050", "Cu": "#C88033", "Zn": "#7D80B0", "Ga": "#C28F8F", "Ge": "#668F8F", "As": "#BD80E3", "Se": "#FFA100", "Br": "#A62929", "Kr": "#5CB8D1", "Rb": "#702EB0", "Sr": "#00FF00", "Y": "#94FFFF", "Zr": "#94E0E0", "Nb": "#73C2C9", "Mo": "#54B5B5", "Tc": "#3B9E9E", "Ru": "#248F8F", "Rh": "#0A7D8C", "Pd": "#006985", "Ag": "#C0C0C0", "Cd": "#FFD98F", "In": "#A67573", "Sn": "#668080", "Sb": "#9E63B5", "Te": "#D47A00", "I": "#940094", "Xe": "#429EB0", "Cs": "#57178F", "Ba": "#00C900", "La": "#70D4FF", "Ce": "#FFFFC7", "Pr": "#D9FFC7", "Nd": "#C7FFC7", "Pm": "#A3FFC7", "Sm": "#8FFFC7", "Eu": "#61FFC7", "Gd": "#45FFC7", "Tb": "#30FFC7", "Dy": "#1FFFC7", "Ho": "#00FF9C", "Er": "#00E675", "Tm": "#00D452", "Yb": "#00BF38", "Lu": "#00AB24", "Hf": "#4DC2FF", "Ta": "#4DA6FF", "W": "#2194D6", "Re": "#267DAB", "Os": "#266696", "Ir": "#175487", "Pt": "#D0D0E0", "Au": "#FFD123", "Hg": "#B8B8D0", "Tl": "#A6544D", "Pb": "#575961", "Bi": "#9E4FB5", "Po": "#AB5C00", "At": "#754F45", "Rn": "#428296", "Fr": "#420066", "Ra": "#007D00", "Ac": "#70ABFA", "Th": "#00BAFF", "Pa": "#00A1FF", "U": "#008FFF", "Np": "#0080FF", "Pu": "#006BFF", "Am": "#545CF2", "Cm": "#785CE3", "Bk": "#8A4FE3", "Cf": "#A136D4", "Es": "#B31FD4", "Fm": "#B31FBA", "Md": "#B30DA6", "No": "#BD0D87", "Lr": "#C70066", "Rf": "#CC0059", "Db": "#D1004F", "Sg": "#D90045", "Bh": "#E00038", "Hs": "#E6002E", "Mt": "#EB0026" }, // NB starting from Bk the radii data are incorrect diff --git a/matinfio/player/player.web.ts b/matinfio/player/player.web.ts index c955822..abd3416 100644 --- a/matinfio/player/player.web.ts +++ b/matinfio/player/player.web.ts @@ -96,8 +96,8 @@ namespace $ { overlays[ oprop ] = atom.overlays[ oprop ] } - const color = ($optimade_cifplayer_matinfio_chemical_elements.JmolColors as any)[ atom.symbol ] || '#FFFF00' - const radius = ($optimade_cifplayer_matinfio_chemical_elements.AseRadii as any)[ atom.symbol ] || 0.66 + const color = $optimade_cifplayer_matinfio_chemical_elements.JmolColors[ atom.symbol ] || '#FFFF00' + const radius = $optimade_cifplayer_matinfio_chemical_elements.AseRadii[ atom.symbol ] || 0.66 const atom_result = { fract: fpos ? { x: fpos[ 0 ], From 2d714a2e6f48481721c55cab8d69d057158d3ba1 Mon Sep 17 00:00:00 2001 From: "stan.donarise" Date: Wed, 17 Jul 2024 15:36:13 +0300 Subject: [PATCH 3/4] matinfio.pos_overlap_limit added --- matinfio/matinfio.web.ts | 2 ++ matinfio/player/player.web.ts | 17 +++++++---------- player/player.view.web.ts | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/matinfio/matinfio.web.ts b/matinfio/matinfio.web.ts index 219911a..413c48f 100644 --- a/matinfio/matinfio.web.ts +++ b/matinfio/matinfio.web.ts @@ -73,6 +73,8 @@ namespace $ { export class $optimade_cifplayer_matinfio extends $mol_object2 { + static pos_overlap_limit = 0.1 + static log = this.$.$optimade_cifplayer_matinfio_log /** Guessing what to do */ diff --git a/matinfio/player/player.web.ts b/matinfio/player/player.web.ts index abd3416..b958e16 100644 --- a/matinfio/player/player.web.ts +++ b/matinfio/player/player.web.ts @@ -48,17 +48,14 @@ namespace $ { // CIF has fractional positions // OPTIMADE has cartesian positions // POSCAR may have either of two - let cpos = pos - let fpos: number[] | null = null - if( !crystal.cartesian ) { - fpos = pos.map( fract_cord_norm ) - cpos = math.multiply( fpos, cell_matrix ) - - } else if( cell_matrix ) { - fpos = math.divide( pos, cell_matrix ).map( fract_cord_norm ) - } + + const fpos: number[] | null = crystal.cartesian + ? cell_matrix ? math.divide( pos, cell_matrix ).map( fract_cord_norm ) : null + : pos.map( fract_cord_norm ) + + const cpos: number[] = fpos ? math.multiply( fpos, cell_matrix ) : pos - const pos_hash = ( fpos ?? cpos ).map( c => c.toFixed( 2 ) ).join( ',' ) + const pos_hash = cpos.map( c => Math.round( c / $optimade_cifplayer_matinfio.pos_overlap_limit ) ).join( ',' ) // make atoms unique, i.e. remove collisions; // makes special sense for partial occupancies diff --git a/player/player.view.web.ts b/player/player.view.web.ts index 5449a99..b3facf6 100644 --- a/player/player.view.web.ts +++ b/player/player.view.web.ts @@ -261,7 +261,7 @@ namespace $.$$ { for (const name of next_symmetries) { const atoms = this.symmetry_atoms( name )! - if( is_overlap( data, atoms, 0.01 ) ) { + if( is_overlap( data, atoms, $optimade_cifplayer_matinfio.pos_overlap_limit ) ) { return } } From aef5daf0accbf25fe7b21778fc39cade159a3f2c Mon Sep 17 00:00:00 2001 From: "stan.donarise" Date: Mon, 29 Jul 2024 06:12:33 +0300 Subject: [PATCH 4/4] atom with the largest AceRadii between coincided --- matinfio/player/player.web.ts | 81 ++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 30 deletions(-) diff --git a/matinfio/player/player.web.ts b/matinfio/player/player.web.ts index b958e16..d1c3ce7 100644 --- a/matinfio/player/player.web.ts +++ b/matinfio/player/player.web.ts @@ -38,8 +38,9 @@ namespace $ { mpds_demo: crystal.mpds_demo } - const in_pos: Map< string, $optimade_cifplayer_matinfio_internal_obj_atom[] > = new Map + const groups: { fpos: number[] | null, cpos: number[], atoms: $optimade_cifplayer_matinfio_internal_obj_atom[] }[] = [] + // make atoms unique, i.e. remove collisions; for( let i = 0; i < crystal.atoms.length; i++ ) { const atom = crystal.atoms[ i ] @@ -48,53 +49,68 @@ namespace $ { // CIF has fractional positions // OPTIMADE has cartesian positions // POSCAR may have either of two - const fpos: number[] | null = crystal.cartesian ? cell_matrix ? math.divide( pos, cell_matrix ).map( fract_cord_norm ) : null : pos.map( fract_cord_norm ) const cpos: number[] = fpos ? math.multiply( fpos, cell_matrix ) : pos - const pos_hash = cpos.map( c => Math.round( c / $optimade_cifplayer_matinfio.pos_overlap_limit ) ).join( ',' ) + if( groups.some( group => { + if( is_overlap( cpos, group.cpos, $optimade_cifplayer_matinfio.pos_overlap_limit ) ) { + + const AseRadii = $optimade_cifplayer_matinfio_chemical_elements.AseRadii[ atom.symbol ] + const pos = group.atoms.findIndex( atom2 => { + return AseRadii > $optimade_cifplayer_matinfio_chemical_elements.AseRadii[ atom2.symbol ] + } ) + + if( pos == -1 ) group.atoms.push( atom ) + else group.atoms.splice( pos, 0, atom ) + + return true - // make atoms unique, i.e. remove collisions; - // makes special sense for partial occupancies - if( in_pos.get( pos_hash )?.length! > 0 ) { + } + } ) ) { + continue + } - const first = in_pos.get( pos_hash )![0] + groups.push( { fpos, cpos, atoms: [ atom ] } ) + } + + for( let i = 0; i < groups.length; i++ ) { + + const { fpos, cpos, atoms } = groups[i] + + const overlays: Record< string, string | number > = { + "S": atoms[0].symbol, + "N": i + 1, + } + for( let oprop in atoms[0].overlays ) { + overlays[ oprop ] = atoms[0].overlays[ oprop ] + } - for( let oprop in first.overlays ) { + atoms.slice(1).forEach( atom => { + for( let oprop in overlays ) { if( oprop == 'S' ) { - if( in_pos.get( pos_hash )?.every( a => a.symbol != atom.symbol ) ) { - first.overlays[ oprop ] += ' ' + atom.symbol + if( atoms.every( a => a.symbol != atom.symbol ) ) { + overlays[ oprop ] += ' ' + atom.symbol } } else if( oprop == 'N' ) { - first.overlays[ oprop ] += ', ' + ( i + 1 ) + overlays[ oprop ] += ', ' + ( i + 1 ) } else if( oprop == '_atom_site_occupancy' ) { - first.overlays[ oprop ] += '+' + atom.overlays[ oprop ] + overlays[ oprop ] += '+' + atom.overlays[ oprop ] } else { - first.overlays[ oprop ] += ' ' + atom.overlays[ oprop ] + overlays[ oprop ] += ' ' + atom.overlays[ oprop ] } } - - continue - } - - const overlays: Record< string, string | number > = { - "S": atom.symbol, - "N": i + 1, - } - for( let oprop in atom.overlays ) { - overlays[ oprop ] = atom.overlays[ oprop ] - } + } ) - const color = $optimade_cifplayer_matinfio_chemical_elements.JmolColors[ atom.symbol ] || '#FFFF00' - const radius = $optimade_cifplayer_matinfio_chemical_elements.AseRadii[ atom.symbol ] || 0.66 + const color = $optimade_cifplayer_matinfio_chemical_elements.JmolColors[ atoms[0].symbol ] || '#FFFF00' + const radius = $optimade_cifplayer_matinfio_chemical_elements.AseRadii[ atoms[0].symbol ] || 0.66 const atom_result = { fract: fpos ? { x: fpos[ 0 ], @@ -107,13 +123,11 @@ namespace $ { c: color, r: radius, overlays, - symbol: atom.symbol, - label: atom.label, + symbol: atoms[0].symbol, + label: atoms[0].label, } render.atoms.push( atom_result ) - - in_pos.get( pos_hash )?.push( atom_result ) ?? in_pos.set( pos_hash, [ atom_result ] ) } @@ -130,4 +144,11 @@ namespace $ { return res > 0 ? res : res + 1 } + function is_overlap( pos1: number[], pos2: number[], threshold: number ) { + for( let i = 0; i < 3; i++ ) { + if ( pos1[i] < pos2[i] - threshold || pos1[i] > pos2[i] + threshold ) return false + } + return true + } + }