Exemple #1
0
def generateParticleList(state, boxsize, D, Drot, randomSeed = -1):
    '''
    Generate a random particle list of two particles corresponding to either state A or state B. As each
    state has several different configurations, this functions picks one randomly. The definition of the
    states is taken form patchyDimer.cpp
    :param state: bound state in which the two particle list should be in, A or B
    :param D and Drot: diffusion coefficients of particles.
    :param randomSeed: seed for python random generator. Important to specify in parallel runs. Default value of -1
    will use the default seed.
    :return: random particle list corresponding to either state A or state B.
    '''

    # Transform boxsize to vector if neccesarry.
    boxsize = boxsize - 1 # to ensure the whole compund is initially inside box (1 is the norm of relpos below)
    if np.isscalar(boxsize):
        boxsize = np.array([boxsize, boxsize, boxsize])

    position1 = np.array([0.0, 0.0, 0.0])
    #position1 = np.array([boxsize[0]*random.random()-0.5*boxsize[0],
    #                      boxsize[1]*random.random()-0.5*boxsize[1],
    #                      boxsize[2]*random.random()-0.5*boxsize[2]])
    orientation1 = np.array([1.0, 0.0, 0.0, 0.0])
    # Define relative position
    relpos1 = np.array([np.cos(angleDiff / 2.0),np.sin(angleDiff / 2.0), 0])
    relpos2 = np.array([np.cos(angleDiff / 2.0),np.sin(-1*angleDiff / 2.0), 0])
    relPos1orthogonal = np.array([-1.0 * np.sin(angleDiff / 2.0), np.cos(angleDiff / 2.0), 0.0])
    relPos2orthogonal = np.array([np.sin(angleDiff / 2.0), np.cos(angleDiff / 2.0), 0.0])
    # Define relative rotations
    rotations = [None]*8
    rotations[0] = np.pi * relPos1orthogonal
    rotations[1] = np.array([0.0, 0.0, -2 * np.pi / 5.0])
    rotations[2] = np.array([0.0, 0.0, np.pi])
    rotations[3] = np.array([0.0, np.pi, 0.0])
    # --first 4 rotations correspond to binding on top patch of particle 1, next 4 rotations to bottom patch
    rotations[4] = np.pi * relPos2orthogonal
    rotations[5] = np.array([0.0, 0.0, 2 * np.pi / 5.0])
    rotations[6] = np.array([0.0, 0.0, np.pi])
    rotations[7] = np.array([0.0, np.pi, 0.0])
    # Convert axis-angle rotations to quaternions
    quatRotations = [None]*8
    for i in range(8):
        quatRotations[i] = quaternions.angle2quat(rotations[i])
    # Assign correct position2 depending on the states
    pos2 = [None]*8
    pos2[0:4] = [position1 + relpos1]*4
    pos2[4:8] = [position1 + relpos2]*4

    # Assign position and orientation depending on initial state A or B
    if state == 'A':
        substate = random.choice([1,2,5,6])
        position2 = pos2[substate - 1]
        orientation2 = quatRotations[substate - 1]
    if state == 'B':
        substate = random.choice([3,4,7,8])
        position2 = pos2[substate - 1]
        orientation2 = quatRotations[substate - 1]
    part1 = msmrd2.particle(D, Drot, position1, orientation1)
    part2 = msmrd2.particle(D, Drot, position2, orientation2)
    partlist = msmrd2.integrators.particleList([part1, part2])
    return partlist
