> > It will be up to the spin-specific function passed in by the calling > > function to handle the 'spin.select' value. Because of the complexity > > of the loop, the use of this single 'spin_loop()' function will simplify > > the relax code base, will minimise potential bugs, and will simplify > > future changes to the relax data model (if necessary). > > use of an iterator object will provide flexibility as iterators can be > wrapped filtered and generally mucked about with using pythons loops > and iter tools. Whats more they are doddle to code as all you do is > write an ordinary function and call yield with a value each time you > have identified a selected spin > (http://www.python.org/dev/peps/pep-0255/).... This also allows > arbitrary selection to be added as wrapper iterators or filtered > iterators >
The UCSF selection syntax is sufficiently powerful for all relax needs, as well as being simple and well known amongst potential users. It seems like an excellent alternative to the current spin selection methods. Coding the parser as an iterator is also a good idea.
Agreed. Before we go down the path of the Molmol/UCSF syntax, is there another more intuitive syntax used by other NMR software?
To extend things a bit further, we could incorporate all of this with a functor similar to that proposed for handling multiple run selections (https://mail.gna.org/public/relax-devel/2007-01/msg00013.html and https://mail.gna.org/public/relax-devel/2007-01/msg00020.html ). Of course the spin functor would opperate at a different level of code to the run functor - whereas all user functions would be instances of the run functor, only certain internal functions (those that act on a single spin) would be instances of the spin functor.
The user functions are instances of the run functor? Do you mean the functions called by the user functions are instances?
The spin selection itself is used quite differently by different parts of the code base and I'm not sure if implementing the parser as a generator is a good idea. For example the selection string could be passed to the spin loop function which is a generator yielding the spin system data container. Using Gary's spin system selection and generator ideas (https://mail.gna.org/public/relax-devel/2007-01/msg00014.html, Message-id: <f001463a0701071417w6bd7927cp8fdd052e698575ec@xxxxxxxxxxxxxx>), the spin loop presented at https://mail.gna.org/public/relax-devel/2006-10/msg00057.html (Message-id: <1160557041.9523.74.camel@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx>) would be simple. One argument goes into the function, the selection string, and the final line would be a yield statement. In this spin loop example, maybe it would be useful to have separate generators/iterators for the molecules, residues, and atoms. Then the spin loop could become:
def spin_loop(selection=None): """Function for selectively looping over all spins."""
# Reassign the data container. data = self.relax.data[self.relax.run]
# Loop over the molecules. for mol in data.mol: # Skip the molecule if there is no match to the selection. skip = 1 for mol_name in mol_iterator(selection): if mol_name == mol.name: skip = 0 if skip: continue
# Loop over the residues. for res in mol.res: # Skip the residue if there is no match to the selection. skip = 1 for res_num, res_name in res_iterator(selection): if res_num == res.num and res_name == res.name: skip = 0 if skip: continue
# Loop over the spins. for spin in res.spin: # Skip the spin if there is no match to the selection. skip = 1 for atom_num, atom_name in atom_iterator(selection): if atom_num == spin.num and atom_name == spin.name: skip = 0 if skip: continue
# Yield the spin system data container. yield spin
This setup could possibly be more numerically efficient than say:
def spin_loop(selection=None): """Function for selectively looping over all spins."""
# Reassign the data container. data = self.relax.data[self.relax.run]
# Loop over the molecules. for mol in data.mol: # Loop over the residues. for res in mol.res: # Loop over the spins. for spin in res.spin: # Skip the spin if there is no match to the selection. skip = 1 for mol_name, res_num, res_name, atom_num, atom_name in atom_iterator(selection): if mol_name == mol.name and res_num == res.num and res_name == res.name and atom_num == spin.num and atom_name == spin.name: skip = 0 if skip: continue
# Yield the spin system data container. yield spin
However rather than using a generator for the selection, maybe the function 'is_selected' could be created:
def spin_loop(selection=None): """Function for selectively looping over all spins."""
# Reassign the data container. data = self.relax.data[self.relax.run]
# Loop over the molecules. for mol in data.mol: # Loop over the residues. for res in mol.res: # Loop over the spins. for spin in res.spin: # Skip the spin if there is no match to the selection. if not is_selected(selection, mol.name, res.num, res.name, spin.num, spin.name): continue
# Yield the spin system data container. yield spin
Edward