Example #1
0
    def load_jmol_conf(self, file_name, number_of_chains=1):
        """
        Load chain configuration from JMOL file

        @string file_name: Name of jmol file
        @dict map_dict: dict with Monomer class
        @int number_of_chains: number of chains in jmol file

        """

        f = open(file_name, "r+")
        data = f.readlines()

        header = int(data[0])
        coordinate_data = map(lambda x: x.split(" "), data[2:])

        chains = []
        for chain_idx in range(number_of_chains):
            chain_seq = []
            slice_coordinate = coordinate_data[chain_idx * header:]
            self.chain_list[chain_idx].positions_list = []
            for c_idx in range(len(slice_coordinate)):
                c = slice_coordinate[c_idx]
                crr = tuple(map(lambda x: float(x)/settings.JMOL_SCALE, tuple(c[1:])))
                print "Load", crr
                self.chain_list[chain_idx].chain[c_idx]._open = crr
                self.chain_list[chain_idx].chain[c_idx].position = Lattice.convert_to_periodic_coordinate(crr)
                self.chain_list[chain_idx].positions_list.append(self.chain_list[chain_idx].chain[c_idx]._position)


            self.chain_list[chain_idx] = self.rebuild_direction(self.chain_list[chain_idx])
Example #2
0
    def begin_rotate(self, monomer_idx = None):
        """
        rotate with begin
        """
        random_direction = randrange(0, Lattice.z)
        #new_position = Lattice.get_coordinate(self.chain[1]._position, random_direction)
        new_position = self.chain[1].neighbours_position[random_direction]
        if self.box.is_occupied(new_position) or new_position in self.positions_list:
            #logging.info("Reject rotate; position: (%d, %d, %d) is occupied" % (new_position))
            return False
        
        new_monomer = self.chain[0]

        old_position = self.chain[0]._position
        old_open_position = self.chain[0]._open
        old_next_direction = self.chain[0].next_direction
        old_energy = self.total_energy

        self.chain[0].position = new_position
        self.chain[0]._open = Lattice.get_open_coordinate(self.chain[1]._open, random_direction)
        self.chain[0].next_direction = Lattice.TURN_DIRECTION[random_direction]
        self.positions_list[0] = new_position
        
        perform_move = True
        new_energy = None
        
        if self.box.athermal_state == False:
        
            new_energy = self.box.calculate_chain_energy(self)
        
            dE = new_energy - old_energy
        
            if self.box.accept(dE, None):
                perform_move = True
            else:
                perform_move = False

        if perform_move:
            if not new_energy:
                new_energy = self.box.calculate_chain_energy(self)
            self.total_energy = new_energy
        else:
            new_monomer.position = old_position
            new_monomer._open = old_open_position
            new_monomer.next_direction = old_next_direction
            self.chain[0] = new_monomer
            self.positions_list[0] = old_position
            # restore old energy
            self.total_energy = old_energy

        return perform_move
Example #3
0
def create_valid_lists(list):
    point_monomer = {}
    monomer_list = []
    position_set = set()

    for line in list:
        type = line[0]
        ll = np.array(line[1:]) * scale

        position = tuple(ll)

        m = DummyMonomer(position, type, Lattice.get_neighbours(tuple(position)))

        point_monomer[position] = m
        monomer_list.append(m)
        position_set.add(position)

    return point_monomer, monomer_list, position_set
Example #4
0
 def get_direction(self, monomer_b, monomer_a):
     """
     Return direction between two monomers, use to avoid some moves
     End point: b
     Start point: a
     """
     
     if not isinstance(monomer_a, Monomer):
         monomer_a = self[monomer_a]
     if not isinstance(monomer_b, Monomer):
         monomer_b = self[monomer_b]
     
     vector = np.array(monomer_b._open) - np.array(monomer_a._open)
     
     try:
         direction = Lattice.get_direction(tuple(vector))
     except Exception, e:
         #logging.debug("get_direction for (%d, %d, %d) - (%d, %d, %d): (%d, %d, %d)" % (monomer_b._open, monomer_a._open, vector))
         raise e