Exemple #2
0
def generateParticleList(state, boxsize, types, unboundMSMs, randomSeed=-1):
    '''
    Generate a random particle list of two particles corresponding to an initial unbound state. As each
    state has several different configurations, this functions picks one randomly. The definition of the
    states is taken form patchyProtein.cpp (trajectory)
    :param state: bound state in which the two particle list should begin in (can be a list of states)
    :param boxsize: size of simulation box, if scalar it assumes the three box edges are the same in all dimensions
    :param types: array containing the particle types. The index should correspond to that of of the particle list.
    :param unboundMSMs: list of unboundMSM, needed to extract diffusion coefficients of particles.
    :param randomSeed: seed for python random generator. Important to specify in parallel runs. Default value of -1
    will use the default seed.
    :return: random particle list corresponding to one of the initialBoundstates.
    '''

    # Transform boxsize to vector if neccesarry.
    boxsize = boxsize - 1  # to ensure the whole compund is initially inside box (1 is the norm of relpos below)
    if np.isscalar(boxsize):
        boxsize = np.array([boxsize, boxsize, boxsize])

    # Transform state to list if neccesary
    if np.isscalar(state):
        state = [state]

    position1 = np.array([0.0, 0.0, 0.0])
    orientation1 = np.array([1.0, 0.0, 0.0, 0.0])
    # Define relative positions
    relPos = [None] * numBoundStates
    relPos[0] = np.array([1., 0., 0.])
    relPos[1] = np.array([0., 1., 0.])
    relPos[2] = np.array([0., 0., 1.])
    relPos[3] = np.array([-1., 0., 0.])
    relPos[4] = np.array([0., -1., 0.])
    relPos[5] = np.array([0., 0., -1.])
    # Define relative rotations
    rotations = [None] * numBoundStates
    rotations[0] = np.array([0.0, 0.0, np.pi])
    rotations[1] = np.array([0.0, 0.0, -np.pi / 2.0])
    rotations[2] = np.array([0.0, np.pi / 2.0, 0.0])
    rotations[3] = np.array([0.0, 0.0, 0.0])
    rotations[4] = np.array([0.0, 0.0, np.pi / 2.0])
    rotations[5] = np.array([0.0, -np.pi / 2.0, 0.0])
    # Convert axis-angle rotations to quaternions
    quatRotations = [None] * numBoundStates
    for i in range(numBoundStates):
        quatRotations[i] = quaternions.angle2quat(rotations[i])

    # Assign position and orientation depending on initial bound state (between 0 and 5)
    substate = np.random.choice(
        state)  # if states was originally a scalar, always picks that value.
    position2 = relPos[substate - 1]
    orientation2 = quatRotations[substate - 1]
    p1State = 0
    p2State = 0
    # Obtain diffusion coefficients from unboundMSM
    D1 = unboundMSMs[types[0]].D[p1State]
    Drot1 = unboundMSMs[types[0]].Drot[p1State]
    D2 = unboundMSMs[types[1]].D[p2State]
    Drot2 = unboundMSMs[types[1]].Drot[p2State]
    # Define particles
    part1 = msmrd2.particle(0, p1State, D1, Drot1, position1, orientation1)
    part2 = msmrd2.particle(1, p2State, D2, Drot2, position2, orientation2)
    part1.setMSMoff()  #Important or it will yield error.
    partlist = msmrd2.integrators.particleList([part1, part2])
    return partlist
Exemple #3
0
def randomParticleList(numparticles,
                       boxsize,
                       separationDistance,
                       D,
                       Drot,
                       randomSeed=-1):
    '''
    :param numparticles: number of particles in list
    :param boxsize: size of simulation box, if scalar it assumes the three box edges are the same in all dimensions
    :param separationDistance: separation distance between particle to avoid overlapping
    :param D and Drot: diffusion coefficients of particles.
    :param randomSeed seed for python random generator. Important to specify in parallel runs. Default value of -1
    will use the default seed.
    :return:
    Generates particle list with uniformly random position and orientation. It also enforces the particles don't
    overlap over a distance given by the relative distance cut off.
    '''

    # Set numpy seed if provided (important when running parallel processes, otherwise not required)
    if randomSeed != -1:
        random.seed(randomSeed)  #better than np.random for parallel seeding

    # Warning in case of not ideal parameters.
    if separationDistance > boxsize:
        warnings.warn(
            "The separation distance between particles should be much smaller than the "
            "boxsize. Otherwise risk of infinite loop.")

    # Transform boxsize to vector if neccesarry.
    if np.isscalar(boxsize):
        boxsize = np.array([boxsize, boxsize, boxsize])

    # Create non-overlapping particle list
    pyPartlist = []
    for i in range(numparticles):
        overlap = True
        numTrials = 0
        while overlap:
            numTrials = numTrials + 1
            position = np.array([
                boxsize[0] * random.random() - 0.5 * boxsize[0],
                boxsize[1] * random.random() - 0.5 * boxsize[1],
                boxsize[2] * random.random() - 0.5 * boxsize[2]
            ])
            overlap = False
            for j in range(len(pyPartlist)):
                if np.linalg.norm(position -
                                  pyPartlist[j].position) < separationDistance:
                    overlap = True
                    continue
            if numTrials >= 100000:
                warnings.warn(
                    "Cannot easily set nonoverlapping particles with current setup. Check boxsize, "
                    "number of particles and separation distance.")

        orientation = np.array([
            random.random(),
            random.random(),
            random.random(),
            random.random()
        ])
        orientation = orientation / np.linalg.norm(orientation)
        part = msmrd2.particle(D, Drot, position, orientation)
        part.setID(i)
        pyPartlist.append(part)

    partlist = msmrd2.integrators.particleList(pyPartlist)
    return partlist
