def set_qmmm_pot(atoms, crack_pos, mm_pot, qm_pot, atom_mask=None):
#  cluster_args = dict(single_cluster=True,
#                      cluster_calc_connect=True,
#                      cluster_hopping=False,
#                      cluster_hopping_nneighb_only=True,
#                      cluster_periodic_z = True, # match Gamma vs. kpts
#                      cluster_vacuum     = 5.0,
#                      hysteretic_buffer=True,
#                      hysteretic_buffer_inner_radius=qm_inner_radius,
#                      hysteretic_buffer_outer_radius=qm_outer_radius,
#                      min_images_only=True,
#                      terminate=False,
#                      force_no_fix_termination_clash=True,
#                      randomise_buffer=False)

  qmmm_pot = ForceMixingPotential(pot1=mm_pot, pot2=qm_pot, atoms=atoms,
                                  qm_args_str='single_cluster cluster_periodic_z carve_cluster ' +
                                              'terminate cluster_hopping=F randomise_buffer=F',
                                  fit_hops=4,
                                  lotf_spring_hops=3,
                                  hysteretic_buffer=True,
                                  hysteretic_buffer_inner_radius=qm_inner_radius,
                                  hysteretic_buffer_outer_radius=qm_outer_radius,
                                  cluster_hopping_nneighb_only=False,
                                  min_images_only=True)

  qmmm_pot.atoms = atoms
  atoms.set_calculator(qmmm_pot)
  fixed_mask = (np.sqrt(map(sum, map(np.square, atoms.positions[:,0:2]-crack_pos[0:2]))) <= 3)
  qm_list  = fixed_mask.nonzero()[0]
  qmmm_pot.set_qm_atoms(qm_list, atoms)
  return qmmm_pot
Exemple #2
0
    def __init__(self, mm_clients, qm_clients, ip, port=0, rundir=None, bulk_scale=None, mpi_obj=None,
                 callback=None, calculator=None, cutoff_skin=1.0, atoms=None,
                 qm_list=None, fpointer=None, finalise=True, error=None, cluster_args=None, test_mode=False,
                 save_clusters=False, force_restart=False, **kwargs):

        def callback_factory(force_prop_name):
            def callback(at):
                at.add_property('force', getattr(at, force_prop_name),
                                overwrite=True)
            return callback

        if isinstance(mm_clients, Potential):
            self.mm_local = True
            self.pot1 = mm_clients
            self.mm_clients = []
        else:
            self.mm_local = False
            self.mm_clients = mm_clients

        if isinstance(qm_clients, Potential):
            self.qm_pot = qm_clients
            self.qm_clients = []
        else:
            self.qm_clients = qm_clients
        
        clients = self.mm_clients + self.qm_clients
        print 'Clients', clients
        if len(clients) > 0:
            self.server = AtomsServerAsync((ip, port), AtomsRequestHandler, clients, bgq=True)
            self.server_thread = threading.Thread(target=self.server.serve_forever)
            self.server_thread.daemon = True
            self.server_thread.start()

        if rundir is None:
            rundir = os.getcwd()
        self.rundir = rundir

        self.label = 1
        self.cluster_args = {}
        if cluster_args is not None:
            self.cluster_args = cluster_args

        if not self.mm_local:
            self.pot1 = Potential('CallbackPot', callback=callback_factory('mm_force'))
        self.pot2 = Potential('CallbackPot', callback=callback_factory('qm_force'))

        self.test_mode = test_mode
        self.save_clusters = save_clusters
        self.force_restart = force_restart

        ForceMixingPotential.__init__(self, self.pot1, self.pot2, bulk_scale=bulk_scale,
                                      mpi_obj=mpi_obj, cutoff_skin=cutoff_skin,
                                      atoms=atoms, qm_list=qm_list, 
                                      fpointer=fpointer, finalise=finalise,
                                      error=error, **kwargs)