Example #5
0
def process(file, step=None, output_file=None):

    if isinstance(file, str):
        f = open(file)
    else:
        f = file
    try:
        line = f.readlines()
        nr = int(line[0].replace("\n", ""))
    except:
        print line
        sys.exit(1)

    data = np.loadtxt(file, dtype=str, skiprows=1)

    # data = f.readlines()[1:]

    point_monomer = {}
    monomer_list = []
    position_set = set()

    if parser.scale == -1 and "." in data[0]:
        print "Atom position are float, please set -scale parameter to rescale them!"
        sys.exit(1)

    for line in data:
        # tmp = line.replace("\n","").split(" ")
        type = line[0]
        ll = map(float, line[1:])

        x = int(ll[0] * scale)
        y = int(ll[1] * scale)
        z = int(ll[2] * scale)

        position = (x, y, z)

        m = DummyMonomer(position, type, Lattice.get_neighbours(tuple(position)))

        point_monomer[position] = m
        monomer_list.append(m)
        position_set.add(position)

    return point_monomer, monomer_list, position_set
Example #6
0
    def pull_move(self, monomer_idx = None):
        """
        Pull move, N. Lesh, M. Mitzemacher, S. Whitesides
        """
        ## flags
        valid_configuration = False
        if settings.DEBUG: self.valid_chain()
        #if monomer_idx is None:
        monomer_idx = randrange(0, self._total_length - 1)
        ## two special cases at the begin/end
        case = 3
        old_energy = self.total_energy
        
        is_occupied = self.box.is_occupied
        is_neighbour = self.box.lattice.is_neighbour

        ##  trials with optimization
        last_affected_idx = 0

        # case
        # 0 - left
        # 1 - right
        # 3 - inside
        #  0 - right
        #  1 - left
        
        #mod_pos = 0
            
        if monomer_idx == 0: ## left
            dir_1 = randrange(0, Lattice.z)
            dir_2 = randrange(0, Lattice.z)
            pos_1 = self.chain[0].neighbours_position[dir_1]
            pos_2 = self.box.lattice.get_coordinate(pos_1, dir_2)
            
            if is_occupied(pos_1) or is_occupied(pos_2):
                return False

            #if not is_neighbour(pos_1, pos_2):
            #    return False

            m1 = self.chain[0]
            m2 = self.chain[1]
           
            open_pos_1 = self.box.lattice.get_open_coordinate(m1._open, dir_1)
            open_pos_2 = self.box.lattice.get_open_coordinate(open_pos_1, dir_2)

            old_position_list = self.positions_list[:]
            #old_monomer_position = [ m._position for m in self.chain ]
            old_monomer_position = old_position_list
            old_monomer_open_position = [ m._open for m in self.chain ]
            old_monomer_next_direction = [ m.next_direction for m in self.chain ]

            m1.position = pos_2
            m1._open = open_pos_2

            m2.position = pos_1
            m2._open = open_pos_1
            
            ## old energy
        
            #self[0] = m1
            m1.chain = self
            self.chain[0] = m1
            self.positions_list[0] = m1._position

            #self[1] = m2
            m2.chain = self
            self.chain[1] = m2
            self.positions_list[1] = m2._position

            #mod_pos = 2
    
            for idx in xrange(2, self._total_length):
                i_monomer = self.chain[idx]
                p_monomer = self.chain[idx - 1]

                diff = np.array(i_monomer._open) - np.array(p_monomer._open)
                diff = diff.dot(diff)

                if diff == 2:
                    p_monomer.next_direction = p_monomer.neighbours_position.index(i_monomer._position)
                    break
                
                #mod_pos += 1

                i_monomer.position = old_monomer_position[idx - 2]
                i_monomer._open = old_monomer_open_position[idx - 2]
                i_monomer.next_direction = old_monomer_next_direction[idx - 2]
                #self[idx] = i_monomer
                i_monomer.chain = self
                self.chain[idx] = i_monomer
                self.positions_list[idx] = i_monomer._position

                last_affected_idx = idx

            
            self.chain[0].next_direction = self.chain[0].neighbours_position.index(self.chain[1]._position)
            self.chain[1].next_direction = self.chain[1].neighbours_position.index(self.chain[2]._position)
            case = 0
            if settings.DEBUG: self.valid_chain()
        elif monomer_idx == self._total_length - 1: ## right
            dir_1 = randrange(0, Lattice.z)
            dir_2 = randrange(0, Lattice.z)
            pos_1 = self.chain[-1].neighbours_position[dir_1]
            pos_2 = self.box.lattice.get_coordinate(pos_1, dir_2)

            if is_occupied(pos_1) or is_occupied(pos_2):
                return False

            open_pos_1 = self.box.lattice.get_open_coordinate(self.chain[-1]._open, dir_1)
            open_pos_2 = self.box.lattice.get_open_coordinate(open_pos_1, dir_2)

            old_position_list = self.positions_list[:]
            #old_monomer_position = [ m._position for m in self.chain ]
            old_monomer_position = old_position_list
            old_monomer_open_position = [ m._open for m in self.chain ]
            old_monomer_next_direction = [ m.next_direction for m in self.chain ]
            
            m1 = self.chain[-1]
            m2 = self.chain[-2]

            m1.position = pos_2
            m1._open = open_pos_2

            m2.position = pos_1
            m2._open = open_pos_1

            #mod_pos = 2

            for idx in xrange(self._total_length - 2, -1, -1):
                i_monomer = self.chain[idx]
                n_monomer = self.chain[idx+1]

                diff = np.array(i_monomer._open) - np.array(n_monomer._open)
                diff = diff.dot(diff)

                if diff == 2:
                    i_monomer.next_direction = i_monomer.neighbours_position.index(n_monomer._position)
                    break
                
                #mod_pos += 1

                i_monomer.position = old_monomer_position[idx + 2]
                i_monomer._open = old_monomer_open_position[idx + 2]
                i_monomer.next_direction = old_monomer_next_direction[idx + 2]

                #self[idx] = i_monomer
                i_monomer.chain = self
                self.chain[idx] = i_monomer
                self.positions_list[idx] = i_monomer._position

                last_affected_idx = idx
            
            case = 1

            self.chain[-2].next_direction = self.chain[-2].neighbours_position.index(self.chain[-1]._position) 
            self.chain[-1].next_direction = 0

            if settings.DEBUG: self.valid_chain()

        if case == 3: 
            prev_monomer = self.chain[monomer_idx - 1]
            monomer = self.chain[monomer_idx]
            next_monomer = self.chain[monomer_idx + 1]

            random_neighbour_prev = randrange(0, Lattice.z)
            #if self.box.is_occupied(prev_monomer.neighbours_position[random_neighbour_prev]):
            #    return False

            #L can be adjenct to i-1 or i+1, randomly choose
            sub_case = randrange(0, 2) ## 0 - right, 1 - left
            
            if sub_case == 0:
                L_position = next_monomer.neighbours_position[random_neighbour_prev]
                L_open_position = Lattice.get_open_coordinate(next_monomer._open, random_neighbour_prev)
                Lnext_direction = self.box.lattice.get_neighbours(L_position).index(next_monomer._position)
            elif sub_case == 1:
                L_position = prev_monomer.neighbours_position[random_neighbour_prev]
                L_open_position = Lattice.get_open_coordinate(prev_monomer._open, random_neighbour_prev)
                Lprev_direction = self.box.lattice.get_neighbours(L_position).index(prev_monomer._position)

            C_position = monomer.neighbours_position[random_neighbour_prev]
            C_open_position = Lattice.get_open_coordinate(monomer._open, random_neighbour_prev)

            diff = np.array(L_position) - np.array(C_position)
            diff = diff.dot(diff)

            if diff != 2:
                return False

            if is_occupied(L_position):
                return False

            if is_occupied(C_position):
                if (sub_case == 0 and  C_position != prev_monomer._position) or (sub_case == 1 and C_position != next_monomer._position):
                    return False


            # old_energy
            #old_energy = self.box.calculate_chain_energy(self)
            
            # direction L->C
            C_nb = self.box.lattice.get_neighbours(C_position)
            CL_direction = C_nb.index(L_position)
            LC_direction = self.box.lattice.TURN_DIRECTION[CL_direction]

            # save old position list
            old_position_list = self.positions_list[:]
            #old_monomer_position = [ m._position for m in self.chain ]
            old_monomer_position = old_position_list
            old_monomer_open_position = [ m._open for m in self.chain ]
            old_monomer_next_direction = [ m.next_direction for m in self.chain ]

            if (sub_case == 0 and prev_monomer._position == C_position) or \
                (sub_case == 1 and next_monomer._position == C_position):
                valid_configuration = True
            

            monomer.position = L_position
            monomer._open = L_open_position
            if sub_case == 0:
                monomer.next_direction = Lnext_direction
            elif sub_case == 1:
                monomer.next_direction = LC_direction

            #self[monomer_idx] = monomer
            monomer.chain = self
            self.chain[monomer_idx] = monomer
            self.positions_list[monomer_idx] = monomer._position

            if sub_case == 0:
                prev_monomer.position = C_position
                prev_monomer._open = C_open_position
                prev_monomer.next_direction = CL_direction
                #self[monomer_idx - 1] = prev_monomer
                prev_monomer.chain = self
                self.chain[monomer_idx - 1] = prev_monomer
                self.positions_list[monomer_idx - 1] = prev_monomer._position

            elif sub_case == 1:
                if not valid_configuration:
                   next_monomer.position = C_position
                   next_monomer._open = C_open_position
                   next_monomer.next_direction = self.box.lattice.TURN_DIRECTION[random_neighbour_prev]
                   #self[monomer_idx + 1] = next_monomer
                   next_monomer.chain = self
                   self.chain[monomer_idx + 1] = next_monomer
                   self.positions_list[monomer_idx + 1] = next_monomer._position

                prev_monomer.next_direction = random_neighbour_prev
                #self[monomer_idx - 1] = prev_monomer
                prev_monomer.chain = self
                self.chain[monomer_idx - 1] = prev_monomer
                self.positions_list[monomer_idx - 1] = prev_monomer._position
            
            #mod_pos = 2

            if not valid_configuration:
                if sub_case == 0: ## right
                    for j in xrange(monomer_idx - 2, -1, -1):
                        j_monomer = self.chain[j]
                        i_monomer = self.chain[j+1]

                        diff = np.array(j_monomer._open) - np.array(i_monomer._open)
                        diff = diff.dot(diff)

                        if diff == 2:
                            j_monomer.next_direction = j_monomer.neighbours_position.index(i_monomer._position)
                            break

                        #mod_pos += 1

                        j_monomer.position = old_monomer_position[j+2]
                        j_monomer._open = old_monomer_open_position[j+2]
                        j_monomer.next_direction = j_monomer.neighbours_position.index(i_monomer._position)
                        #self[j] = j_monomer
                        j_monomer.chain = self
                        self.chain[j] = j_monomer
                        self.positions_list[j] = j_monomer._position

                elif sub_case == 1:
                    for j in xrange(monomer_idx + 2, self._total_length):
                        j_monomer = self.chain[j]
                        i_monomer = self.chain[j-1]

                        diff = np.array(j_monomer._open) - np.array(i_monomer._open)
                        diff = diff.dot(diff)

                        if diff == 2:
                            i_monomer.next_direction = i_monomer.neighbours_position.index(j_monomer._position)
                            break
                        
                        #mod_pos += 1

                        j_monomer.position = old_monomer_position[j-2]
                        j_monomer._open = old_monomer_open_position[j-2]
                        i_monomer.next_direction = i_monomer.neighbours_position.index(j_monomer._position)
                        #self[j] = j_monomer
                        j_monomer.chain = self
                        self.chain[j] = j_monomer
                        self.positions_list[j] = j_monomer._position
                
            if settings.DEBUG: self.valid_chain()
        
        ## end move procedure
        # old_energy, emergency case
        new_energy = self.box.calculate_chain_energy(self)
        dE = new_energy - old_energy

        accept_move = False

        if dE <= 0.0 or self.box.athermal_state:
            accept_move = True
        else:
            accept_move = self.box.accept(dE, None)
        
        if accept_move is True:
            self.total_energy = new_energy
            #self.positions_list = [ m._position for m in self.chain ]

            self.box.global_energy_update([self.idx])
            
            #f = open("mod_pos_%d" % self._total_length, "w+")
            #f.write("%d\n" % mod_pos)
            #f.close()

            #self.valid_chain()
        else: ## revert
            if case == 3:
                if sub_case == 0:
                    monomer_list = xrange(0, monomer_idx+2)
                elif sub_case == 1:
                    monomer_list = xrange(monomer_idx-1, self._total_length)
            elif case == 0:
                monomer_list = xrange(0, self._total_length)
            elif case == 1: ##
                monomer_list = xrange(0, self._total_length)
            #monomer_list = changed_monomer_idx
            for idx in monomer_list:
                m = self.chain[idx]
                old_pos = old_monomer_position[idx]
                old_dir = old_monomer_next_direction[idx]

                if m._position != old_pos or m.next_direction != old_dir:
                    m.position = old_monomer_position[idx]
                    m._open = old_monomer_open_position[idx]
                    m.next_direction = old_monomer_next_direction[idx]
                    #self[idx] = m
                    m.chain = self
                    self.chain[idx] = m
                    self.positions_list[idx] = m._position

            #self.positions_list = old_position_list[:]
            self.total_energy = old_energy
            #self.valid_chain()

        return accept_move
