// Bob Hanson hansonr@stolaf.edu // getPlaneGroups.spt // // 2024.09.28 Bob Hanson hansonr@stolaf.edu // // Create JSON of International Tables plane groups // // 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/plane/programs/nph-plane_getgen?gnum=5&what=gp&type=plane // https://www.cryst.ehu.es/cgi-bin/plane/programs/nph-plane_getgen?gnum=5&what=gen&type=plane // // Output files are available in the src/org/jmol/symmetry/sg/json folder // // a bit of hardwiring: targetDir = "c:/temp/bilbao/ita/"; // variables for the createJSONFiles call (below), after the functions issues = []; usetext = false; itadelay = 0.1; itaFirst = 0; itaLast = 17; itaForceNew = false; ncleg = 0; // 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 allMore = []; var allTransforms = [["i", "subtype", "clegId","det","hm","more"]]; // , "xyz"]]; //var allOps = []; for (var i = 1; i <= itaLast; i++) { if (n < 0 && i < -n || n > 0 && i != n) { continue; } //_getAllOps(i, allOps); var adata = _createJSONArray(i, forceNew, allSettings, allMore, allTransforms); var thedata = adata.format("JSON"); var fname = targetDir + "jsonp/plane_" +i + ".json"; write var thedata @fname; } if (n == 0 || n == -1) { allSettings += allMore; var thedata = allSettings.format("JSON"); var fname = targetDir + "json/ita_all_plane.json"; write var thedata @fname; for (var g in allSettings) { if (g.gen) { g.ngen = g.pop("gen").length; g.ngp = g.pop("gp").length; g.nwpos = g.pop("wpos").pos.length; } } // fname = targetDir + "json/plane_list.json"; // thedata = allSettings.format("JSON"); // write var thedata @fname; var thedata = allTransforms.join("\t",TRUE); var fname = targetDir + "txt/cleg_settings_plane.tab"; write var thedata @fname; print "done -- " + allSettings.length + " settings"; } print issues; } function _getAllOps(sg, allOps) { // "gp_full" does something different - ignored, and gets just generators var y = _getITADataHTM(sg, 1, "gp", "", "", 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, allMore, allTransforms) { // 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); // this will be generators var myTransforms = ""; //var x = x.split(" tag // normalize -n/m to +(1-n/m); "+1," to "," var gp = []; var d = gdata.split(""); for (var j = 2; j <= d.length; j++) { var xyz0 = d[j].split(" ")[2].split("<")[1]; var xyz = _fixUnitXYZ(xyz0 + "z"); gp.push(xyz); } return gp; } function _fixURI(s) { return s.replace(" ","%20"); } function _fixUnitXYZ(xyz) { // this only for translation // ITA gives non-normalized transformed operations for several settings, particularly when // rotating to F from I or C from P in tetragonals, h to r in trigonals, and I to F in cubics. // normalizes the code and removes the duplicated operators in case those are created return (xyz + ",").replace("-1/2","+1/2").replace("-3/2","+1/2").replace("+3/2","+1/2").replace("-1/4","+3/4").replace("-3/4","+1/4").replace("-1/3","+2/3").replace("-2/3","+1/3").replace("-1/6","+5/6").replace("-5/6","+1/6").replace("-1/8","+7/8").replace("-3/8","+5/8").replace("-7/8","+1/8").replace("-1/12","+11/12").replace("-5/12","+7/12").replace("-7/12","+5/12").replace("-11/12","+1/12").replace("+1,",",")[1][-1]; } function _getGeneratorJson(gdata, genOrgp) { // parse the generator list using tags. All cases return two columns here, // so we always just skip the first column. var gen = []; var y = gdata.split(""); if (y.length == 1) { y= gdata.split("   "); for (var j = 2; j <= y.length; j+=2) { var coor = y[j].replace(" ","") + ",z"; gen.push(_fixUnitXYZ(coor)); } } else { 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(_fixUnitXYZ(coor)); } } return gen; } function _getWyckoffJson(its, gdata) { // Wyckoff positions are a bit trickier. // currently adding some Jmol-derived geometric elements here, just for the first member of the list. var wyck = {}; var isUnconv = gdata.find("Standard/Default setting"); var w = gdata.split("Wyck"); var y = w[4].split('align="center">'); var pos = []; wyck.pos = pos; var n = 0; var nctr = 0; for (var j = 1; j <= y.length; j++) { if (j == 1 && y[j].find("(")) { var cent = []; var c = y[j].split("th>")[0].split("+"); for (var k = 1; k <= c.length; k++) { var cp = c[k].split("(")[2].split(")")[1].replace(" ",""); if (cp && cp != "0,0") { cent.push(cp + ",0"); nctr++; } } if (nctr > 0) wyck.cent = cent; continue; } if (j == 1) continue; var d = {}; d.mult = 0+y[j++].split(""); if (td.length == 1) { td = "" + td.replace(")(",")("); td = td.split(""); } var coor = []; for (var k = 1; k <= td.length; k++) { var t = td[k]; if (t.find(')')) { var p = t.split("(")[2].split(")")[1].replace(" ",""); coor.push(p + ",0"); } } d.mult = coor.length * (nctr + 1); 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, fix "unconv" :R return (s.trim() .replace("","").replace("","") .replace("","").replace("","") .replace("","").replace("","") .replace("|","|")).replace(" "," ").replace(":R",":r"); } m40 = [[0 0 0 0][0 0 0 0][0 0 0 0][0 0 0 1]]; function _toTransform(xyz){ // 1 0 -1 | 0 // 0 1 0 | 0 // 0 0 2 | 1/2 // // x-z,y,2z+1/2 to "a,b,-a+2c;0,0,1/2 (transposed) // a,b,-a+2c+1/2 also to "a,b,-a+2c;0,0,1/2 (not transposed) // the function unitcell() runs org.jmol.symmetry.UnitCell.getMatrixAndUnitCell() // unitcell() removes any embedded translations and puts the rotation in xyz-row,abc column format // we get any embedded translation using symop() var m = -unitcell(xyz, true); var abc = symop(m, "xyz").replace('x','a').replace('y','b').replace('z','c'); var t = symop(xyz, "matrix")%2; if (t == 0) { return abc; } return abc + ";" + symop(m40 + t, "xyz"); } 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(gdata, field, max) { // get a &... field form a URL var i = gdata.find(field + "="); return (i == 0 ? "" : (gdata[i+field.length + 1][i+max] + "&").split("&")[1]); } createJSONFiles(itaFirst, itaForceNew); /** 2024.09.28 new