Exemple #3
0
def set_qmmm_pot(atoms, crack_pos, mm_pot, qm_pot, atom_mask=None):
    #  cluster_args = dict(single_cluster=True,
    #                      cluster_calc_connect=True,
    #                      cluster_hopping=False,
    #                      cluster_hopping_nneighb_only=True,
    #                      cluster_periodic_z = True, # match Gamma vs. kpts
    #                      cluster_vacuum     = 5.0,
    #                      hysteretic_buffer=True,
    #                      hysteretic_buffer_inner_radius=qm_inner_radius,
    #                      hysteretic_buffer_outer_radius=qm_outer_radius,
    #                      min_images_only=True,
    #                      terminate=False,
    #                      force_no_fix_termination_clash=True,
    #                      randomise_buffer=False)

    qmmm_pot = ForceMixingPotential(
        pot1=mm_pot,
        pot2=qm_pot,
        atoms=atoms,
        qm_args_str='single_cluster cluster_periodic_z carve_cluster ' +
        'terminate cluster_hopping=F randomise_buffer=F',
        fit_hops=4,
        lotf_spring_hops=3,
        hysteretic_buffer=True,
        hysteretic_buffer_inner_radius=qm_inner_radius,
        hysteretic_buffer_outer_radius=qm_outer_radius,
        cluster_hopping_nneighb_only=False,
        min_images_only=True)

    qmmm_pot.atoms = atoms
    atoms.set_calculator(qmmm_pot)
    fixed_mask = (np.sqrt(
        map(sum, map(np.square, atoms.positions[:, 0:2] - crack_pos[0:2]))) <=
                  3)
    qm_list = fixed_mask.nonzero()[0]
    qmmm_pot.set_qm_atoms(qm_list, atoms)
    return qmmm_pot
Exemple #4
0
# Density functional tight binding (DFTB) potential
qm_pot = Potential(qm_init_args,
                   param_filename=param_file)

# Parameters which control how the QM calculation is carried out:
# we use a single cluster, periodic in the z direction and terminated
# with hydrogen atoms. The positions of the outer layer of buffer atoms
# are not randomised.
qm_args_str = ('single_cluster cluster_periodic_z '+
               'carve_cluster terminate randomise_buffer=F')

# Construct the QM/MM potential, which mixes QM and MM forces
qmmm_pot = ForceMixingPotential(pot1=mm_pot, pot2=qm_pot,
                                qm_args_str=qm_args_str,
                                fit_hops=4,
                                lotf_spring_hops=3,
                                buffer_hops=4)

# Use the force mixing potential as the Atoms' calculator
atoms.set_calculator(qmmm_pot)


# *** Set up the initial QM region ****

qm_list = update_hysteretic_qm_region(atoms, [], orig_crack_pos,
                                      qm_inner_radius, qm_outer_radius)
qmmm_pot.set_qm_atoms(qm_list)

