diff --git a/src/group.cpp b/src/group.cpp index 38ffa79a22..9e140779ef 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -30,6 +30,7 @@ #include "region.h" #include "tokenizer.h" #include "variable.h" +#include "exceptions.h" #include #include @@ -152,12 +153,14 @@ void Group::assign(int narg, char **arg) // add a new group if igroup = -1 int igroup = find(arg[0]); + bool created = false; if (igroup == -1) { if (ngroup == MAX_GROUP) error->all(FLERR,"Too many groups"); igroup = find_unused(); names[igroup] = utils::strdup(arg[0]); ngroup++; + created = true; } double **x = atom->x; @@ -165,349 +168,360 @@ void Group::assign(int narg, char **arg) int nlocal = atom->nlocal; int bit = bitmask[igroup]; - // style = region - // add to group if atom is in region + try { + // style = region + // add to group if atom is in region - if (strcmp(arg[1],"region") == 0) { + if (strcmp(arg[1],"region") == 0) { - if (narg != 3) error->all(FLERR,"Illegal group command"); + if (narg != 3) error->all(FLERR,"Illegal group command"); - int iregion = domain->find_region(arg[2]); - if (iregion == -1) error->all(FLERR,"Group region ID does not exist"); - domain->regions[iregion]->init(); - domain->regions[iregion]->prematch(); + int iregion = domain->find_region(arg[2]); + if (iregion == -1) error->all(FLERR,"Group region ID does not exist"); + domain->regions[iregion]->init(); + domain->regions[iregion]->prematch(); - for (i = 0; i < nlocal; i++) - if (domain->regions[iregion]->match(x[i][0],x[i][1],x[i][2])) - mask[i] |= bit; + for (i = 0; i < nlocal; i++) + if (domain->regions[iregion]->match(x[i][0],x[i][1],x[i][2])) + mask[i] |= bit; - // create an empty group + // create an empty group - } else if (strcmp(arg[1],"empty") == 0) { + } else if (strcmp(arg[1],"empty") == 0) { - if (narg != 2) error->all(FLERR,"Illegal group command"); - // nothing else to do here + if (narg != 2) error->all(FLERR,"Illegal group command"); + // nothing else to do here - // style = type, molecule, id - // add to group if atom matches type/molecule/id or condition + // style = type, molecule, id + // add to group if atom matches type/molecule/id or condition - } else if (strcmp(arg[1],"type") == 0 || strcmp(arg[1],"molecule") == 0 || - strcmp(arg[1],"id") == 0) { + } else if (strcmp(arg[1],"type") == 0 || strcmp(arg[1],"molecule") == 0 || + strcmp(arg[1],"id") == 0) { - if (narg < 3) error->all(FLERR,"Illegal group command"); + if (narg < 3) error->all(FLERR,"Illegal group command"); - int category=NONE; - if (strcmp(arg[1],"type") == 0) category = TYPE; - else if (strcmp(arg[1],"molecule") == 0) category = MOLECULE; - else if (strcmp(arg[1],"id") == 0) category = ID; + int category=NONE; + if (strcmp(arg[1],"type") == 0) category = TYPE; + else if (strcmp(arg[1],"molecule") == 0) category = MOLECULE; + else if (strcmp(arg[1],"id") == 0) category = ID; - if ((category == MOLECULE) && (!atom->molecule_flag)) - error->all(FLERR,"Group command requires atom attribute molecule"); - - if ((category == ID) && (!atom->tag_enable)) - error->all(FLERR,"Group command requires atom IDs"); - - // args = logical condition - - if (narg > 3 && - (strcmp(arg[2],"<") == 0 || strcmp(arg[2],">") == 0 || - strcmp(arg[2],"<=") == 0 || strcmp(arg[2],">=") == 0 || - strcmp(arg[2],"==") == 0 || strcmp(arg[2],"!=") == 0 || - strcmp(arg[2],"<>") == 0)) { - - int condition = -1; - if (strcmp(arg[2],"<") == 0) condition = LT; - else if (strcmp(arg[2],"<=") == 0) condition = LE; - else if (strcmp(arg[2],">") == 0) condition = GT; - else if (strcmp(arg[2],">=") == 0) condition = GE; - else if (strcmp(arg[2],"==") == 0) condition = EQ; - else if (strcmp(arg[2],"!=") == 0) condition = NEQ; - else if (strcmp(arg[2],"<>") == 0) condition = BETWEEN; - else error->all(FLERR,"Illegal group command"); - - tagint bound1,bound2; - bound1 = utils::tnumeric(FLERR,arg[3],false,lmp); - bound2 = -1; - - if (condition == BETWEEN) { - if (narg != 5) error->all(FLERR,"Illegal group command"); - bound2 = utils::tnumeric(FLERR,arg[4],false,lmp); - } else if (narg != 4) error->all(FLERR,"Illegal group command"); - - int *attribute = nullptr; - tagint *tattribute = nullptr; - if (category == TYPE) attribute = atom->type; - else if (category == MOLECULE) tattribute = atom->molecule; - else if (category == ID) tattribute = atom->tag; - - // add to group if meets condition - - if (attribute) { - if (condition == LT) { - for (i = 0; i < nlocal; i++) - if (attribute[i] < bound1) mask[i] |= bit; - } else if (condition == LE) { - for (i = 0; i < nlocal; i++) - if (attribute[i] <= bound1) mask[i] |= bit; - } else if (condition == GT) { - for (i = 0; i < nlocal; i++) - if (attribute[i] > bound1) mask[i] |= bit; - } else if (condition == GE) { - for (i = 0; i < nlocal; i++) - if (attribute[i] >= bound1) mask[i] |= bit; - } else if (condition == EQ) { - for (i = 0; i < nlocal; i++) - if (attribute[i] == bound1) mask[i] |= bit; - } else if (condition == NEQ) { - for (i = 0; i < nlocal; i++) - if (attribute[i] != bound1) mask[i] |= bit; - } else if (condition == BETWEEN) { - for (i = 0; i < nlocal; i++) - if (attribute[i] >= bound1 && attribute[i] <= bound2) - mask[i] |= bit; - } - } else { - if (condition == LT) { - for (i = 0; i < nlocal; i++) - if (tattribute[i] < bound1) mask[i] |= bit; - } else if (condition == LE) { - for (i = 0; i < nlocal; i++) - if (tattribute[i] <= bound1) mask[i] |= bit; - } else if (condition == GT) { - for (i = 0; i < nlocal; i++) - if (tattribute[i] > bound1) mask[i] |= bit; - } else if (condition == GE) { - for (i = 0; i < nlocal; i++) - if (tattribute[i] >= bound1) mask[i] |= bit; - } else if (condition == EQ) { - for (i = 0; i < nlocal; i++) - if (tattribute[i] == bound1) mask[i] |= bit; - } else if (condition == NEQ) { - for (i = 0; i < nlocal; i++) - if (tattribute[i] != bound1) mask[i] |= bit; - } else if (condition == BETWEEN) { - for (i = 0; i < nlocal; i++) - if (tattribute[i] >= bound1 && tattribute[i] <= bound2) - mask[i] |= bit; - } - } - - // args = list of values - - } else { - int *attribute = nullptr; - tagint *tattribute = nullptr; - if (category == TYPE) attribute = atom->type; - else if (category == MOLECULE) tattribute = atom->molecule; - else if (category == ID) tattribute = atom->tag; - - tagint start,stop,delta; - - for (int iarg = 2; iarg < narg; iarg++) { - delta = 1; - try { - ValueTokenizer values(arg[iarg],":"); - start = values.next_tagint(); - if (utils::strmatch(arg[iarg],"^-?\\d+$")) { - stop = start; - } else if (utils::strmatch(arg[iarg],"^-?\\d+:-?\\d+$")) { - stop = values.next_tagint(); - } else if (utils::strmatch(arg[iarg],"^-?\\d+:-?\\d+:\\d+$")) { - stop = values.next_tagint(); - delta = values.next_tagint(); - } else throw TokenizerException("Syntax error",""); - } catch (TokenizerException &e) { - error->all(FLERR,fmt::format("Incorrect range string " - "'{}': {}",arg[iarg],e.what())); - } - if (delta < 1) - error->all(FLERR,"Illegal range increment value"); - - // add to group if attribute matches value or sequence - - if (attribute) { - for (i = 0; i < nlocal; i++) - if (attribute[i] >= start && attribute[i] <= stop && - (attribute[i]-start) % delta == 0) mask[i] |= bit; - } else { - for (i = 0; i < nlocal; i++) - if (tattribute[i] >= start && tattribute[i] <= stop && - (tattribute[i]-start) % delta == 0) mask[i] |= bit; - } - } - } - - // style = variable - // add to group if atom-atyle variable is non-zero - - } else if (strcmp(arg[1],"variable") == 0) { - - int ivar = input->variable->find(arg[2]); - if (ivar < 0) error->all(FLERR,"Variable name for group does not exist"); - if (!input->variable->atomstyle(ivar)) - error->all(FLERR,"Variable for group is invalid style"); - - double *aflag; - - // aflag = evaluation of per-atom variable - - memory->create(aflag,nlocal,"group:aflag"); - input->variable->compute_atom(ivar,0,aflag,1,0); - - // add to group if per-atom variable evaluated to non-zero - - for (i = 0; i < nlocal; i++) - if (aflag[i] != 0.0) mask[i] |= bit; - - memory->destroy(aflag); - - // style = include - - } else if (strcmp(arg[1],"include") == 0) { - - if (narg != 3) error->all(FLERR,"Illegal group command"); - if (strcmp(arg[2],"molecule") == 0) { - if (!atom->molecule_flag) + if ((category == MOLECULE) && (!atom->molecule_flag)) error->all(FLERR,"Group command requires atom attribute molecule"); - add_molecules(igroup,bit); + if ((category == ID) && (!atom->tag_enable)) + error->all(FLERR,"Group command requires atom IDs"); + + // args = logical condition + + if (narg > 3 && + (strcmp(arg[2],"<") == 0 || strcmp(arg[2],">") == 0 || + strcmp(arg[2],"<=") == 0 || strcmp(arg[2],">=") == 0 || + strcmp(arg[2],"==") == 0 || strcmp(arg[2],"!=") == 0 || + strcmp(arg[2],"<>") == 0)) { + + int condition = -1; + if (strcmp(arg[2],"<") == 0) condition = LT; + else if (strcmp(arg[2],"<=") == 0) condition = LE; + else if (strcmp(arg[2],">") == 0) condition = GT; + else if (strcmp(arg[2],">=") == 0) condition = GE; + else if (strcmp(arg[2],"==") == 0) condition = EQ; + else if (strcmp(arg[2],"!=") == 0) condition = NEQ; + else if (strcmp(arg[2],"<>") == 0) condition = BETWEEN; + else error->all(FLERR,"Illegal group command"); + + tagint bound1,bound2; + bound1 = utils::tnumeric(FLERR,arg[3],false,lmp); + bound2 = -1; + + if (condition == BETWEEN) { + if (narg != 5) error->all(FLERR,"Illegal group command"); + bound2 = utils::tnumeric(FLERR,arg[4],false,lmp); + } else if (narg != 4) error->all(FLERR,"Illegal group command"); + + int *attribute = nullptr; + tagint *tattribute = nullptr; + if (category == TYPE) attribute = atom->type; + else if (category == MOLECULE) tattribute = atom->molecule; + else if (category == ID) tattribute = atom->tag; + + // add to group if meets condition + + if (attribute) { + if (condition == LT) { + for (i = 0; i < nlocal; i++) + if (attribute[i] < bound1) mask[i] |= bit; + } else if (condition == LE) { + for (i = 0; i < nlocal; i++) + if (attribute[i] <= bound1) mask[i] |= bit; + } else if (condition == GT) { + for (i = 0; i < nlocal; i++) + if (attribute[i] > bound1) mask[i] |= bit; + } else if (condition == GE) { + for (i = 0; i < nlocal; i++) + if (attribute[i] >= bound1) mask[i] |= bit; + } else if (condition == EQ) { + for (i = 0; i < nlocal; i++) + if (attribute[i] == bound1) mask[i] |= bit; + } else if (condition == NEQ) { + for (i = 0; i < nlocal; i++) + if (attribute[i] != bound1) mask[i] |= bit; + } else if (condition == BETWEEN) { + for (i = 0; i < nlocal; i++) + if (attribute[i] >= bound1 && attribute[i] <= bound2) + mask[i] |= bit; + } + } else { + if (condition == LT) { + for (i = 0; i < nlocal; i++) + if (tattribute[i] < bound1) mask[i] |= bit; + } else if (condition == LE) { + for (i = 0; i < nlocal; i++) + if (tattribute[i] <= bound1) mask[i] |= bit; + } else if (condition == GT) { + for (i = 0; i < nlocal; i++) + if (tattribute[i] > bound1) mask[i] |= bit; + } else if (condition == GE) { + for (i = 0; i < nlocal; i++) + if (tattribute[i] >= bound1) mask[i] |= bit; + } else if (condition == EQ) { + for (i = 0; i < nlocal; i++) + if (tattribute[i] == bound1) mask[i] |= bit; + } else if (condition == NEQ) { + for (i = 0; i < nlocal; i++) + if (tattribute[i] != bound1) mask[i] |= bit; + } else if (condition == BETWEEN) { + for (i = 0; i < nlocal; i++) + if (tattribute[i] >= bound1 && tattribute[i] <= bound2) + mask[i] |= bit; + } + } + + // args = list of values + + } else { + int *attribute = nullptr; + tagint *tattribute = nullptr; + if (category == TYPE) attribute = atom->type; + else if (category == MOLECULE) tattribute = atom->molecule; + else if (category == ID) tattribute = atom->tag; + + tagint start,stop,delta; + + for (int iarg = 2; iarg < narg; iarg++) { + delta = 1; + try { + ValueTokenizer values(arg[iarg],":"); + start = values.next_tagint(); + if (utils::strmatch(arg[iarg],"^-?\\d+$")) { + stop = start; + } else if (utils::strmatch(arg[iarg],"^-?\\d+:-?\\d+$")) { + stop = values.next_tagint(); + } else if (utils::strmatch(arg[iarg],"^-?\\d+:-?\\d+:\\d+$")) { + stop = values.next_tagint(); + delta = values.next_tagint(); + } else throw TokenizerException("Syntax error",""); + } catch (TokenizerException &e) { + error->all(FLERR,fmt::format("Incorrect range string " + "'{}': {}",arg[iarg],e.what())); + } + if (delta < 1) + error->all(FLERR,"Illegal range increment value"); + + // add to group if attribute matches value or sequence + + if (attribute) { + for (i = 0; i < nlocal; i++) + if (attribute[i] >= start && attribute[i] <= stop && + (attribute[i]-start) % delta == 0) mask[i] |= bit; + } else { + for (i = 0; i < nlocal; i++) + if (tattribute[i] >= start && tattribute[i] <= stop && + (tattribute[i]-start) % delta == 0) mask[i] |= bit; + } + } + } + + // style = variable + // add to group if atom-atyle variable is non-zero + + } else if (strcmp(arg[1],"variable") == 0) { + + int ivar = input->variable->find(arg[2]); + if (ivar < 0) error->all(FLERR,"Variable name for group does not exist"); + if (!input->variable->atomstyle(ivar)) + error->all(FLERR,"Variable for group is invalid style"); + + double *aflag; + + // aflag = evaluation of per-atom variable + + memory->create(aflag,nlocal,"group:aflag"); + input->variable->compute_atom(ivar,0,aflag,1,0); + + // add to group if per-atom variable evaluated to non-zero + + for (i = 0; i < nlocal; i++) + if (aflag[i] != 0.0) mask[i] |= bit; + + memory->destroy(aflag); + + // style = include + + } else if (strcmp(arg[1],"include") == 0) { + + if (narg != 3) error->all(FLERR,"Illegal group command"); + if (strcmp(arg[2],"molecule") == 0) { + if (!atom->molecule_flag) + error->all(FLERR,"Group command requires atom attribute molecule"); + + add_molecules(igroup,bit); + + } else error->all(FLERR,"Illegal group command"); + + // style = subtract + + } else if (strcmp(arg[1],"subtract") == 0) { + + if (narg < 4) error->all(FLERR,"Illegal group command"); + + int length = narg-2; + std::vector list(length); + + int jgroup; + for (int iarg = 2; iarg < narg; iarg++) { + jgroup = find(arg[iarg]); + if (jgroup == -1) error->all(FLERR,"Group ID does not exist"); + if (dynamic[jgroup]) + error->all(FLERR,"Cannot subtract groups using a dynamic group"); + list[iarg-2] = jgroup; + } + + // add to group if in 1st group in list + + int otherbit = bitmask[list[0]]; + + for (i = 0; i < nlocal; i++) + if (mask[i] & otherbit) mask[i] |= bit; + + // remove atoms if they are in any of the other groups + // AND with inverse mask removes the atom from group + + int inverse = inversemask[igroup]; + + for (int ilist = 1; ilist < length; ilist++) { + otherbit = bitmask[list[ilist]]; + for (i = 0; i < nlocal; i++) + if (mask[i] & otherbit) mask[i] &= inverse; + } + + // style = union + + } else if (strcmp(arg[1],"union") == 0) { + + if (narg < 3) error->all(FLERR,"Illegal group command"); + + int length = narg-2; + std::vector list(length); + + int jgroup; + for (int iarg = 2; iarg < narg; iarg++) { + jgroup = find(arg[iarg]); + if (jgroup == -1) error->all(FLERR,"Group ID does not exist"); + if (dynamic[jgroup]) + error->all(FLERR,"Cannot union groups using a dynamic group"); + list[iarg-2] = jgroup; + } + + // add to group if in any other group in list + + int otherbit; + + for (int ilist = 0; ilist < length; ilist++) { + otherbit = bitmask[list[ilist]]; + for (i = 0; i < nlocal; i++) + if (mask[i] & otherbit) mask[i] |= bit; + } + + // style = intersect + + } else if (strcmp(arg[1],"intersect") == 0) { + + if (narg < 4) error->all(FLERR,"Illegal group command"); + + int length = narg-2; + std::vector list(length); + + int jgroup; + for (int iarg = 2; iarg < narg; iarg++) { + jgroup = find(arg[iarg]); + if (jgroup == -1) error->all(FLERR,"Group ID does not exist"); + if (dynamic[jgroup]) + error->all(FLERR,"Cannot intersect groups using a dynamic group"); + list[iarg-2] = jgroup; + } + + // add to group if in all groups in list + + int otherbit,ok,ilist; + + for (i = 0; i < nlocal; i++) { + ok = 1; + for (ilist = 0; ilist < length; ilist++) { + otherbit = bitmask[list[ilist]]; + if ((mask[i] & otherbit) == 0) ok = 0; + } + if (ok) mask[i] |= bit; + } + + // style = dynamic + // create a new FixGroup to dynamically determine atoms in group + + } else if (strcmp(arg[1],"dynamic") == 0) { + + if (narg < 4) error->all(FLERR,"Illegal group command"); + if (strcmp(arg[0],arg[2]) == 0) + error->all(FLERR,"Group dynamic cannot reference itself"); + if (find(arg[2]) < 0) + error->all(FLERR,"Group dynamic parent group does not exist"); + if (igroup == 0) error->all(FLERR,"Group all cannot be made dynamic"); + + // if group is already dynamic, delete existing FixGroup + + if (dynamic[igroup]) + modify->delete_fix(std::string("GROUP_") + names[igroup]); + + dynamic[igroup] = 1; + + std::string fixcmd = "GROUP_"; + fixcmd += fmt::format("{} {} GROUP",names[igroup],arg[2]); + for (int i = 3; i < narg; i++) fixcmd += std::string(" ") + arg[i]; + modify->add_fix(fixcmd); + + // style = static + // remove dynamic FixGroup if necessary + + } else if (strcmp(arg[1],"static") == 0) { + + if (narg != 2) error->all(FLERR,"Illegal group command"); + + if (dynamic[igroup]) + modify->delete_fix(std::string("GROUP_") + names[igroup]); + + dynamic[igroup] = 0; + + // not a valid group style } else error->all(FLERR,"Illegal group command"); - // style = subtract - - } else if (strcmp(arg[1],"subtract") == 0) { - - if (narg < 4) error->all(FLERR,"Illegal group command"); - - int length = narg-2; - std::vector list(length); - - int jgroup; - for (int iarg = 2; iarg < narg; iarg++) { - jgroup = find(arg[iarg]); - if (jgroup == -1) error->all(FLERR,"Group ID does not exist"); - if (dynamic[jgroup]) - error->all(FLERR,"Cannot subtract groups using a dynamic group"); - list[iarg-2] = jgroup; + } catch (LAMMPSException & e) { + // undo created group in case of an error + if (created) { + delete [] names[igroup]; + names[igroup] = nullptr; + ngroup--; } - - // add to group if in 1st group in list - - int otherbit = bitmask[list[0]]; - - for (i = 0; i < nlocal; i++) - if (mask[i] & otherbit) mask[i] |= bit; - - // remove atoms if they are in any of the other groups - // AND with inverse mask removes the atom from group - - int inverse = inversemask[igroup]; - - for (int ilist = 1; ilist < length; ilist++) { - otherbit = bitmask[list[ilist]]; - for (i = 0; i < nlocal; i++) - if (mask[i] & otherbit) mask[i] &= inverse; - } - - // style = union - - } else if (strcmp(arg[1],"union") == 0) { - - if (narg < 3) error->all(FLERR,"Illegal group command"); - - int length = narg-2; - std::vector list(length); - - int jgroup; - for (int iarg = 2; iarg < narg; iarg++) { - jgroup = find(arg[iarg]); - if (jgroup == -1) error->all(FLERR,"Group ID does not exist"); - if (dynamic[jgroup]) - error->all(FLERR,"Cannot union groups using a dynamic group"); - list[iarg-2] = jgroup; - } - - // add to group if in any other group in list - - int otherbit; - - for (int ilist = 0; ilist < length; ilist++) { - otherbit = bitmask[list[ilist]]; - for (i = 0; i < nlocal; i++) - if (mask[i] & otherbit) mask[i] |= bit; - } - - // style = intersect - - } else if (strcmp(arg[1],"intersect") == 0) { - - if (narg < 4) error->all(FLERR,"Illegal group command"); - - int length = narg-2; - std::vector list(length); - - int jgroup; - for (int iarg = 2; iarg < narg; iarg++) { - jgroup = find(arg[iarg]); - if (jgroup == -1) error->all(FLERR,"Group ID does not exist"); - if (dynamic[jgroup]) - error->all(FLERR,"Cannot intersect groups using a dynamic group"); - list[iarg-2] = jgroup; - } - - // add to group if in all groups in list - - int otherbit,ok,ilist; - - for (i = 0; i < nlocal; i++) { - ok = 1; - for (ilist = 0; ilist < length; ilist++) { - otherbit = bitmask[list[ilist]]; - if ((mask[i] & otherbit) == 0) ok = 0; - } - if (ok) mask[i] |= bit; - } - - // style = dynamic - // create a new FixGroup to dynamically determine atoms in group - - } else if (strcmp(arg[1],"dynamic") == 0) { - - if (narg < 4) error->all(FLERR,"Illegal group command"); - if (strcmp(arg[0],arg[2]) == 0) - error->all(FLERR,"Group dynamic cannot reference itself"); - if (find(arg[2]) < 0) - error->all(FLERR,"Group dynamic parent group does not exist"); - if (igroup == 0) error->all(FLERR,"Group all cannot be made dynamic"); - - // if group is already dynamic, delete existing FixGroup - - if (dynamic[igroup]) - modify->delete_fix(std::string("GROUP_") + names[igroup]); - - dynamic[igroup] = 1; - - std::string fixcmd = "GROUP_"; - fixcmd += fmt::format("{} {} GROUP",names[igroup],arg[2]); - for (int i = 3; i < narg; i++) fixcmd += std::string(" ") + arg[i]; - modify->add_fix(fixcmd); - - // style = static - // remove dynamic FixGroup if necessary - - } else if (strcmp(arg[1],"static") == 0) { - - if (narg != 2) error->all(FLERR,"Illegal group command"); - - if (dynamic[igroup]) - modify->delete_fix(std::string("GROUP_") + names[igroup]); - - dynamic[igroup] = 0; - - // not a valid group style - - } else error->all(FLERR,"Illegal group command"); + throw e; + } // print stats for changed group diff --git a/unittest/commands/test_groups.cpp b/unittest/commands/test_groups.cpp index 181f000a7c..7feddcc685 100644 --- a/unittest/commands/test_groups.cpp +++ b/unittest/commands/test_groups.cpp @@ -297,7 +297,7 @@ TEST_F(GroupTest, Dynamic) command("group grow delete"); command("variable ramp equal step"); END_HIDE_OUTPUT(); - ASSERT_EQ(group->ngroup, 4); + ASSERT_EQ(group->ngroup, 3); TEST_FAILURE(".*ERROR: Group dynamic cannot reference itself.*", command("group half dynamic half region top"););