Example #7
0
    def _snake(self, move_type):
        """
        Snake move
        """
        
        #direction_to_choice = range(0, Lattice.z)
        if settings.DEBUG: self.valid_chain()    
        if move_type == 0:
            monomer = self.chain[0]
        elif move_type == 1:
            monomer = self.chain[-1]
            
        #random new position
        random_direction = randrange(0, Lattice.z)
        new_position = monomer.neighbours_position[random_direction]
        open_position = Lattice.get_open_coordinate(monomer._open, random_direction)

        if self.box.is_occupied(new_position) or new_position in self.positions_list:
            return False
        
        # copy
        old_position_list = self.positions_list[:]
        old_open_position_list = [ x._open for x in self.chain ]
        old_next_direction_list = [ x.next_direction for x in self.chain ]
        old_energy = self.total_energy
        
        self.positions_list = []
        
        """
        Update position of monomers in chain (copy)
        """
        if move_type == 0: # left
            self.positions_list = [new_position]
            
            for idx in range(1, self._total_length):
                self.chain[idx].position = old_position_list[idx - 1]
                self.chain[idx]._open = old_open_position_list[idx - 1]
                self.chain[idx].next_direction = old_next_direction_list[idx - 1]
            
            self.positions_list += old_position_list[0: self._total_length - 1]
            self.chain[0].position = new_position
            self.chain[0]._open = open_position
            self.chain[0].next_direction = self.box.lattice.TURN_DIRECTION[random_direction]
            
        elif move_type == 1: # right
            for idx in range(0, self._total_length - 1):
                self.chain[idx].position = old_position_list[idx + 1]
                self.chain[idx]._open = old_open_position_list[idx + 1]
                self.chain[idx].next_direction = old_next_direction_list[idx + 1]
                
            self.chain[-1].position = new_position
            self.chain[-1]._open = open_position
            self.chain[-2].next_direction = random_direction
            
            self.positions_list = old_position_list[1:] + [new_position]
       

        new_energy = self.box.calculate_chain_energy(self)
        
        dE = new_energy - old_energy
         
        if dE <= 0.0 or self.box.athermal_state:
            if settings.DEBUG: self.valid_chain()
            self.total_energy = new_energy

            return True
        elif self.box.accept(dE, None):
            if settings.DEBUG: self.valid_chain()
            self.total_energy = new_energy
            return True
        else:
            for idx in xrange(self._total_length):
                self.chain[idx].position = old_position_list[idx]
                self.chain[idx]._open = old_open_position_list[idx]
                self.chain[idx].next_direction = old_next_direction_list[idx]
            
            self.positions_list = old_position_list[:]
            self.total_energy = old_energy
            
            if settings.DEBUG: self.valid_chain()
            
        return False