# *** Milestone 3.1 -- exit early - we don't want to run the classical MD! ***
Exemple #5
0
    def __init__(self,
                 mm_clients,
                 qm_clients,
                 ip,
                 port=0,
                 rundir=None,
                 bulk_scale=None,
                 mpi_obj=None,
                 callback=None,
                 calculator=None,
                 cutoff_skin=1.0,
                 atoms=None,
                 qm_list=None,
                 fpointer=None,
                 finalise=True,
                 error=None,
                 cluster_args=None,
                 test_mode=False,
                 save_clusters=False,
                 force_restart=False,
                 **kwargs):
        def callback_factory(force_prop_name):
            def callback(at):
                at.add_property('force',
                                getattr(at, force_prop_name),
                                overwrite=True)

            return callback

        if isinstance(mm_clients, Potential):
            self.mm_local = True
            self.pot1 = mm_clients
            self.mm_clients = []
        else:
            self.mm_local = False
            self.mm_clients = mm_clients

        if isinstance(qm_clients, Potential):
            self.qm_pot = qm_clients
            self.qm_clients = []
        else:
            self.qm_clients = qm_clients

        clients = self.mm_clients + self.qm_clients
        print 'Clients', clients
        if len(clients) > 0:
            self.server = AtomsServerAsync((ip, port),
                                           AtomsRequestHandler,
                                           clients,
                                           bgq=True)
            self.server_thread = threading.Thread(
                target=self.server.serve_forever)
            self.server_thread.daemon = True
            self.server_thread.start()

        if rundir is None:
            rundir = os.getcwd()
        self.rundir = rundir

        self.label = 1
        self.cluster_args = {}
        if cluster_args is not None:
            self.cluster_args = cluster_args

        if not self.mm_local:
            self.pot1 = Potential('CallbackPot',
                                  callback=callback_factory('mm_force'))
        self.pot2 = Potential('CallbackPot',
                              callback=callback_factory('qm_force'))

        self.test_mode = test_mode
        self.save_clusters = save_clusters
        self.force_restart = force_restart

        ForceMixingPotential.__init__(self,
                                      self.pot1,
                                      self.pot2,
                                      bulk_scale=bulk_scale,
                                      mpi_obj=mpi_obj,
                                      cutoff_skin=cutoff_skin,
                                      atoms=atoms,
                                      qm_list=qm_list,
                                      fpointer=fpointer,
                                      finalise=finalise,
                                      error=error,
                                      **kwargs)
