Guys,
I'd like my code (MMB) to create arbitrary molecules at runtime from a user-provided set of commands. I didn't get far before I realized I don't know how to do this, and whether it's even feasible. I started by hacking the "Methane" class in Compound.h:
class Methane : public Molecule {
public:
Methane()
{
setBaseCompound("methyl", MethylGroup());
inheritAtomNames("methyl");
convertInboardBondCenterToOutboard();
bondAtom(AliphaticHydrogen("H4"), "methyl/bond", 0.1112);
setBiotypeIndex( "C", Biotype::MethaneC().getIndex() );
setBiotypeIndex( "H1", Biotype::MethaneH().getIndex() );
setBiotypeIndex( "H2", Biotype::MethaneH().getIndex() );
setBiotypeIndex( "H3", Biotype::MethaneH().getIndex() );
setBiotypeIndex( "H4", Biotype::MethaneH().getIndex() );
setCompoundName("Methane");
}
};
I figured the user could issue commands like "setBaseCompound methyl MethylGroup()" and I would somehow parse this. The problem, of course, is that I can't turn the literal "MethylGroup()" into an object. I thought about listing all available descendants of Compound in a vector and looping through it, but that seems clumsy. A ridiculously simple-minded way to go about this would be :
class CustomMolecule : public Molecule {
public:
CustomMolecule(vector <MoleculeBuildCommand> moleculeBuildCommandVector)
{
for ( int i = 0; i < moleculeBuildCommandVector.size(); i++) {
if (moleculeBuildCommandVector.Command.compare("setBaseCompound") == 0) {
if (moleculeBuildCommandVector.BaseCompoundName.compare("methyl") == 0) {setBaseCompound(moleculeBuildCommandVector.BaseCompoundName , MethylGroup()); }
if (moleculeBuildCommandVector.BaseCompoundName.compare("MethyleneGroup") == 0) {setBaseCompound(moleculeBuildCommandVector.BaseCompoundName , MethyleneGroup()); }
}
}
}}
.. and so on for all available compounds. Plus I would have to add parsing of the other commands ("bondAtom", etc).
In any event, is there any way to do this in a way that's not downright dumb?
Sam
making custom molecules at runtime?
- Michael Sherman
- Posts: 807
- Joined: Fri Apr 01, 2005 6:05 pm
Re: making custom molecules at runtime?
I think you could do what you want by using std::map or std::unordered_map (hash table) in C++ to map strings to particular objects or to functions. You would look up the string "methyl" in the map, which would return the object MethylGroup() which you would then feed to setBaseCompound(). That would be very fast and clean.
Regards,
Sherm
Regards,
Sherm
- Samuel Flores
- Posts: 189
- Joined: Mon Apr 30, 2007 1:06 pm
Re: making custom molecules at runtime?
Hi Sherm,
I followed your advice and created a map :
map <const String , Compound> compoundObjectMap;
I initialized it with commands such as:
compoundObjectMap.insert (std::pair <const String , Compound> ("AliphaticHydrogen",AliphaticHydrogen() ))
This actually worked for the setBaseCompound command.
Note that AliphaticHydrogen() is a Compound::SingleAtom. Now I want to actually use this in the bondAtom command. But it is expecting a Compound::SingleAtom, whereas the object I retrieve from compoundObjectMap has been upcast and now thinks it's just a Compound:
41 Compound::SingleAtom myCompound = compoundObjectMapContainer.fetch(moleculeBuildCommandVector[1]);
42 myCompound.setCompoundName(moleculeBuildCommandVector[2]);
44 bondAtom(Compound::SingleAtom(myCompound) ,
45 moleculeBuildCommandVector[3], // name of bond at which to attach the atom
46 atof(moleculeBuildCommandVector[4].c_str()) ); // Default bond length
I can't seem to downcast myCompound back into a Compound::SingleAtom. Is there some way to use compoundObjectMap that doesn't require upcasting? Any ideas on how to get around this?
Sam
Sam
I followed your advice and created a map :
map <const String , Compound> compoundObjectMap;
I initialized it with commands such as:
compoundObjectMap.insert (std::pair <const String , Compound> ("AliphaticHydrogen",AliphaticHydrogen() ))
This actually worked for the setBaseCompound command.
Note that AliphaticHydrogen() is a Compound::SingleAtom. Now I want to actually use this in the bondAtom command. But it is expecting a Compound::SingleAtom, whereas the object I retrieve from compoundObjectMap has been upcast and now thinks it's just a Compound:
41 Compound::SingleAtom myCompound = compoundObjectMapContainer.fetch(moleculeBuildCommandVector[1]);
42 myCompound.setCompoundName(moleculeBuildCommandVector[2]);
44 bondAtom(Compound::SingleAtom(myCompound) ,
45 moleculeBuildCommandVector[3], // name of bond at which to attach the atom
46 atof(moleculeBuildCommandVector[4].c_str()) ); // Default bond length
I can't seem to downcast myCompound back into a Compound::SingleAtom. Is there some way to use compoundObjectMap that doesn't require upcasting? Any ideas on how to get around this?
Sam
Sam
- Samuel Flores
- Posts: 189
- Joined: Mon Apr 30, 2007 1:06 pm
Re: making custom molecules at runtime?
I decided to maintain separate maps for Compound's and Compound::SingleAtom's:
35 map <const String , Compound::SingleAtom> singleAtomMap;
Then I had trouble fetching a SingleAtom:
33 Compound::SingleAtom CompoundObjectMapContainer::fetchSingleAtom(const String compoundName) {
34 if (singleAtomMap.find(compoundName) == singleAtomMap.end()) {
35 ErrorManager::instance <<__FILE__<<":"<<__LINE__<<" No Compound object found with name "<<compoundName <<endl; ErrorManager::instance.treatError() ;
36 }
37 else return singleAtomMap[compoundName];
38 }
I got this error:
In file included from /Users/Sam/svn/RNAToolbox/trunk/src/MoleculeContainer.cpp:3:
In file included from /Users/Sam/svn/RNAToolbox/trunk/include/MoleculeContainer.h:6:
In file included from /usr/local/SimTK/include/SimTKmolmodel.h:47:
In file included from /usr/local/SimTK/include/Simbody.h:38:
In file included from /usr/local/SimTK/include/SimTKcommon.h:64:
In file included from /usr/local/SimTK/include/SimTKcommon/internal/ThreadLocal.h:28:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1/map:66:
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1/bits/stl_map.h:350:45: error: no
matching constructor for initialization of 'mapped_type' (aka 'SimTK::Compound::SingleAtom')
__i = insert(__i, value_type(__k, mapped_type()));
^
/Users/Sam/svn/RNAToolbox/trunk/src/MoleculeContainer.cpp:37:30: note: in instantiation of member function 'std::map<const SimTK::String,
SimTK::Compound::SingleAtom, std::less<const SimTK::String>, std::allocator<std::pair<const SimTK::String, SimTK::Compound::SingleAtom> > >::operator[]'
requested here
else return singleAtomMap[compoundName];
^
/usr/local/SimTK/include/molmodel/internal/Compound.h:1205:5: note: candidate constructor not viable: requires 2 arguments, but 0 were provided
SingleAtom(
^
/usr/local/SimTK/include/molmodel/internal/Compound.h:1203:18: note: candidate constructor (the implicit copy constructor) not viable: requires 1 argument,
but 0 were provided
class Compound::SingleAtom : public Compound {
I think it's saying that there is no default constructor for SingleAtom, only constructors that require 1 or 2 arguments. Can you tell me how to fetch this object? I had no trouble fetching a Compound from a similar map.
35 map <const String , Compound::SingleAtom> singleAtomMap;
Then I had trouble fetching a SingleAtom:
33 Compound::SingleAtom CompoundObjectMapContainer::fetchSingleAtom(const String compoundName) {
34 if (singleAtomMap.find(compoundName) == singleAtomMap.end()) {
35 ErrorManager::instance <<__FILE__<<":"<<__LINE__<<" No Compound object found with name "<<compoundName <<endl; ErrorManager::instance.treatError() ;
36 }
37 else return singleAtomMap[compoundName];
38 }
I got this error:
In file included from /Users/Sam/svn/RNAToolbox/trunk/src/MoleculeContainer.cpp:3:
In file included from /Users/Sam/svn/RNAToolbox/trunk/include/MoleculeContainer.h:6:
In file included from /usr/local/SimTK/include/SimTKmolmodel.h:47:
In file included from /usr/local/SimTK/include/Simbody.h:38:
In file included from /usr/local/SimTK/include/SimTKcommon.h:64:
In file included from /usr/local/SimTK/include/SimTKcommon/internal/ThreadLocal.h:28:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1/map:66:
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1/bits/stl_map.h:350:45: error: no
matching constructor for initialization of 'mapped_type' (aka 'SimTK::Compound::SingleAtom')
__i = insert(__i, value_type(__k, mapped_type()));
^
/Users/Sam/svn/RNAToolbox/trunk/src/MoleculeContainer.cpp:37:30: note: in instantiation of member function 'std::map<const SimTK::String,
SimTK::Compound::SingleAtom, std::less<const SimTK::String>, std::allocator<std::pair<const SimTK::String, SimTK::Compound::SingleAtom> > >::operator[]'
requested here
else return singleAtomMap[compoundName];
^
/usr/local/SimTK/include/molmodel/internal/Compound.h:1205:5: note: candidate constructor not viable: requires 2 arguments, but 0 were provided
SingleAtom(
^
/usr/local/SimTK/include/molmodel/internal/Compound.h:1203:18: note: candidate constructor (the implicit copy constructor) not viable: requires 1 argument,
but 0 were provided
class Compound::SingleAtom : public Compound {
I think it's saying that there is no default constructor for SingleAtom, only constructors that require 1 or 2 arguments. Can you tell me how to fetch this object? I had no trouble fetching a Compound from a similar map.
- Michael Sherman
- Posts: 807
- Joined: Fri Apr 01, 2005 6:05 pm
Re: making custom molecules at runtime?
Hi, Sam.
I think your original approach would work -- you just have to downcast the Compound to the appropriate type (e.g. Compound::SingleAtom) once you know what you have. In C++ you should be able to do static_cast<Compound::SingleAtom>(someCompound).
Since SingleAtom doesn't have a default constructor, it can't be put in an std::map which apparently depends on that. You could fix that by adding a defalut constructor to SingleAtom, or go back to using the Compound base type (which apparently has the needed constructors), and then figure out how to downcast it.
Regards,
Sherm
I think your original approach would work -- you just have to downcast the Compound to the appropriate type (e.g. Compound::SingleAtom) once you know what you have. In C++ you should be able to do static_cast<Compound::SingleAtom>(someCompound).
Since SingleAtom doesn't have a default constructor, it can't be put in an std::map which apparently depends on that. You could fix that by adding a defalut constructor to SingleAtom, or go back to using the Compound base type (which apparently has the needed constructors), and then figure out how to downcast it.
Regards,
Sherm