Exemple #4
0
def randomParticleMSList(numparticles,
                         boxsize,
                         separationDistance,
                         types,
                         unboundMSMs,
                         randomSeed=-1):
    '''
    :param numparticles: number of particles in list
    :param boxsize: size of simulation box, if scalar it assumes the three box edges are the same in all dimensions
    :param separationDistance: separation distance between particle to avoid overlapping
    :param types types of particles (can be array of integers or integer if all particles are the same)
    :param unboundMSMs: list of unboundMSM, needed to extract diffusion coefficients of particles.
    :param randomSeed seed for pyton random generator. Important to specify in parallel runs. Default value of -1
    will use the default seed.
    :return:
    Generates list of particles (with Markovian switch) with uniformly random position and orientation. It
    also enforces the particles don't overlap over a distance given by the relative distance cut off.
    '''

    # Set numpy seed if provided (important when running parallel processes, otherwise not required)
    if randomSeed != -1:
        random.seed(randomSeed)

    # Warning in case of not ideal parameters.
    if separationDistance > boxsize:
        warnings.warn(
            "The separation distance between particles should be much smaller than the "
            "boxsize. Otherwise risk of infinite loop.")

    # Transform boxsize, diffusion coefficients, type and state to vectors if neccesarry.
    if np.isscalar(boxsize):
        boxsize = np.array([boxsize, boxsize, boxsize])
    if np.isscalar(types):
        newtypes = np.ones(numparticles, dtype='int')
        types = (types * newtypes).astype(int)

    # Obtain initial state randomly from all available states
    states = np.zeros(numparticles, dtype='int')
    for i in range(numparticles):
        maxstates = len(unboundMSMs[types[i]].D)
        states[i] = random.randint(0, maxstates - 1)

    # Create non-overlapping particle list
    pyPartlist = []
    for i in range(numparticles):
        numTrials = 0
        overlap = True
        while overlap:
            numTrials = numTrials + 1
            position = np.array([
                boxsize[0] * random.random() - 0.5 * boxsize[0],
                boxsize[1] * random.random() - 0.5 * boxsize[1],
                boxsize[2] * random.random() - 0.5 * boxsize[2]
            ])
            overlap = False
            for j in range(len(pyPartlist)):
                if np.linalg.norm(position -
                                  pyPartlist[j].position) < separationDistance:
                    overlap = True
                    continue
            if numTrials >= 100000:
                warnings.warn(
                    "Cannot easily set nonoverlapping particles with current setup. Check boxsize, "
                    "number of particles and separation distance.")

        orientation = np.array([
            random.random(),
            random.random(),
            random.random(),
            random.random()
        ])
        orientation = orientation / np.linalg.norm(orientation)
        D = unboundMSMs[types[i]].D[states[i]]
        Drot = unboundMSMs[types[i]].Drot[states[i]]
        part = msmrd2.particle(types[i], states[i], D, Drot, position,
                               orientation)
        part.setID(i)
        # Deactivate inner unbound MSM if there is only one state.
        if len(unboundMSMs[types[i]].D) == 1:
            part.deactivateMSM()

        pyPartlist.append(part)

    partlist = msmrd2.integrators.particleList(pyPartlist)
    return partlist