Exemple #6
0
    def calc(self,
             at,
             energy=None,
             force=None,
             virial=None,
             local_energy=None,
             local_virial=None,
             args_str=None,
             error=None,
             **kwargs):

        clusters = []
        orig_label = self.label
        if not self.mm_local:
            # always submit the MM calc
            if self.test_mode:
                clusters.append(at)
            else:
                self.server.put(at,
                                0,
                                self.label,
                                force_restart=self.force_restart)
            self.label += 1

        do_qm = not self.get('method').startswith('lotf') or self.get(
            'lotf_do_qm')

        if do_qm:
            #print 'REGIONS', [ k for k in at.properties.keys() if re.match('hybrid_[0-9]+', k) ]
            n_region = len([
                k for k in at.properties.keys()
                if re.match('hybrid_[0-9]+', k)
            ])

            if self.get('calc_weights'):
                system_timer('create_hybrid_weights')
                # overall hybrid property is union of all the hybrids
                if not hasattr(at, 'hybrid'):
                    at.add_property('hybrid', 0)
                at.hybrid[:] = 0
                for i in frange(n_region):
                    hybrid = getattr(at, 'hybrid_%d' % i)
                    at.hybrid[hybrid ==
                              HYBRID_ACTIVE_MARK] = HYBRID_ACTIVE_MARK
                if not hasattr(at, 'hybrid_mark'):
                    at.add_property('hybrid_mark', at.hybrid)
                at.hybrid_mark[:] = 0
                at.hybrid_mark = at.hybrid
                create_hybrid_weights_args = self.cluster_args.copy()
                create_hybrid_weights_args['buffer_hops'] = 0
                create_hybrid_weights_args[
                    'transition_hops'] = 0  # ensure a fast exit
                # overall hybrid -> hybrid_mark, weight_region1
                create_hybrid_weights(
                    at,
                    args_str=quippy.util.args_str(create_hybrid_weights_args))
                system_timer('create_hybrid_weights')

            # make clusters and submit to QM clients
            system_timer('make_clusters')
            for i in frange(n_region):
                hybrid_name = 'hybrid_%d' % i
                hybrid_mark_name = 'hybrid_mark_%d' % i
                if self.get('calc_weights'):
                    hybrid = getattr(at, hybrid_name)
                    if not hasattr(at, hybrid_mark_name):
                        at.add_property(hybrid_mark_name, HYBRID_NO_MARK)
                    hybrid_mark = getattr(at, hybrid_mark_name)

                    # set marks to allow previously active atoms to become buffer atoms
                    # create_hybrid_weights will then set the buffer marks
                    hybrid_mark[hybrid_mark ==
                                HYBRID_ACTIVE_MARK] = HYBRID_BUFFER_MARK
                    hybrid_mark[hybrid ==
                                HYBRID_ACTIVE_MARK] = HYBRID_ACTIVE_MARK

                    print('region %d, sum(hybrid) %d, sum(hybrid_mark) %d' %
                          (i, sum(hybrid), sum(hybrid_mark)))

                    create_hybrid_weights_args = self.cluster_args.copy()
                    create_hybrid_weights_args['run_suffix'] = '_%d' % i
                    create_hybrid_weights_args_str = quippy.util.args_str(
                        create_hybrid_weights_args)
                    print 'calling create_hybrid_weights with args_str %s' % create_hybrid_weights_args_str
                    create_hybrid_weights(
                        at, args_str=create_hybrid_weights_args_str)

                cluster_args_str = quippy.util.args_str(self.cluster_args)
                print 'calling create_cluster_simple with args_str %s' % cluster_args_str
                c = create_cluster_simple(at,
                                          mark_name=hybrid_mark_name,
                                          args_str=cluster_args_str)

                client_id = i
                if self.mm_local:
                    client_id -= 1
                if self.save_clusters:
                    c.write(
                        os.path.join(
                            self.rundir, 'cluster.client-%03d.label-%04d.xyz' %
                            (client_id, self.label)))
                if self.test_mode:
                    clusters.append(c)
                else:
                    self.server.put(c,
                                    client_id,
                                    self.label,
                                    force_restart=self.force_restart)
                self.label += 1
            system_timer('make_clusters')

        # wait for results to be ready
        system_timer('get_results')
        if self.test_mode:
            results = []
            for i, cluster in enumerate(clusters):
                result = cluster.copy()
                result.set_cutoff(self.qm_pot.cutoff())
                result.calc_connect()
                self.qm_pot.calc(result, force=True)
                result.params['label'] = orig_label + i
                results.append(result)
        else:
            results = self.server.get_results()
        system_timer('get_results')

        system_timer('process_results')
        if self.mm_local:
            qm_results = results
        else:
            # process MM results
            mm_results, qm_results = results[:1], results[1:]
            mm_result = mm_results[0]
            at.add_property('mm_force', mm_result.force, overwrite=True)

        if do_qm:
            # process QM results
            at.add_property('qm_force', 0., n_cols=3, overwrite=True)

            # extract forces from each cluster
            for i, r in fenumerate(qm_results):
                #print 'qm forces (orig order?):'
                #print '\n'.join(['%d: pos=%s f=%s' % (j, p, f) for j, p, f in zip(r.index, r.pos, r.force)])

                client_id = i
                if self.mm_local:
                    client_id -= 1
                if self.save_clusters:
                    r.write(
                        os.path.join(
                            self.rundir, 'results.client-%03d.label-%04d.xyz' %
                            (client_id, r.label)))

                mark_name = 'hybrid_mark_%d' % i
                mask = getattr(r, mark_name) == HYBRID_ACTIVE_MARK
                #print 'Cluster %d: QM forces on atoms %s' % (i, r.index[mask])
                #print r.force[:, mask].T
                # HL if set_fortran is false we need to reduce the index here because
                # the atoms object is expecting python indexing.
                at.qm_force[:,
                            [ind - 1
                             for ind in list(r.index[mask])]] = r.force[:,
                                                                        mask]

        system_timer('process_results')

        # now call the parent calc() to do the force mixing etc.
        force_mixing_args = kwargs.copy()
        force_mixing_args.update(self.cluster_args)
        force_mixing_args['calc_weights'] = False  # already done this above
        #print 'calling ForceMixingPotential.calc() with args %r' % force_mixing_args
        ForceMixingPotential.calc(self, at, energy, force, virial,
                                  local_energy, local_virial, args_str, error,
                                  **force_mixing_args)
