refactor command to be more flexible and capable
This commit is contained in:
@ -8,17 +8,27 @@ Syntax
|
|||||||
|
|
||||||
.. code-block:: LAMMPS
|
.. code-block:: LAMMPS
|
||||||
|
|
||||||
region2vmd file args
|
region2vmd file keyword arg ...
|
||||||
|
|
||||||
* file = name of VMD script file to write
|
* filename = name of file to write VMD script commands to
|
||||||
* args = one or more region IDs may be appended
|
* zero or more keyword/arg pairs may be appended
|
||||||
|
* keyword = *region* or *color* or *material* or *command*
|
||||||
|
|
||||||
|
.. parsed-literal::
|
||||||
|
|
||||||
|
*region* region-ID = name of region to translate to VMD graphics
|
||||||
|
*color* color-name = set color for following visualized objects
|
||||||
|
*material* material-name = set material for following visualized objects
|
||||||
|
*command* string = string with custom VMD script command (in quotes)
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
""""""""
|
""""""""
|
||||||
|
|
||||||
.. code-block:: LAMMPS
|
.. code-block:: LAMMPS
|
||||||
|
|
||||||
region2vmd regions.vmd box c1 c2
|
region2vmd regions.vmd material Opaque color red region c1 color green region c2
|
||||||
|
region2vmd vizbox.vmd command "mol new system.lammpstrj waitfor all" region box
|
||||||
|
region2vmd regdefs.vmd region upper region lower region hole
|
||||||
|
|
||||||
Description
|
Description
|
||||||
"""""""""""
|
"""""""""""
|
||||||
@ -28,17 +38,106 @@ Description
|
|||||||
Write a `VMD <https:://ks.uiuc.edu/Research/vmd/>`_ Tcl script file with
|
Write a `VMD <https:://ks.uiuc.edu/Research/vmd/>`_ Tcl script file with
|
||||||
commands that aim to create a visualization of :doc:`LAMMPS regions
|
commands that aim to create a visualization of :doc:`LAMMPS regions
|
||||||
<region>`. There may be multiple region visualizations stored in a
|
<region>`. There may be multiple region visualizations stored in a
|
||||||
single file. Only a limited amount of region styles and settings are
|
single file.
|
||||||
currently supported. See **Restrictions** below.
|
|
||||||
|
|
||||||
The visualization is implemented by creating a new (and empty) "VMD
|
The visualization is implemented by creating a new (and empty) "VMD
|
||||||
molecule" and then using VMD graphics primitives to represent the region
|
molecule" and then assigning a sequence of VMD graphics primitives to
|
||||||
in VMD. Each region will be stored in a separate "VMD molecule" with
|
represent the region in VMD. Each region will be stored in a separate
|
||||||
the name "LAMMPS region <region ID>".
|
"VMD molecule" with the name "LAMMPS region <region ID>".
|
||||||
|
|
||||||
|
The *region2vmd* command is following by the filename for the resulting
|
||||||
|
VMD script and an arbitrary number of keyword argument pairs to either
|
||||||
|
write out a new *region* visualization, change the *color* or *material*
|
||||||
|
setting, or to insert arbitrary VMD script *command*s. The keywords
|
||||||
|
and arguments are processed in sequence.
|
||||||
|
|
||||||
|
The *region* keyword must be followed by a previously defined LAMMPS
|
||||||
|
:doc:`region <region>`. Only a limited set region styles and region
|
||||||
|
settings are currently supported. See **Restrictions** below.
|
||||||
|
Unsupported region styles or regions with unsupported settings will be
|
||||||
|
skipped and a corresponding message is printed.
|
||||||
|
|
||||||
|
The *color* keyword must be followed by a color name that is defined in
|
||||||
|
VMD. This color will be used by all following region visualizations.
|
||||||
|
The default setting is 'silver'. VMD has the following colors
|
||||||
|
pre-defined:
|
||||||
|
|
||||||
|
.. table_from_list::
|
||||||
|
:columns: 11
|
||||||
|
|
||||||
|
* blue
|
||||||
|
* red
|
||||||
|
* gray
|
||||||
|
* orange
|
||||||
|
* yellow
|
||||||
|
* tan
|
||||||
|
* silver
|
||||||
|
* green
|
||||||
|
* white
|
||||||
|
* pink
|
||||||
|
* cyan
|
||||||
|
* purple
|
||||||
|
* lime
|
||||||
|
* mauve
|
||||||
|
* ochre
|
||||||
|
* iceblue
|
||||||
|
* black
|
||||||
|
* yellow2
|
||||||
|
* yellow3
|
||||||
|
* green2
|
||||||
|
* green3
|
||||||
|
* cyan2
|
||||||
|
* cyan3
|
||||||
|
* blue2
|
||||||
|
* blue3
|
||||||
|
* violet
|
||||||
|
* violet2
|
||||||
|
* magenta
|
||||||
|
* magenta2
|
||||||
|
* red2
|
||||||
|
* red3
|
||||||
|
* orange2
|
||||||
|
* orange3
|
||||||
|
|
||||||
|
The *material* keyword must be followed by a material name that is defined in
|
||||||
|
VMD. This material will be used by all following visualizations. The
|
||||||
|
default setting is 'Transparent'. VMD has the following materials
|
||||||
|
pre-defined:
|
||||||
|
|
||||||
|
.. table_from_list::
|
||||||
|
:columns: 7
|
||||||
|
|
||||||
|
* Opaque
|
||||||
|
* Transparent
|
||||||
|
* BrushedMetal
|
||||||
|
* Diffuse
|
||||||
|
* Ghost
|
||||||
|
* Glass1
|
||||||
|
* Glass2
|
||||||
|
* Glass3
|
||||||
|
* Glossy
|
||||||
|
* HardPlastic
|
||||||
|
* MetallicPastel
|
||||||
|
* Steel
|
||||||
|
* Translucent
|
||||||
|
* Edgy
|
||||||
|
* EdgyShiny
|
||||||
|
* EdgyGlass
|
||||||
|
* Goodsell
|
||||||
|
* AOShiny
|
||||||
|
* AOChalky
|
||||||
|
* AOEdgy
|
||||||
|
* BlownGlass
|
||||||
|
* GlassBubble
|
||||||
|
* RTChrome
|
||||||
|
|
||||||
|
The *command* keyword must be followed by a VMD script command as a
|
||||||
|
single string in quotes. This command will be directly inserted into
|
||||||
|
the created VMD script.
|
||||||
|
|
||||||
The created file can be loaded into VMD either from the command line
|
The created file can be loaded into VMD either from the command line
|
||||||
with the -e flag, or from the command prompt with play <script file>, or
|
with the '-e' flag, or from the command prompt with 'play <script
|
||||||
from the File menu via "Load VMD visualization state".
|
file>', or from the File menu via "Load VMD visualization state".
|
||||||
|
|
||||||
----------
|
----------
|
||||||
|
|
||||||
@ -62,7 +161,7 @@ Related commands
|
|||||||
|
|
||||||
:doc:`region <region>`
|
:doc:`region <region>`
|
||||||
|
|
||||||
Default
|
Defaults
|
||||||
"""""""
|
""""""""
|
||||||
|
|
||||||
none
|
*color* = silver, *material* = Transparent
|
||||||
|
|||||||
@ -27,20 +27,49 @@
|
|||||||
#include "region_cylinder.h"
|
#include "region_cylinder.h"
|
||||||
#include "region_sphere.h"
|
#include "region_sphere.h"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cstring>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
using namespace LAMMPS_NS;
|
using namespace LAMMPS_NS;
|
||||||
|
|
||||||
static constexpr double SMALL = 1.0e-10;
|
static constexpr double SMALL = 1.0e-10;
|
||||||
|
|
||||||
|
static const std::unordered_set<std::string> vmdcolors{
|
||||||
|
"blue", "red", "gray", "orange", "yellow", "tan", "silver", "green", "white",
|
||||||
|
"pink", "cyan", "purple", "lime", "mauve", "ochre", "iceblue", "black", "yellow2",
|
||||||
|
"yellow3", "green2", "green3", "cyan2", "cyan3", "blue2", "blue3", "violet", "violet2",
|
||||||
|
"magenta", "magenta2", "red2", "red3", "orange2", "orange3"};
|
||||||
|
|
||||||
|
static const std::unordered_set<std::string> vmdmaterials{
|
||||||
|
"Opaque", "Transparent", "BrushedMetal", "Diffuse", "Ghost", "Glass1",
|
||||||
|
"Glass2", "Glass3", "Glossy", "HardPlastic", "MetallicPastel", "Steel",
|
||||||
|
"Translucent", "Edgy", "EdgyShiny", "EdgyGlass", "Goodsell", "AOShiny",
|
||||||
|
"AOChalky", "AOEdgy", "BlownGlass", "GlassBubble", "RTChrome"};
|
||||||
|
|
||||||
|
// class that "owns" the file pointer and closes it when going out of scope.
|
||||||
|
// this avoids a lot of redundant checks and calls.
|
||||||
|
class AutoClose {
|
||||||
|
public:
|
||||||
|
AutoClose() = delete;
|
||||||
|
AutoClose(const AutoClose &) = delete;
|
||||||
|
AutoClose(const AutoClose &&) = delete;
|
||||||
|
explicit AutoClose(FILE *_fp) : fp(_fp) {};
|
||||||
|
~AutoClose()
|
||||||
|
{
|
||||||
|
if (fp) fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FILE *fp;
|
||||||
|
};
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------- */
|
/* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
void Region2VMD::command(int narg, char **arg)
|
void Region2VMD::command(int narg, char **arg)
|
||||||
{
|
{
|
||||||
|
if (narg < 3) utils::missing_cmd_args(FLERR, "region2vmd", error);
|
||||||
|
|
||||||
FILE *fp = nullptr;
|
FILE *fp = nullptr;
|
||||||
|
|
||||||
if (narg < 2) utils::missing_cmd_args(FLERR, "region2vmd", error);
|
|
||||||
|
|
||||||
if (comm->me == 0) {
|
if (comm->me == 0) {
|
||||||
fp = fopen(arg[0], "w");
|
fp = fopen(arg[0], "w");
|
||||||
if (fp == nullptr) {
|
if (fp == nullptr) {
|
||||||
@ -52,21 +81,67 @@ void Region2VMD::command(int narg, char **arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int iarg = 1; iarg < narg; ++iarg) {
|
// automatically close fp when fpowner goes out of scope
|
||||||
auto *region = domain->get_region_by_id(arg[iarg]);
|
AutoClose fpowner(fp);
|
||||||
if (!region) {
|
|
||||||
if (fp) fclose(fp);
|
// defaults
|
||||||
error->all(FLERR, iarg, "Region {} does not exist", arg[iarg]);
|
std::string color = "silver";
|
||||||
|
std::string material = "Transparent";
|
||||||
|
|
||||||
|
int iarg = 1;
|
||||||
|
std::string thisarg = arg[iarg];
|
||||||
|
while (iarg < narg) {
|
||||||
|
if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "region2vmd", error);
|
||||||
|
thisarg = arg[iarg];
|
||||||
|
++iarg;
|
||||||
|
|
||||||
|
if (thisarg == "color") {
|
||||||
|
color = arg[iarg];
|
||||||
|
if (const auto &search = vmdcolors.find(color); search == vmdcolors.end())
|
||||||
|
error->all(FLERR, iarg, "Color {} is not a known VMD color", color);
|
||||||
|
|
||||||
|
} else if (thisarg == "material") {
|
||||||
|
material = arg[iarg];
|
||||||
|
if (const auto &search = vmdmaterials.find(material); search == vmdmaterials.end())
|
||||||
|
error->all(FLERR, iarg, "Material {} is not a known VMD material", material);
|
||||||
|
|
||||||
|
} else if (thisarg == "command") {
|
||||||
|
if (fp) {
|
||||||
|
fputs("\n# custom command\n", fp);
|
||||||
|
fputs(arg[iarg], fp);
|
||||||
|
fputs("\n", fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (thisarg == "region") {
|
||||||
|
auto *region = domain->get_region_by_id(arg[iarg]);
|
||||||
|
if (!region) {
|
||||||
|
error->all(FLERR, iarg, "Region {} does not exist", arg[iarg]);
|
||||||
|
} else {
|
||||||
|
if (fp) {
|
||||||
|
utils::logmesg(lmp, " writing region {} ...", region->id);
|
||||||
|
utils::print(fp, "\n# region {} of style {}\n", region->id, region->style);
|
||||||
|
|
||||||
|
fputs("# create new empty VMD molecule to store the graphics primitives\n"
|
||||||
|
"set gfxmol [mol new]\nmol top $gfxmol\n",
|
||||||
|
fp);
|
||||||
|
utils::print(fp, "mol rename $gfxmol {{LAMMPS region {}}}\n", region->id);
|
||||||
|
fputs("# set color and material\n", fp);
|
||||||
|
utils::print(fp, "graphics $gfxmol color {}\n", color);
|
||||||
|
utils::print(fp, "graphics $gfxmol material {}\n", material);
|
||||||
|
write_region(fp, region);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
write_region(fp, region);
|
error->all(FLERR, iarg - 1, "Unknown region2vmd keyword {}", thisarg);
|
||||||
}
|
}
|
||||||
|
++iarg;
|
||||||
}
|
}
|
||||||
|
|
||||||
// done, close file
|
// done. file will be close automatically
|
||||||
if (comm->me == 0) {
|
if (fp) {
|
||||||
// reset views and restore previous top molecule
|
// reset views and restore previous top molecule
|
||||||
fputs("display resetview\nif {$oldtop >= 0} {mol top $oldtop}\n", fp);
|
fputs("after idle {if {$oldtop >= 0} {mol top $oldtop}; display resetview}\n", fp);
|
||||||
fclose(fp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,26 +155,11 @@ void Region2VMD::write_region(FILE *fp, Region *region)
|
|||||||
if (!fp || !region) return;
|
if (!fp || !region) return;
|
||||||
|
|
||||||
if (region->dynamic_check()) {
|
if (region->dynamic_check()) {
|
||||||
utils::logmesg(lmp, "Cannot (yet) handle moving or rotating region {}. Skipping... ",
|
utils::logmesg(lmp, "Cannot (yet) handle moving or rotating regions {}. Skipping... ",
|
||||||
region->id);
|
region->id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a new (empty) VMD molecule to hold the graphics for this specific region.
|
|
||||||
|
|
||||||
utils::logmesg(lmp, " writing region {} ...", region->id);
|
|
||||||
utils::print(fp, "\n# region {} of style {}\n", region->id, region->style);
|
|
||||||
fputs("# create new empty VMD molecule to store the graphics primitives\n"
|
|
||||||
"set gfxmol [mol new]\nmol top $gfxmol\n",
|
|
||||||
fp);
|
|
||||||
utils::print(fp, "mol rename $gfxmol {{LAMMPS region {}}}\n", region->id);
|
|
||||||
fputs("# set color to desired value. Use 'silver' by default\n"
|
|
||||||
"graphics $gfxmol color silver\n",
|
|
||||||
fp);
|
|
||||||
fputs("# set material to desired choice\n"
|
|
||||||
"graphics $gfxmol material Transparent\n",
|
|
||||||
fp);
|
|
||||||
|
|
||||||
// translate compatible regions to VMD graphics primitives, skip others.
|
// translate compatible regions to VMD graphics primitives, skip others.
|
||||||
|
|
||||||
const std::string regstyle = region->style;
|
const std::string regstyle = region->style;
|
||||||
@ -108,9 +168,7 @@ void Region2VMD::write_region(FILE *fp, Region *region)
|
|||||||
if (!block) {
|
if (!block) {
|
||||||
error->one(FLERR, Error::NOLASTLINE, "Region {} is not of style 'block'", region->id);
|
error->one(FLERR, Error::NOLASTLINE, "Region {} is not of style 'block'", region->id);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// a block is represented by 12 triangles
|
// a block is represented by 12 triangles
|
||||||
|
|
||||||
utils::print(fp,
|
utils::print(fp,
|
||||||
"draw triangle {{{0} {2} {4}}} {{{0} {2} {5}}} {{{0} {3} {4}}}\n"
|
"draw triangle {{{0} {2} {4}}} {{{0} {2} {5}}} {{{0} {3} {4}}}\n"
|
||||||
"draw triangle {{{0} {3} {4}}} {{{0} {3} {5}}} {{{0} {2} {5}}}\n"
|
"draw triangle {{{0} {3} {4}}} {{{0} {3} {5}}} {{{0} {2} {5}}}\n"
|
||||||
@ -132,10 +190,9 @@ void Region2VMD::write_region(FILE *fp, Region *region)
|
|||||||
if (!cone) {
|
if (!cone) {
|
||||||
error->one(FLERR, Error::NOLASTLINE, "Region {} is not of style 'cone'", region->id);
|
error->one(FLERR, Error::NOLASTLINE, "Region {} is not of style 'cone'", region->id);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// The VMD cone primitive requires one radius set to zero
|
// The VMD cone primitive requires one radius set to zero
|
||||||
if (cone->radiuslo < SMALL) {
|
if (cone->radiuslo < SMALL) {
|
||||||
// a cone uses a single cone primitive
|
// a VMD cone uses a single cone primitive
|
||||||
if (cone->axis == 'x') {
|
if (cone->axis == 'x') {
|
||||||
utils::print(fp, "draw cone {{{1} {2} {3}}} {{{0} {2} {3}}} radius {4} resolution 20\n",
|
utils::print(fp, "draw cone {{{1} {2} {3}}} {{{0} {2} {3}}} radius {4} resolution 20\n",
|
||||||
cone->lo, cone->hi, cone->c1, cone->c2, cone->radiushi);
|
cone->lo, cone->hi, cone->c1, cone->c2, cone->radiushi);
|
||||||
@ -147,7 +204,7 @@ void Region2VMD::write_region(FILE *fp, Region *region)
|
|||||||
cone->lo, cone->hi, cone->c1, cone->c2, cone->radiushi);
|
cone->lo, cone->hi, cone->c1, cone->c2, cone->radiushi);
|
||||||
}
|
}
|
||||||
} else if (cone->radiushi < SMALL) {
|
} else if (cone->radiushi < SMALL) {
|
||||||
// a cone uses a single cone primitive
|
// a VMD cone uses a single cone primitive
|
||||||
if (cone->axis == 'x') {
|
if (cone->axis == 'x') {
|
||||||
utils::print(fp, "draw cone {{{0} {2} {3}}} {{{1} {2} {3}}} radius {4} resolution 20\n",
|
utils::print(fp, "draw cone {{{0} {2} {3}}} {{{1} {2} {3}}} radius {4} resolution 20\n",
|
||||||
cone->lo, cone->hi, cone->c1, cone->c2, cone->radiuslo);
|
cone->lo, cone->hi, cone->c1, cone->c2, cone->radiuslo);
|
||||||
@ -163,6 +220,7 @@ void Region2VMD::write_region(FILE *fp, Region *region)
|
|||||||
"Cannot (yet) translate a truncated cone to VMD graphics. Skipping...\n");
|
"Cannot (yet) translate a truncated cone to VMD graphics. Skipping...\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (regstyle == "cylinder") {
|
} else if (regstyle == "cylinder") {
|
||||||
const auto cylinder = dynamic_cast<RegCylinder *>(region);
|
const auto cylinder = dynamic_cast<RegCylinder *>(region);
|
||||||
if (!cylinder) {
|
if (!cylinder) {
|
||||||
@ -180,6 +238,7 @@ void Region2VMD::write_region(FILE *fp, Region *region)
|
|||||||
cylinder->lo, cylinder->hi, cylinder->c1, cylinder->c2, cylinder->radius);
|
cylinder->lo, cylinder->hi, cylinder->c1, cylinder->c2, cylinder->radius);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (regstyle == "sphere") {
|
} else if (regstyle == "sphere") {
|
||||||
const auto sphere = dynamic_cast<RegSphere *>(region);
|
const auto sphere = dynamic_cast<RegSphere *>(region);
|
||||||
if (!sphere) {
|
if (!sphere) {
|
||||||
@ -189,6 +248,7 @@ void Region2VMD::write_region(FILE *fp, Region *region)
|
|||||||
utils::print(fp, "draw sphere {{{} {} {}}} radius {} resolution 20\n", sphere->xc, sphere->yc,
|
utils::print(fp, "draw sphere {{{} {} {}}} radius {} resolution 20\n", sphere->xc, sphere->yc,
|
||||||
sphere->zc, sphere->radius);
|
sphere->zc, sphere->radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
utils::logmesg(lmp,
|
utils::logmesg(lmp,
|
||||||
"Cannot (yet) translate region {} of style {} to VMD graphics. Skipping... ",
|
"Cannot (yet) translate region {} of style {} to VMD graphics. Skipping... ",
|
||||||
|
|||||||
Reference in New Issue
Block a user