def calculateDiffusionCoefficeints(numparticles):
    '''
    :param numparticles: must have a value between 1 and 5
    :return: translational diffusion coefficeint and rotational diffusion coefficient
    '''
    simulationSuccess = False

    # Define particle list with between two and five particles to estimate the diffusion
    # of dimers/ trimers / cuatrimers? and pentamer
    pyPartlist = []

    # Position List for pentamer IC
    for i in range(numparticles):
        position = 0.85 * np.array([np.cos(th * i), np.sin(th * i), 0.0])
        orientation = np.array([
            np.cos(0.5 * (thextra - th * i)), 0, 0,
            np.sin(0.5 * (thextra - th * i))
        ])
        part = msmrd2.particle(D, Drot, position, quats.conjugate(orientation))
        pyPartlist.append(part)

    # Create list of particles that can be read from msmrd
    # Note the particles in this list will be independent from the python list.
    partlist = msmrd2.integrators.particleList(pyPartlist)

    # Over-damped Langevin integrator definition
    #0.000005
    seed = -1  #1 #-1 # Negative seed, uses random device as seed
    bodytype = 'rigidbody'
    integrator = odLangevin(dt, seed, bodytype)

    # Define Patchy Particle potential
    if numparticles > 1:
        potentialPatchyParticleAngular2 = patchyParticleAngular2(
            sigma, strength, angularStrength, patchesCoordinates)
        integrator.setPairPotential(potentialPatchyParticleAngular2)

    # Define arrays to calculate autocorrelation functions
    pentamerPositionArray = []
    pentamerOrientationArray = []

    #Integrate particle list and print only positions
    cross = [None] * 2
    if outputVMD:
        datafile = open('../../data/vmd/pentamerTest.xyz', 'w')
    for i in range(timesteps):
        if i % stride == 0:
            if outputVMD:
                datafile.write(str(3 * len(partlist) + 3) + '\n')
                datafile.write(str(0) + '\n')
            if numparticles == 1:
                pentamerPositionArray.append(partlist[0].position)
                pentamerOrientationArray.append(partlist[0].orientation)
            else:
                # Calculate and plot center of pentamer position
                relpos = partlist[1].position - partlist[0].position
                relposnorm = np.linalg.norm(relpos)
                if (relposnorm >= 1.5):
                    return simulationSuccess, 0, 0
                relpos = relpos / relposnorm
                patch1 = 0.5 * sigma * quats.rotateVec(patchesCoordinates[0],
                                                       partlist[0].orientation)
                patch2 = 0.5 * sigma * quats.rotateVec(patchesCoordinates[1],
                                                       partlist[0].orientation)
                cross[0] = np.cross(relpos, patch1)
                cross[1] = np.cross(relpos, patch2)
                maxIndex = np.argmax(np.linalg.norm(cross, axis=1))
                rotAxis = cross[maxIndex] / np.linalg.norm(cross[maxIndex])
                rotation = 3 * np.pi * rotAxis / 10.0
                quatRotation = quats.angle2quat(rotation)
                pentamerCenter = 0.85 * quats.rotateVec(relpos, quatRotation)
                pentamerCenter = pentamerCenter + partlist[0].position
                pentamerPositionArray.append(pentamerCenter)
                # Calculate and plot orientation of pentamer (using only the orientation of particle 0)
                orientation0 = np.array(
                    [np.cos(0.5 * (thextra)), 0, 0,
                     np.sin(0.5 * (thextra))])
                vec1 = 0.85 * np.array(
                    [1., 0., 0.]) + 0.5 * sigma * quats.rotateVec(
                        patchesCoordinates[0], orientation0)
                vec2 = 0.85 * np.array(
                    [1., 0., 0.]) + 0.5 * sigma * quats.rotateVec(
                        patchesCoordinates[1], orientation0)
                rotVec1 = partlist[0].position + 0.5 * sigma * quats.rotateVec(
                    patchesCoordinates[0], partlist[0].orientation)
                rotVec2 = partlist[0].position + 0.5 * sigma * quats.rotateVec(
                    patchesCoordinates[1], partlist[0].orientation)
                pentamerOrientation = quats.recoverRotationFromVectors(np.array([0.,0.,0.]), vec1, vec2, \
                                                                       pentamerCenter, rotVec1, rotVec2)
                pentamerOrientationArray.append(pentamerOrientation)
                if outputVMD:
                    # Calculate cross reference
                    v0 = pentamerCenter
                    v1 = v0 + 0.25 * quats.rotateVec(refVec4,
                                                     pentamerOrientation)
                    v2 = v0 + 0.25 * quats.rotateVec(refVec5,
                                                     pentamerOrientation)
                    # Plot cross reference
                    datafile.write('type_2' + ' ' + ' '.join(map(str, v0)) +
                                   '\n')
                    datafile.write('type_3' + ' ' + ' '.join(map(str, v1)) +
                                   '\n')
                    datafile.write('type_3' + ' ' + ' '.join(map(str, v2)) +
                                   '\n')
        if outputVMD:
            for j, part in enumerate(partlist):
                if i % stride == 0:
                    v0 = part.position
                    v1 = v0 + 0.5 * sigma * quats.rotateVec(
                        patchesCoordinates[0], part.orientation)
                    v2 = v0 + 0.5 * sigma * quats.rotateVec(
                        patchesCoordinates[1], part.orientation)
                    datafile.write('type_0' + ' ' + ' '.join(map(str, v0)) +
                                   '\n')
                    datafile.write('type_1' + ' ' + ' '.join(map(str, v1)) +
                                   '\n')
                    datafile.write('type_1' + ' ' + ' '.join(map(str, v2)) +
                                   '\n')
        integrator.integrate(partlist)
        if i % 10000 == 0:
            print("Percentage complete: ", 100 * i / timesteps, "%", end="\r")
    if outputVMD:
        datafile.close()
        # Generate TCL script to visualize with VMD
        msmrdvis.generateTCL_pentamerTest(
            numparticles=numparticles,
            outfname='pentamerTest',
            tclfname='../../data/vmd/pentamerTest_2vmd.tcl')
    print("Simulation complete: ", 100, " % w/", numparticles, " particles")

    # Calculate auto-correlation/mean-square displacement for several lagtimes
    tsteps = len(pentamerPositionArray)
    lagtimesIndexes = np.arange(100, 2000, 100)  #np.arange(1,20,1)
    lagtimes = np.zeros(len(lagtimesIndexes) + 1)
    MSD_3D = np.zeros(len(lagtimesIndexes) + 1)
    MSD_3D_orientation = np.zeros(len(lagtimesIndexes) + 1)
    for i, lagtimeIndex in enumerate(lagtimesIndexes):
        lagtimes[i + 1] = dt * lagtimeIndex * stride
        MSD = 0.0
        MSD_orientation = 0.0
        for j in range(tsteps - lagtimeIndex):
            dr = pentamerPositionArray[j +
                                       lagtimeIndex] - pentamerPositionArray[j]
            dq = pentamerOrientationArray[
                j + lagtimeIndex] - pentamerOrientationArray[j]
            MSD += dr * dr
            MSD_orientation += dq * dq
        MSD = MSD / (tsteps - lagtimeIndex + 1)
        MSD_3D[i + 1] = sum(MSD)  # D = sum(MSD)/(6*lagtime)
        MSD_orientation = MSD_orientation / (tsteps - lagtimeIndex + 1)
        MSD_3D_orientation[i + 1] = sum(MSD_orientation[1:])

        # Least square approximation with numpy
        A = np.vstack([lagtimes, np.ones(len(lagtimes))]).T
        slope, b = np.linalg.lstsq(A, MSD_3D / 6, rcond=None)[0]
        #print(slope,b)
        # Least square approximation with numpy (rotation)
        y = -np.log(1 - 4 * MSD_3D_orientation / 3.0) / 2
        slope2, b2 = np.linalg.lstsq(A, y, rcond=None)[0]
        #print(slope2,b2)
    print("Estimated diffusion coefficients")

    # Plot MSD against lagtime and fits
    plt.plot(lagtimes, MSD_3D / 6, 'o', label='position')
    plt.plot(lagtimes,
             -np.log(1 - 4 * MSD_3D_orientation / 3.0) / 2,
             'o',
             label='orientation')
    plt.plot(lagtimes, slope * lagtimes + b, '-', label='position fit')
    plt.plot(lagtimes, slope2 * lagtimes + b2, '-', label='orientation fit')
    plt.legend()
    plt.savefig(Diffdirectory + 'diffusionFitPentamer_' + str(numparticles) +
                'particles.pdf',
                bbox_inches='tight')
    plt.close()
    print("Finished plots")

    Dapprox = slope
    DrotApprox = slope2
    simulationSuccess = True
    return simulationSuccess, Dapprox, DrotApprox