Exemple #7
0
mm_pot.set_default_quantities(['stresses'])

# Density functional tight binding (DFTB) potential
qm_pot = Potential(qm_init_args, param_filename=param_file)

# Construct the QM/MM potential, which mixes QM and MM forces.
# The qm_args_str parameters control how the QM calculation is carried out:
# we use a single cluster, periodic in the z direction and terminated
# with hydrogen atoms. The positions of the outer layer of buffer atoms
# are not randomised.
qmmm_pot = ForceMixingPotential(
    pot1=mm_pot,
    pot2=qm_pot,
    qm_args_str='single_cluster cluster_periodic_z carve_cluster ' +
    'terminate cluster_hopping=F randomise_buffer=F',
    fit_hops=4,
    lotf_spring_hops=3,
    hysteretic_buffer=True,
    hysteretic_buffer_inner_radius=7.0,
    hysteretic_buffer_outer_radius=9.0,
    cluster_hopping_nneighb_only=False,
    min_images_only=True)

# Use the force mixing potential as the Atoms' calculator
atoms.set_calculator(qmmm_pot)

# *** Set up the initial QM region ****

qm_list = update_hysteretic_qm_region(atoms, [], orig_crack_pos,
                                      qm_inner_radius, qm_outer_radius)
qmmm_pot.set_qm_atoms(qm_list)
Exemple #8
0
                          param_str="""
 <params>
   <LMTO_TBE_params n_types="3" control_file="ctrl.fe">
     <per_type_data type="1" atomic_num="26"/>
     <per_type_data type="2" atomic_num="6"/>
     <per_type_data type="3" atomic_num="1"/>
   </LMTO_TBE_params>
 </params>""")
       print 'Initializing LOTF Potential, qm_radii:', params.qm_inner_radius, params.qm_outer_radius
       qmmm_pot = ForceMixingPotential(
           pot1=mm_pot,
           pot2=qm_pot,
           atoms=defect,
           qm_args_str='single_cluster cluster_periodic_z carve_cluster '
           + 'terminate=F cluster_hopping=F randomise_buffer=F',
           fit_hops=2,
           lotf_spring_hops=2,
           hysteretic_buffer=True,
           hysteretic_buffer_inner_radius=params.hyst_buffer_inner,
           hysteretic_buffer_outer_radius=params.hyst_buffer_outer,
           cluster_hopping_nneighb_only=False,
           min_images_only=True)
       defect.set_calculator(qmmm_pot)
   elif pot_type == 'EAM':
       eam_pot = os.path.join(potdir, 'PotBH.xml')
       r_scale = 1.00894848312
       pot = Potential(
           'IP EAM_ErcolAd do_rescale_r=T r_scale={0}'.format(r_scale),
           param_filename=eam_pot)
       defect.set_calculator(pot)
   else:
Exemple #9
0
right = atoms.positions[:, 0].max()

fixed_mask = ((abs(atoms.positions[:, 1] - top) < 1.0) |
              (abs(atoms.positions[:, 1] - bottom) < 1.0))
fix_atoms = FixAtoms(mask=fixed_mask)

strain_atoms = ConstantStrainRate(orig_height, strain_rate*timestep)
atoms.set_constraint([fix_atoms, strain_atoms])

mm_pot = Potential("IP SW", param_filename="params.xml", cutoff_skin=cutoff_skin)
mm_pot.set_default_properties(['stresses'])

# Density functional tight binding (DFTB) potential
qm_pot = Potential(qm_init_args, param_filename="params.xml")
qm_list = update_hysteretic_qm_region(atoms, [], orig_crack_pos, qm_inner_radius, qm_outer_radius)
qmmm_pot = ForceMixingPotential(pot1=mm_pot,
                                pot2=qm_pot)
atoms.set_calculator(qmmm_pot)
qmmm_pot.set_qm_atoms(qm_list)


# *** Set up the initial QM region ****