Example #8
0
        
        if number_of_positions == 0:
            return False
        
        new_position = list(common_neighbours_positions)[randrange(0, number_of_positions)]
        if not new_position or len(new_position) != 3:
            if __debug__: logging.debug("New_position %s:" % str(new_position))
            return False
        
        try:
            direction = prev_monomer.neighbours_position.index(new_position)
        except Exception, e:
            if __debug__: logging.error("Reject segment, error in direction, (%d, %d, %d)" % (new_position))
            return False
            
        open_position = Lattice.get_open_coordinate(prev_monomer._open, direction)
        
        if self.box.is_occupied(new_position):
            #logging.debug("Reject segment; Position: (%d, %d, %d) is occupied" % (new_position))
            return False
        
        # make copy of old monomer
        old_position = rotate_monomer._position
        old_open_position = rotate_monomer._open
        old_prev_next_direction = self.chain[monomer_idx - 1].next_direction
        old_next_direction = rotate_monomer.next_direction

        old_energy = self.total_energy

        self.chain[monomer_idx].position = new_position
        self.chain[monomer_idx]._open = open_position
Example #9
0
    def random_strategy(self, chain):
        """
        Put chain in random mode
        """
        random_point = tuple(map(lambda x: x/2, self.box_size))
        while self.is_occupied(random_point) or not Lattice.is_valid_coordinate(random_point):
            random_point = tuple([ random.randrange(0, self.box_size[x]) for x in range(3) ])

        if __debug__: logging.info("Chain start point: %d, %d, %d" % random_point)

        f_monomer = chain.chain[0]
        f_monomer.position = random_point
        f_monomer._open = random_point
        chain[0] = f_monomer

        for idx in range(1, chain._total_length):
            prev_monomer = chain.chain[idx - 1]
            monomer = chain.chain[idx]

            not_found = True
            direction_list = range(0, Lattice.z)
            direction_list_idx = Lattice.z
            while not_found:
                try:
                    direction_idx = random.randrange(0, direction_list_idx)
                except:
                    return False
                direction = direction_list[direction_idx]

                pos = Lattice.get_coordinate(prev_monomer._position, direction)
                open_pos = Lattice.get_open_coordinate(prev_monomer._open, direction)

                if pos in chain.positions_list or  \
                    (self.number_of_chains > 1 and self.is_occupied(pos)):

                    not_found = True
                    direction_list.remove(direction)
                    direction_list_idx -= 1
                else:
                    not_found = False

            monomer.position = pos
            monomer.open_position = open_pos
            #monomer.next_direction = None
            prev_monomer.next_monomer = monomer
            prev_monomer.next_direction = direction

            chain[idx] = monomer
            chain.chain[idx-1] = prev_monomer

        ## rebuild directions
        for idx in range(1, chain._total_length):
            p = chain.chain[idx - 1]
            m = chain.chain[idx]
            d = p.neighbours_position.index(m._position)
            p.next_direction = d
            chain.chain[idx - 1].next_direction = d

        chain.chain[-1].next_direction = 0

        if __debug__: logging.info('Chain inside box, length=%d' % chain.length)
        return chain