Exemple #6
0
import msmrd2 as m
import numpy as np

assert m.__version__ == '0.0.1'
assert m.add(1, 2) == 3
assert m.subtract(1, 2) == -1

particles = []
for i in range(10):
    particles.append(
        m.particle(i, 1, 2., 3., np.random.rand(4), np.random.rand(3)))

sim = m.simulation(particles)
sim.run(0.1, 100)
Exemple #7
0
def generateParticleList(state, boxsize, types, couplingMSM, randomSeed=-1):
    '''
    Generate a random particle list of two particles corresponding to either state A or state B. As each
    state has several different configurations, this functions picks one randomly. The definition of the
    states is taken form patchyDimer.cpp
    :param state: bound state in which the two particle list should be in, A or B
    :param boxsize: size of simulation box, if scalar it assumes the three box edges are the same in all dimensions
    :param couplingMSM: MSM for MSM/RD, needed to extract diffusion coefficients of particles.
    :param randomSeed: seed for python random generator. Important to specify in parallel runs. Default value of -1
    will use the default seed.
    :return: random particle list corresponding to either state A or state B.
    '''

    # Transform boxsize and type to vector if neccesary.
    boxsize = boxsize - 1  # to ensure the whole compund is initially inside box (1 is the norm of relpos below)
    if np.isscalar(boxsize):
        boxsize = np.array([boxsize, boxsize, boxsize])

    if np.isscalar(types):
        newtypes = np.ones(2, dtype='int')
        types = (types * newtypes).astype(int)

    position1 = np.array([0.0, 0.0, 0.0])
    #position1 = np.array([boxsize[0]*random.random()-0.5*boxsize[0],
    #                    boxsize[1]*random.random()-0.5*boxsize[1],
    #                     boxsize[2]*random.random()-0.5*boxsize[2]])
    orientation1 = np.array([1.0, 0.0, 0.0, 0.0])
    # Define relative position
    relpos1 = np.array([np.cos(angleDiff / 2.0), np.sin(angleDiff / 2.0), 0])
    relpos2 = np.array(
        [np.cos(angleDiff / 2.0),
         np.sin(-1 * angleDiff / 2.0), 0])
    relPos1orthogonal = np.array(
        [-1.0 * np.sin(angleDiff / 2.0),
         np.cos(angleDiff / 2.0), 0.0])
    relPos2orthogonal = np.array(
        [np.sin(angleDiff / 2.0),
         np.cos(angleDiff / 2.0), 0.0])
    # Define relative rotations
    rotations = [None] * 8
    rotations[0] = np.pi * relPos1orthogonal
    rotations[1] = np.array([0.0, 0.0, -2 * np.pi / 5.0])
    rotations[2] = np.array([0.0, 0.0, np.pi])
    rotations[3] = np.array([0.0, np.pi, 0.0])
    # --first 4 rotations correspond to binding on top patch of particle 1, next 4 rotations to bottom patch
    rotations[4] = np.pi * relPos2orthogonal
    rotations[5] = np.array([0.0, 0.0, 2 * np.pi / 5.0])
    rotations[6] = np.array([0.0, 0.0, np.pi])
    rotations[7] = np.array([0.0, np.pi, 0.0])
    # Convert axis-angle rotations to quaternions
    quatRotations = [None] * 8
    for i in range(8):
        quatRotations[i] = quaternions.angle2quat(rotations[i])
    # Assign correct position2 depending on the states
    pos2 = [None] * 8
    pos2[0:4] = [position1 + relpos1] * 4
    pos2[4:8] = [position1 + relpos2] * 4

    # Assign position and orientation depending on initial state A or B
    substate = 0
    if state == 'A':
        substate = random.choice([1, 2, 5, 6])
        position2 = pos2[substate - 1]
        orientation2 = quatRotations[substate - 1]
    if state == 'B':
        substate = random.choice([3, 4, 7, 8])
        position2 = pos2[substate - 1]
        orientation2 = quatRotations[substate - 1]
    # Obtain diffusion coefficients from unboundMSM
    Dbound = couplingMSM.D[substate - 1]
    DrotBound = couplingMSM.Drot[substate - 1]
    # Define particles
    part1 = msmrd2.particle(types[0], -1, Dbound, DrotBound, position1,
                            orientation1)
    part2 = msmrd2.particle(types[1], -1, 0., 0., position2, orientation2)
    # Set up bound particles as if done by the code (deactivate one of them)
    part1.setBoundState(substate)
    part2.setBoundState(substate)
    part1.setBoundTo(1)
    part2.setBoundTo(0)
    part1.deactivateMSM()
    part2.deactivateMSM()
    part2.deactivate()
    part2.setPosition([10000000.0, 10000000.0, 10000000.0])
    partlist = msmrd2.integrators.particleList([part1, part2])
    return partlist