#
#
# MaxwellBoltzmannDistribution(atoms, 2.0*sim_T)
#
# dynamics = LOTFDynamics(atoms, timestep, extrapolate_steps, check_force_error=True)
#
# def printstatus():
#     if dynamics.nsteps == 1:
Exemple #10
0
                             npar=4,
                             **vasp_args)

    qm_pot = Potential(calculator=SocketCalculator(vasp_client))

#construct_buffer_use_only_heavy_atoms might be necessary to stop confusion with H!
qmmm_pot = ForceMixingPotential(
    pot1=mm_pot,
    pot2=qm_pot,
    atoms=atoms,
    qm_args_str='carve_cluster=T cluster_periodic_z=T cluster_calc_connect=T '
    + 'single_cluster=T cluster_vacuum=5.0 terminate=F print_clusters=F',
    fit_hops=4,
    carve_cluster=True,
    lotf_spring_hops=3,
    terminate=False,
    calc_connect=True,
    buffer_hops=3,
    hysteretic_buffer=True,
    cluster_vacuum=5.0,
    cluster_hopping=True,
    single_cluster=True,
    hysteretic_buffer_inner_radius=qm_inner_radius,
    hysteretic_buffer_outer_radius=qm_outer_radius,
    cluster_hopping_nneighb_only=True,
    min_images_only=True)

atoms.cutoff = 3.0
atoms.calc_connect()
qm_list = update_hysteretic_qm_region(atoms, [], crack_pos, qm_inner_radius,
                                      qm_outer_radius)
