// Bob Hanson hansonr@stolaf.edu // getWyckoff.spt // // 2023.10.24 Bob Hanson hansonr@stolaf.edu // // requires Jmol 16.1.43 or higher // // Create JSON of International Tables space group data, // including all settings found using the getgen program // for the settings (using &settings=ITA+Settings) // and for general positions (using &what=text and mat=-y,-z,x-y) // // For generators and Wyckoff positions, we use trgen // with flags what=gen and what=wpos. &trmat=c,-a-c,b is used rather than &trm=-y,-z,x-y // // for example: // // https://www.cryst.ehu.es/cgi-bin/cryst/programs/nph-getgen?gnum=7&what=text&mat=x,y,z // https://www.cryst.ehu.es/cgi-bin/cryst/programs/nph-getgen?gnum=7&settings=ITA+Settings // https://www.cryst.ehu.es/cgi-bin/cryst/programs//nph-trgen?gnum=51&what=gen&trmat=b,a,-c // // settings included are those from getgen, not wp-list: // // https://www.cryst.ehu.es/cgi-bin/cryst/programs/nph-wp-list?gnum=7&settings=ITA+Settings // // which are far more abundant. Note that with just a bit of modification, this script could be used to // create files from any desired list of transfomrmations. For now I a just using this simple list. // // General position coordinates for Wyckoff positions are skipped, as they are redundant. // // Output files are available in the src/org/jmol/symmetry/sg/json folder // // a bit of hardwiring: targetDir = "c:/temp/bilbao/wyckoff/"; // variables for the createJSONFiles call (below), after the functions itadelay = 0.1; itaFirst = 0; itaForceNew = false; // only one function to run here: function createJSONFiles(n, forceNew) { // creates ita_.json, by space group // n option to start at a given number (-30), or only a certain number (30), or all (0) // forceNew option true to overwrite local files; false to use them if they are there. var allSettings = []; var allOps = []; for (var i = 1; i <= 230; i++) { if (n < 0 && i < -n || n > 0 && i != n) { continue; } _getAllOps(i, allOps); var adata = _createJSONArray(i, forceNew, allSettings); var thedata = adata.format("JSON"); var fname = targetDir + "json/ita_" + i + ".json"; write var thedata @fname; } if (n == 0 || n == -1) { var thedata = allSettings.format("JSON"); var fname = targetDir + "json/ita_all.json"; write var thedata @fname; var thedata = allOps.format("JSON"); var fname = targetDir + "json/ops_all.json"; write var thedata @fname; print "done -- " + allSettings.length + " settings and " + allOps.length + " operations"; } } function _getAllOps(sg, allOps) { var y = _getITADataHTM(sg, 1, "gp_full", "", forceNew).split('")[2].split("")[1].trim().replace("\n"," |"); var ita = _fixName((y[i+1])[2][0].split("","_")); allOps.push({"sg":i,"xyz":xyz,"ita":ita,"seitz":seitz,"m":m}); } } // all other functions are "private" to this script function _createJSONArray(i, forceNew, allSettings) { // Create ITA records for a specific space group. // start by getting the ITA settings using getgen // some reordering is done for groups 48, 50, 59, 68, and 78, // which for some reason are not presented with the default (a,b,c) transformation // as the first setting on the list. var x = _getITADataHTM(i, 0, "", "", forceNew); var x = x.split("")[1]); } its.id = _fixName(its.u.split("[")[1].split("(")[1]).replace(" ",""); var tm = _getField(href, "trmat", 100); if (!tm)tm = "a,b,c"; its.tm = tm; its.set = j; its.sg = i; if (tm == "a,b,c" && j > 1 || outOfOrder) { // out of order! -- we want origin 2 to be first here because it is the default // space groups 48, 50, 59, 68, and 70 if (!outOfOrder) { for (var k = sg.its.length; k > 0; --k) { var kk = k + x.length/2; var itk = sg.its[k]; sg.its[kk] = itk; } } k0++; sg.its[k0] = its; outOfOrder = true; } else { sg.its.push(its); } var data = _getGPText(i, j, its.tm, forceNew); its.gp = _getGPJson(its, data); data = _getITADataHTM(i, j, "gen", its.tm, forceNew); its.gen = _getGeneratorJson(data); data = _getITADataHTM(i, j, "wpos", its.tm, forceNew); its.wpos = _getWyckoffJson(its, data); } for (var j = 1; j <= sg.its.length; j++) { sg.its[j].set = j; allSettings.push(sg.its[j]); } return sg; } function _getITADataHTM(i, j, what, trmat, forceNew) { // the main function for retieving data from trgen or getgen var localFile = targetDir + "data/" + if(what ; what ; "its") + "_" + i + if(j;"_"+j+".html";".htm"); var prog = if(trmat ; "trgen" ; "getgen"); var url = if(forceNew ; "https://www.cryst.ehu.es/cgi-bin/cryst/programs/nph-" + prog + "?gnum=" + i + if(what ; "&what=" + what.split("_")[1] ; "&settings=ITA+Settings") + if(trmat ; "&trmat=" + trmat ; "") ; localFile); print "getting " + url; delay @itadelay; var data = load(url); if (forceNew) { write var data @localFile; } else if (data.find("FileNotFound")) { data = _getITADataHTM(i, j, what, trmat, true); } return data; } function _getGPText(i, j, trmat, forceNew) { // specifically for retieving data from getgen for general position text var localFile = targetDir + "data/gp_" + i + if(j;"_"+j+".html";".htm"); var url = if(forceNew ; "https://www.cryst.ehu.es/cgi-bin/cryst/programs/nph-getgen" + "?gnum=" + i + "&what=text&mat=" + _toXYZ(trmat) ; localFile); print "getting " + url; delay @itadelay; var data = load(url); if (forceNew) { write var data @localFile; } else if (data.find("FileNotFound")) { data = _getGPText(i, j, trmat, true); } return data; } function _getGPJson(its, data) { // just split the text on the tag var gp = []; var d = data.split(""); for (var j = 2; j <= d.length; j++) { gp.push(d[j].split(" ")[2].split("<")[1]); } var sg = spacegroup(gp.join(";")); if (sg && sg.type == "hash") { its.itaFull = sg.itaFull; } else { its.itaFull = "ita?" + i + "." + isetting; } return gp; } function _getGeneratorJson(data) { // parse the generator list using tags. All cases return two columns here, // so we always just skip the first column. var gen = []; var y = data.split(""); for (var j = 5; j <= y.length; j += 3) { var coor = y[j++].split("")[1] + "," + y[j++].split("")[1] + "," + y[j++].split("")[1]; gen.push(coor); } return gen; } function _getWyckoffJson(its, data) { // Wyckoff positions are a bit trickier. // currently adding some Jmol-derived geometric elements here, just for the first member of the list. var wyck = {}; data = data.split("NOTE")[1]; var isUnconv = data.find("Standard/Default setting"); var w = data.split("Wyck"); var y = w[4].split("align=center>")[2][0]; var pos = []; wyck.pos = pos; var n = 0; for (var j = 1; j <= y.length; j++) { if (j == 1 && y[j].find("(")) { if (isUnconv) { // skip first column j++; } var c = y[j].split(""); var coor = []; for (var k = 2; k <= td.length; k++) { var t = td[k]; if (t.find('(') == 1) { t = '">' + t; } if (t.find('">') && t.find(')')) { var p = t.split('">')[2].split("(")[2].split(")")[1]; coor.push(p); } } if (++n == 1) { // skip first, which is general positions in a transformed format d.geom = "general"; } else { d.coord = coor; d.geom = _getWyckoffElement(coor[1]); } pos.push(d); } return wyck; } function _getWyckoffElement(p) { var xyz = p.split(","); var n = 0; var nxyz = ({}); for (var i = 1; i <= 3; i++) { if (xyz[i].find('x')) { nxyz |= 1; } if (xyz[i].find('y')) { nxyz |= 2; } if (xyz[i].find('z')) { nxyz |= 3; } } switch (nxyz.length) { case 0: return "point"; case 1: return "line"; case 2: return "plane"; } return "general"; } // utility methods function _fixName(s) { // remove HTML markings return (s.trim() .replace("","").replace("","") .replace("","").replace("","") .replace("","").replace("","") .replace("|","|")); } function _toXYZ(abc){ // getGen needs the transposed matrix, in xyz format. // this does the trick, as -m4 is the transpose of m4, and symop(matrix, "xyz") // will accept a,b,c and return x,y,z // check for translation and don't transpose that var a = symop(abc, "matrix"); var t = a%2; // get translation vector if (1.0 * t == 0) { // transpose if no translation a = -a; } else { // remove translation from 4x4, transpose it, then return translation a = -(a + -t) + t; } return symop(a, "xyz"); } function _getField(data, field, max) { // get a &... field form a URL var i = data.find(field + "="); return (i == 0 ? "" : (data[i+field.length + 1][i+max] + "&").split("&")[1]); } // run the function createJSONFiles(itaFirst, itaForceNew); /** Lots missing, but no first setting at least. First one missing is #75 (tetragonols) and last is #167 (trigonals, :r option never implemented in Jmol).