def _parse_bias_file(bias_filename):
    """
    Return a list of bias positions and force constants from a "bias file" of 
    the format:

    bias_position1 force_constant1 bias_position2 force_constant2 ...

    where any number of bias specifications are permitted. 
    """
    bias_positions = []
    force_constants = []
    for i,line in enumerate(open(bias_filename,'r')):
        try:
            line = line[:line.index('#')] # ignore comments
        except ValueError:
            pass
        if len(line) > 0: # ignore empty lines
            tokens = line.strip().split()
            if len(bias_positions) == len(force_constants) == 0:
                dim = len(tokens)/2
            else:
                if len(tokens)/2 != dim:
                    _exit('Bad harmonic bias specification on line %d of %s'
                          %(i+1,bias_filename))
            bias_positions.append([float(r0) for r0 in tokens[0::2]])
            force_constants.append([float(k0) for k0 in tokens[1::2]])
    return bias_positions,force_constants
    def _checkInput(self):
        pj_amber_job._checkInput(self)
        #    Check that all umbrellas are the same temperature. The reduced 
        # energies calculated in this module do not account for replicas 
        # running at different temperatures.
        #
        temp0 = self.states.state_params_are_same('cntrl','temp0')
        if not temp0:
            _exit('All temperatures MUST be the same when using AMBER-US.')
        self.beta = 1./(at.KB*temp0)

        # Umbrella sampling state information.
        #
        setup_us_states_from_configobj(self.states,self.keywords,self.verbose)
def setup_us_states_from_configobj(states, keywords, verbose=False):
    """
    Augment a set of AMBER states to include umbrella sampling state
    information (i.e. restraint information) from a ConfigObj.
    """
    # Umbrella Sampling Parameters
    #
    nreplicas = len(states)
    bias_filename = keywords.get('BIAS_FILE')
    if bias_filename is not None:
        try:
            bias_positions,force_constants = _parse_bias_file(bias_filename)
        except IOError:
            _exit('Problem reading BIAS_FILE = %s'%bias_filename)
    elif (keywords.get('FORCE_CONSTANTS') is not None
          and keywords.get('BIAS_POSITIONS') is not None):
        force_constants = _parse_state_params(
            keywords.get('FORCE_CONSTANTS'))
        bias_positions = _parse_state_params(
            keywords.get('BIAS_POSITIONS'))
    else:
        _exit('No bias specifications! Either BIAS_FILE or BIAS_POSITIONS and '
              'FORCE_CONSTANTS must be specified.')

    if len(bias_positions) != len(force_constants):
        _exit('Number of FORCE_CONSTANTS not equal to number of BIAS_POSITIONS')
    if (len(bias_positions) != nreplicas
        or len(force_constants) != nreplicas):
        _exit('Expected %d umbrella parameter sets, but instead found %d '
              'FORCE_CONSTANTS and %d BIAS_POSITIONS'
              %(nreplicas,len(force_constants),len(bias_positions)))    

    # Look for a restraint template (try the basename?)
    basename = keywords.get('ENGINE_INPUT_BASENAME')
    if keywords.get('AMBER_RESTRAINT_TEMPLATE') is not None:
        restraint_template = keywords.get('AMBER_RESTRAINT_TEMPLATE')
    else:
        restraint_template = '%s.RST'%basename
    if verbose:
        print 'Using restraint template file: %s'%restraint_template

    # Read the restraint template and then modify the restraint objects. 
    for n,state in enumerate(states):
        state.add_restraints(restraint_template)
        state.mdin.nmr_vars['DISANG'] = DISANG_NAME
        state.rstr.set_restraint_params(r0=bias_positions[n],
                                        k0=force_constants[n])