qmmm_pot.set_qm_atoms(qm_list, atoms)
Exemple #11
0
    def calc(self, at, energy=None, force=None, virial=None,
             local_energy=None, local_virial=None,
             args_str=None, error=None, **kwargs):

        clusters = []
        orig_label = self.label
        if not self.mm_local:
            # always submit the MM calc
            if self.test_mode:
                clusters.append(at)
            else:
                self.server.put(at, 0, self.label, force_restart=self.force_restart)
            self.label += 1
        
        do_qm = not self.get('method').startswith('lotf') or self.get('lotf_do_qm')
        
        if do_qm:
            #print 'REGIONS', [ k for k in at.properties.keys() if re.match('hybrid_[0-9]+', k) ]
            n_region = len([ k for k in at.properties.keys() if re.match('hybrid_[0-9]+', k) ])

            if self.get('calc_weights'):
                system_timer('create_hybrid_weights')
                # overall hybrid property is union of all the hybrids
                if not hasattr(at, 'hybrid'):
                    at.add_property('hybrid', 0)
                at.hybrid[:] = 0
                for i in frange(n_region):
                    hybrid = getattr(at, 'hybrid_%d' % i)
                    at.hybrid[hybrid == HYBRID_ACTIVE_MARK] = HYBRID_ACTIVE_MARK
                if not hasattr(at, 'hybrid_mark'):
                    at.add_property('hybrid_mark', at.hybrid)
                at.hybrid_mark[:] = 0
                at.hybrid_mark = at.hybrid
                create_hybrid_weights_args = self.cluster_args.copy()
                create_hybrid_weights_args['buffer_hops'] = 0
                create_hybrid_weights_args['transition_hops'] = 0 # ensure a fast exit
                # overall hybrid -> hybrid_mark, weight_region1
                create_hybrid_weights(at, args_str=quippy.util.args_str(create_hybrid_weights_args))
                system_timer('create_hybrid_weights')
            
            # make clusters and submit to QM clients
            system_timer('make_clusters')
            for i in frange(n_region):
                hybrid_name = 'hybrid_%d' % i
                hybrid_mark_name = 'hybrid_mark_%d' % i
                if self.get('calc_weights'):
                    hybrid = getattr(at, hybrid_name)
                    if not hasattr(at, hybrid_mark_name):
                        at.add_property(hybrid_mark_name, HYBRID_NO_MARK)
                    hybrid_mark = getattr(at, hybrid_mark_name)

                    # set marks to allow previously active atoms to become buffer atoms
                    # create_hybrid_weights will then set the buffer marks
                    hybrid_mark[hybrid_mark == HYBRID_ACTIVE_MARK] = HYBRID_BUFFER_MARK
                    hybrid_mark[hybrid == HYBRID_ACTIVE_MARK] = HYBRID_ACTIVE_MARK

                    print ('region %d, sum(hybrid) %d, sum(hybrid_mark) %d' % 
                            (i, sum(hybrid), sum(hybrid_mark)))

                    create_hybrid_weights_args = self.cluster_args.copy()
                    create_hybrid_weights_args['run_suffix'] = '_%d' % i
                    create_hybrid_weights_args_str = quippy.util.args_str(create_hybrid_weights_args)
                    print 'calling create_hybrid_weights with args_str %s' % create_hybrid_weights_args_str
                    create_hybrid_weights(at, args_str=create_hybrid_weights_args_str)

                cluster_args_str = quippy.util.args_str(self.cluster_args)
                print 'calling create_cluster_simple with args_str %s' % cluster_args_str
                c = create_cluster_simple(at, mark_name=hybrid_mark_name, args_str=cluster_args_str)
                
                client_id = i
                if self.mm_local:
                    client_id -= 1
                if self.save_clusters:
                    c.write(os.path.join(self.rundir,
                                         'cluster.client-%03d.label-%04d.xyz' %
                                         (client_id, self.label)))
                if self.test_mode:
                    clusters.append(c)
                else:
                    self.server.put(c, client_id, self.label, force_restart=self.force_restart)
                self.label += 1
            system_timer('make_clusters')

        # wait for results to be ready
        system_timer('get_results')
        if self.test_mode:
            results = []
            for i, cluster in enumerate(clusters):
                result = cluster.copy()
                result.set_cutoff(self.qm_pot.cutoff())
                result.calc_connect()
                self.qm_pot.calc(result, force=True)
                result.params['label'] = orig_label + i
                results.append(result)
        else:
            results = self.server.get_results()
        system_timer('get_results')

        system_timer('process_results')
        if self.mm_local:
            qm_results = results
        else:
            # process MM results
            mm_results, qm_results = results[:1], results[1:]
            mm_result = mm_results[0]
            at.add_property('mm_force', mm_result.force, overwrite=True)

        if do_qm:
            # process QM results
            at.add_property('qm_force', 0., n_cols=3, overwrite=True)

            # extract forces from each cluster
            for i, r in fenumerate(qm_results):
                #print 'qm forces (orig order?):'
                #print '\n'.join(['%d: pos=%s f=%s' % (j, p, f) for j, p, f in zip(r.index, r.pos, r.force)])

                client_id = i
                if self.mm_local:
                    client_id -= 1
                if self.save_clusters:
                    r.write(os.path.join(self.rundir,
                                         'results.client-%03d.label-%04d.xyz' %
                                         (client_id, r.label)))

                mark_name = 'hybrid_mark_%d' % i
                mask = getattr(r, mark_name) == HYBRID_ACTIVE_MARK
                #print 'Cluster %d: QM forces on atoms %s' % (i, r.index[mask])
                #print r.force[:, mask].T
        # HL if set_fortran is false we need to reduce the index here because
        # the atoms object is expecting python indexing.
                at.qm_force[:, [ind-1 for ind in list(r.index[mask])]] = r.force[:, mask]

        system_timer('process_results')

        # now call the parent calc() to do the force mixing etc.
        force_mixing_args = kwargs.copy()
        force_mixing_args.update(self.cluster_args)
        force_mixing_args['calc_weights'] = False # already done this above
        #print 'calling ForceMixingPotential.calc() with args %r' % force_mixing_args
        ForceMixingPotential.calc(self, at, energy, force, virial, local_energy, 
                                  local_virial, args_str, error, **force_mixing_args)