Example #10
0
    def greedy_strategy(self, chain):
        """
        Put chain in the box based on a greedy strategy
        """
        random_point = tuple(map(lambda x: x/2, self.box_size))
        while self.is_occupied(random_point) or not Lattice.is_valid_coordinate(random_point):
            random_point = tuple([ random.randrange(0, self.box_size[x]) for x in range(3) ])

        f_monomer0 = chain.chain[0]
        f_monomer0.position = random_point
        f_monomer0._open = random_point
        chain[0] = f_monomer0

        next_direction = random.randrange(0, Lattice.z)

        f_monomer1 = chain.chain[1]
        f_monomer1.position = Lattice.get_coordinate(random_point, next_direction)
        f_monomer1._open = Lattice.get_open_coordinate(random_point, next_direction)
        chain[1] = f_monomer1
        chain[0].next_direction = next_direction

        pos_list = [f_monomer0._position, f_monomer1._position]

        direction_list = range(0, Lattice.z)
        for idx in range(2, chain._total_length):
            prev_monomer = chain.chain[idx - 1]
            monomer = chain.chain[idx]
            min_energy = self.calculate_chain_energy(chain)
            min_pos = None
            min_open_pos = None
            min_dir = 0

            np.random.shuffle(direction_list)

            last_valid_pos = None
            last_valid_open_pos = None

            for dir in direction_list:
                pos = Lattice.get_coordinate(prev_monomer._position, dir)
                open_pos = Lattice.get_open_coordinate(prev_monomer._open, dir)
                if pos in chain.positions_list or \
                    (self.number_of_chains > 1 and self.is_occupied(pos)) or \
                    pos in pos_list:
                    pass
                else:
                    last_valid_pos = pos
                    last_valid_open_pos = open_pos
                    monomer.position = pos
                    monomer._open = open_pos
                    chain[idx] = monomer
                    m_e = self.calculate_chain_energy(chain, slice=[0, idx+1])
                    if min_energy > m_e:
                        min_energy = m_e
                        min_pos = pos
                        min_open_pos = open_pos
                        min_dir = dir

            monomer.position = min_pos if min_pos else last_valid_pos
            monomer._open = min_open_pos if min_open_pos else last_valid_open_pos
            chain[idx] = monomer

            prev_monomer.next_direction = min_dir
            chain[idx - 1] = prev_monomer

        ## rebuild directions
        for idx in range(1, chain._total_length):
            p = chain.chain[idx - 1]
            m = chain.chain[idx]
            d = p.neighbours_position.index(m._position)
            p.next_direction = d
            chain.chain[idx - 1].next_direction = d

        if __debug__: logging.info("Greedy strategy, chain with min energy: %f" % \
                self.calculate_chain_energy(chain))
        return chain