We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
`Bio.PDB.PDBIO.set_structure()` alters parent of chains, residues and atoms
Calling the `set_structure` method of the `Bio.PDB.PDBIO` class alters the parent of (non-`Structure`) entities in a non-obvious way. For example, this script:
```python
import Bio.PDB
pdbio = Bio.PDB.PDBIO()
parser = Bio.PDB.MMCIFParser()
structure = parser.get_structure("12as", "12as.cif")
for chain in structure[0]:
    old_id = chain.id
    chain.id = " "
    pdbio.set_structure(chain)
    chain.id = old_id
```
Gives this error from line 91 of `Bio/PDB/Entity.py`:
```
ValueError: Cannot change id from `B` to ` `. The id ` ` is already used for a sibling of this entity.
```
Removing the call to `set_structure` acts as I would expect: temporarily renaming the chain name, and then reverting it without problems.

When `set_structure` is called with an `Entity` other than a `Structure` object, it constructs a `Structure` and populates it with the entity using the `add` method, which calls `set_parent` on the entity. To me, this isn't very intuitive: I don't expect writing an object to have side effects that act on that object.

I think a good solution to this would be to call `copy()` in `set_structure` before `add`ing the entity to the new `Structure` object. That is, change `sb.structure.add(pdb_object)` to `sb.structure.add(pdb_object.copy())` in `Bio.PDB.PDBIO.set_structure`. I'm happy to make a pull request if people agree that this is an issue.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `Bio/PDB/PDBIO.py`
Content:
```
1 # Copyright (C) 2002, Thomas Hamelryck (thamelry@binf.ku.dk)
2 # This code is part of the Biopython distribution and governed by its
3 # license.  Please see the LICENSE file that should have been included
4 # as part of this package.
5 
6 """Output of PDB files."""
7 
8 from Bio._py3k import basestring
9 
10 from Bio.PDB.StructureBuilder import StructureBuilder  # To allow saving of chains, residues, etc..
11 from Bio.Data.IUPACData import atom_weights  # Allowed Elements
12 
13 
14 _ATOM_FORMAT_STRING = "%s%5i %-4s%c%3s %c%4i%c   %8.3f%8.3f%8.3f%s%6.2f      %4s%2s%2s\n"
15 
16 
17 class Select(object):
18     """Select everything for PDB output (for use as a base class).
19 
20     Default selection (everything) during writing - can be used as base class
21     to implement selective output. This selects which entities will be written out.
22     """
23 
24     def __repr__(self):
25         """Represent the output as a string for debugging."""
26         return "<Select all>"
27 
28     def accept_model(self, model):
29         """Overload this to reject models for output."""
30         return 1
31 
32     def accept_chain(self, chain):
33         """Overload this to reject chains for output."""
34         return 1
35 
36     def accept_residue(self, residue):
37         """Overload this to reject residues for output."""
38         return 1
39 
40     def accept_atom(self, atom):
41         """Overload this to reject atoms for output."""
42         return 1
43 
44 
45 class PDBIO(object):
46     """Write a Structure object (or a subset of a Structure object) as a PDB file.
47 
48     Examples
49     --------
50     >>> p=PDBParser()
51     >>> s=p.get_structure("1fat", "1fat.pdb")
52     >>> io=PDBIO()
53     >>> io.set_structure(s)
54     >>> io.save("out.pdb")
55 
56     """
57 
58     def __init__(self, use_model_flag=0):
59         """Create the PDBIO object.
60 
61         :param use_model_flag: if 1, force use of the MODEL record in output.
62         :type use_model_flag: int
63         """
64         self.use_model_flag = use_model_flag
65 
66     # private mathods
67 
68     def _get_atom_line(self, atom, hetfield, segid, atom_number, resname,
69                        resseq, icode, chain_id, charge="  "):
70         """Return an ATOM PDB string (PRIVATE)."""
71         if hetfield != " ":
72             record_type = "HETATM"
73         else:
74             record_type = "ATOM  "
75 
76         if atom.element:
77             element = atom.element.strip().upper()
78             if element.capitalize() not in atom_weights:
79                 raise ValueError("Unrecognised element %r" % atom.element)
80             element = element.rjust(2)
81         else:
82             element = "  "
83 
84         name = atom.get_fullname().strip()
85         # Pad atom name if:
86         #     - smaller than 4 characters
87         # AND - is not C, N, O, S, H, F, P, ..., one letter elements
88         # AND - first character is NOT numeric (funky hydrogen naming rules)
89         if len(name) < 4 and name[:1].isalpha() and len(element.strip()) < 2:
90             name = " " + name
91 
92         altloc = atom.get_altloc()
93         x, y, z = atom.get_coord()
94         bfactor = atom.get_bfactor()
95         occupancy = atom.get_occupancy()
96         try:
97             occupancy_str = "%6.2f" % occupancy
98         except TypeError:
99             if occupancy is None:
100                 occupancy_str = " " * 6
101                 import warnings
102                 from Bio import BiopythonWarning
103                 warnings.warn("Missing occupancy in atom %s written as blank" %
104                               repr(atom.get_full_id()), BiopythonWarning)
105             else:
106                 raise TypeError("Invalid occupancy %r in atom %r"
107                                 % (occupancy, atom.get_full_id()))
108 
109         args = (record_type, atom_number, name, altloc, resname, chain_id,
110                 resseq, icode, x, y, z, occupancy_str, bfactor, segid,
111                 element, charge)
112         return _ATOM_FORMAT_STRING % args
113 
114     # Public methods
115 
116     def set_structure(self, pdb_object):
117         """Check what the user is providing and build a structure."""
118         if pdb_object.level == "S":
119             structure = pdb_object
120         else:
121             sb = StructureBuilder()
122             sb.init_structure('pdb')
123             sb.init_seg(' ')
124             # Build parts as necessary
125             if pdb_object.level == "M":
126                 sb.structure.add(pdb_object)
127                 self.structure = sb.structure
128             else:
129                 sb.init_model(0)
130                 if pdb_object.level == "C":
131                     sb.structure[0].add(pdb_object)
132                 else:
133                     sb.init_chain('A')
134                     if pdb_object.level == "R":
135                         try:
136                             parent_id = pdb_object.parent.id
137                             sb.structure[0]['A'].id = parent_id
138                         except Exception:
139                             pass
140                         sb.structure[0]['A'].add(pdb_object)
141                     else:
142                         # Atom
143                         sb.init_residue('DUM', ' ', 1, ' ')
144                         try:
145                             parent_id = pdb_object.parent.parent.id
146                             sb.structure[0]['A'].id = parent_id
147                         except Exception:
148                             pass
149                         sb.structure[0]['A'].child_list[0].add(pdb_object)
150 
151             # Return structure
152             structure = sb.structure
153         self.structure = structure
154 
155     def save(self, file, select=Select(), write_end=True, preserve_atom_numbering=False):
156         """Save structure to a file.
157 
158         :param file: output file
159         :type file: string or filehandle
160 
161         :param select: selects which entities will be written.
162         :type select: object
163 
164         Typically select is a subclass of L{Select}, it should
165         have the following methods:
166 
167          - accept_model(model)
168          - accept_chain(chain)
169          - accept_residue(residue)
170          - accept_atom(atom)
171 
172         These methods should return 1 if the entity is to be
173         written out, 0 otherwise.
174 
175         Typically select is a subclass of L{Select}.
176         """
177         get_atom_line = self._get_atom_line
178         if isinstance(file, basestring):
179             fp = open(file, "w")
180             close_file = 1
181         else:
182             # filehandle, I hope :-)
183             fp = file
184             close_file = 0
185         # multiple models?
186         if len(self.structure) > 1 or self.use_model_flag:
187             model_flag = 1
188         else:
189             model_flag = 0
190         for model in self.structure.get_list():
191             if not select.accept_model(model):
192                 continue
193             # necessary for ENDMDL
194             # do not write ENDMDL if no residues were written
195             # for this model
196             model_residues_written = 0
197             if not preserve_atom_numbering:
198                 atom_number = 1
199             if model_flag:
200                 fp.write("MODEL      %s\n" % model.serial_num)
201             for chain in model.get_list():
202                 if not select.accept_chain(chain):
203                     continue
204                 chain_id = chain.get_id()
205                 # necessary for TER
206                 # do not write TER if no residues were written
207                 # for this chain
208                 chain_residues_written = 0
209                 for residue in chain.get_unpacked_list():
210                     if not select.accept_residue(residue):
211                         continue
212                     hetfield, resseq, icode = residue.get_id()
213                     resname = residue.get_resname()
214                     segid = residue.get_segid()
215                     for atom in residue.get_unpacked_list():
216                         if select.accept_atom(atom):
217                             chain_residues_written = 1
218                             model_residues_written = 1
219                             if preserve_atom_numbering:
220                                 atom_number = atom.get_serial_number()
221                             s = get_atom_line(atom, hetfield, segid, atom_number, resname,
222                                               resseq, icode, chain_id)
223                             fp.write(s)
224                             if not preserve_atom_numbering:
225                                 atom_number += 1
226                 if chain_residues_written:
227                     fp.write("TER   %5i      %3s %c%4i%c                                                      \n"
228                              % (atom_number, resname, chain_id, resseq, icode))
229 
230             if model_flag and model_residues_written:
231                 fp.write("ENDMDL\n")
232         if write_end:
233             fp.write('END\n')
234         if close_file:
235             fp.close()
236
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/Bio/PDB/PDBIO.py b/Bio/PDB/PDBIO.py
--- a/Bio/PDB/PDBIO.py
+++ b/Bio/PDB/PDBIO.py
@@ -123,12 +123,12 @@
             sb.init_seg(' ')
             # Build parts as necessary
             if pdb_object.level == "M":
-                sb.structure.add(pdb_object)
+                sb.structure.add(pdb_object.copy())
                 self.structure = sb.structure
             else:
                 sb.init_model(0)
                 if pdb_object.level == "C":
-                    sb.structure[0].add(pdb_object)
+                    sb.structure[0].add(pdb_object.copy())
                 else:
                     sb.init_chain('A')
                     if pdb_object.level == "R":
@@ -137,7 +137,7 @@
                             sb.structure[0]['A'].id = parent_id
                         except Exception:
                             pass
-                        sb.structure[0]['A'].add(pdb_object)
+                        sb.structure[0]['A'].add(pdb_object.copy())
                     else:
                         # Atom
                         sb.init_residue('DUM', ' ', 1, ' ')
@@ -146,7 +146,7 @@
                             sb.structure[0]['A'].id = parent_id
                         except Exception:
                             pass
-                        sb.structure[0]['A'].child_list[0].add(pdb_object)
+                        sb.structure[0]['A'].child_list[0].add(pdb_object.copy())
 
             # Return structure
             structure = sb.structure

{"golden_diff": "diff --git a/Bio/PDB/PDBIO.py b/Bio/PDB/PDBIO.py\n--- a/Bio/PDB/PDBIO.py\n+++ b/Bio/PDB/PDBIO.py\n@@ -123,12 +123,12 @@\n             sb.init_seg(' ')\n             # Build parts as necessary\n             if pdb_object.level == \"M\":\n-                sb.structure.add(pdb_object)\n+                sb.structure.add(pdb_object.copy())\n                 self.structure = sb.structure\n             else:\n                 sb.init_model(0)\n                 if pdb_object.level == \"C\":\n-                    sb.structure[0].add(pdb_object)\n+                    sb.structure[0].add(pdb_object.copy())\n                 else:\n                     sb.init_chain('A')\n                     if pdb_object.level == \"R\":\n@@ -137,7 +137,7 @@\n                             sb.structure[0]['A'].id = parent_id\n                         except Exception:\n                             pass\n-                        sb.structure[0]['A'].add(pdb_object)\n+                        sb.structure[0]['A'].add(pdb_object.copy())\n                     else:\n                         # Atom\n                         sb.init_residue('DUM', ' ', 1, ' ')\n@@ -146,7 +146,7 @@\n                             sb.structure[0]['A'].id = parent_id\n                         except Exception:\n                             pass\n-                        sb.structure[0]['A'].child_list[0].add(pdb_object)\n+                        sb.structure[0]['A'].child_list[0].add(pdb_object.copy())\n \n             # Return structure\n             structure = sb.structure\n", "issue": "`Bio.PDB.PDBIO.set_structure()` alters parent of chains, residues and atoms\nCalling the `set_structure` method of the `Bio.PDB.PDBIO` class alters the parent of (non-`Structure`) entities in a non-obvious way. For example, this script:\r\n```python\r\nimport Bio.PDB\r\npdbio = Bio.PDB.PDBIO()\r\nparser = Bio.PDB.MMCIFParser()\r\nstructure = parser.get_structure(\"12as\", \"12as.cif\")\r\nfor chain in structure[0]:\r\n    old_id = chain.id\r\n    chain.id = \" \"\r\n    pdbio.set_structure(chain)\r\n    chain.id = old_id\r\n```\r\nGives this error from line 91 of `Bio/PDB/Entity.py`:\r\n```\r\nValueError: Cannot change id from `B` to ` `. The id ` ` is already used for a sibling of this entity.\r\n```\r\nRemoving the call to `set_structure` acts as I would expect: temporarily renaming the chain name, and then reverting it without problems.\r\n\r\nWhen `set_structure` is called with an `Entity` other than a `Structure` object, it constructs a `Structure` and populates it with the entity using the `add` method, which calls `set_parent` on the entity. To me, this isn't very intuitive: I don't expect writing an object to have side effects that act on that object.\r\n\r\nI think a good solution to this would be to call `copy()` in `set_structure` before `add`ing the entity to the new `Structure` object. That is, change `sb.structure.add(pdb_object)` to `sb.structure.add(pdb_object.copy())` in `Bio.PDB.PDBIO.set_structure`. I'm happy to make a pull request if people agree that this is an issue.\n", "before_files": [{"content": "# Copyright (C) 2002, Thomas Hamelryck (thamelry@binf.ku.dk)\n# This code is part of the Biopython distribution and governed by its\n# license.  Please see the LICENSE file that should have been included\n# as part of this package.\n\n\"\"\"Output of PDB files.\"\"\"\n\nfrom Bio._py3k import basestring\n\nfrom Bio.PDB.StructureBuilder import StructureBuilder  # To allow saving of chains, residues, etc..\nfrom Bio.Data.IUPACData import atom_weights  # Allowed Elements\n\n\n_ATOM_FORMAT_STRING = \"%s%5i %-4s%c%3s %c%4i%c   %8.3f%8.3f%8.3f%s%6.2f      %4s%2s%2s\\n\"\n\n\nclass Select(object):\n    \"\"\"Select everything for PDB output (for use as a base class).\n\n    Default selection (everything) during writing - can be used as base class\n    to implement selective output. This selects which entities will be written out.\n    \"\"\"\n\n    def __repr__(self):\n        \"\"\"Represent the output as a string for debugging.\"\"\"\n        return \"<Select all>\"\n\n    def accept_model(self, model):\n        \"\"\"Overload this to reject models for output.\"\"\"\n        return 1\n\n    def accept_chain(self, chain):\n        \"\"\"Overload this to reject chains for output.\"\"\"\n        return 1\n\n    def accept_residue(self, residue):\n        \"\"\"Overload this to reject residues for output.\"\"\"\n        return 1\n\n    def accept_atom(self, atom):\n        \"\"\"Overload this to reject atoms for output.\"\"\"\n        return 1\n\n\nclass PDBIO(object):\n    \"\"\"Write a Structure object (or a subset of a Structure object) as a PDB file.\n\n    Examples\n    --------\n    >>> p=PDBParser()\n    >>> s=p.get_structure(\"1fat\", \"1fat.pdb\")\n    >>> io=PDBIO()\n    >>> io.set_structure(s)\n    >>> io.save(\"out.pdb\")\n\n    \"\"\"\n\n    def __init__(self, use_model_flag=0):\n        \"\"\"Create the PDBIO object.\n\n        :param use_model_flag: if 1, force use of the MODEL record in output.\n        :type use_model_flag: int\n        \"\"\"\n        self.use_model_flag = use_model_flag\n\n    # private mathods\n\n    def _get_atom_line(self, atom, hetfield, segid, atom_number, resname,\n                       resseq, icode, chain_id, charge=\"  \"):\n        \"\"\"Return an ATOM PDB string (PRIVATE).\"\"\"\n        if hetfield != \" \":\n            record_type = \"HETATM\"\n        else:\n            record_type = \"ATOM  \"\n\n        if atom.element:\n            element = atom.element.strip().upper()\n            if element.capitalize() not in atom_weights:\n                raise ValueError(\"Unrecognised element %r\" % atom.element)\n            element = element.rjust(2)\n        else:\n            element = \"  \"\n\n        name = atom.get_fullname().strip()\n        # Pad atom name if:\n        #     - smaller than 4 characters\n        # AND - is not C, N, O, S, H, F, P, ..., one letter elements\n        # AND - first character is NOT numeric (funky hydrogen naming rules)\n        if len(name) < 4 and name[:1].isalpha() and len(element.strip()) < 2:\n            name = \" \" + name\n\n        altloc = atom.get_altloc()\n        x, y, z = atom.get_coord()\n        bfactor = atom.get_bfactor()\n        occupancy = atom.get_occupancy()\n        try:\n            occupancy_str = \"%6.2f\" % occupancy\n        except TypeError:\n            if occupancy is None:\n                occupancy_str = \" \" * 6\n                import warnings\n                from Bio import BiopythonWarning\n                warnings.warn(\"Missing occupancy in atom %s written as blank\" %\n                              repr(atom.get_full_id()), BiopythonWarning)\n            else:\n                raise TypeError(\"Invalid occupancy %r in atom %r\"\n                                % (occupancy, atom.get_full_id()))\n\n        args = (record_type, atom_number, name, altloc, resname, chain_id,\n                resseq, icode, x, y, z, occupancy_str, bfactor, segid,\n                element, charge)\n        return _ATOM_FORMAT_STRING % args\n\n    # Public methods\n\n    def set_structure(self, pdb_object):\n        \"\"\"Check what the user is providing and build a structure.\"\"\"\n        if pdb_object.level == \"S\":\n            structure = pdb_object\n        else:\n            sb = StructureBuilder()\n            sb.init_structure('pdb')\n            sb.init_seg(' ')\n            # Build parts as necessary\n            if pdb_object.level == \"M\":\n                sb.structure.add(pdb_object)\n                self.structure = sb.structure\n            else:\n                sb.init_model(0)\n                if pdb_object.level == \"C\":\n                    sb.structure[0].add(pdb_object)\n                else:\n                    sb.init_chain('A')\n                    if pdb_object.level == \"R\":\n                        try:\n                            parent_id = pdb_object.parent.id\n                            sb.structure[0]['A'].id = parent_id\n                        except Exception:\n                            pass\n                        sb.structure[0]['A'].add(pdb_object)\n                    else:\n                        # Atom\n                        sb.init_residue('DUM', ' ', 1, ' ')\n                        try:\n                            parent_id = pdb_object.parent.parent.id\n                            sb.structure[0]['A'].id = parent_id\n                        except Exception:\n                            pass\n                        sb.structure[0]['A'].child_list[0].add(pdb_object)\n\n            # Return structure\n            structure = sb.structure\n        self.structure = structure\n\n    def save(self, file, select=Select(), write_end=True, preserve_atom_numbering=False):\n        \"\"\"Save structure to a file.\n\n        :param file: output file\n        :type file: string or filehandle\n\n        :param select: selects which entities will be written.\n        :type select: object\n\n        Typically select is a subclass of L{Select}, it should\n        have the following methods:\n\n         - accept_model(model)\n         - accept_chain(chain)\n         - accept_residue(residue)\n         - accept_atom(atom)\n\n        These methods should return 1 if the entity is to be\n        written out, 0 otherwise.\n\n        Typically select is a subclass of L{Select}.\n        \"\"\"\n        get_atom_line = self._get_atom_line\n        if isinstance(file, basestring):\n            fp = open(file, \"w\")\n            close_file = 1\n        else:\n            # filehandle, I hope :-)\n            fp = file\n            close_file = 0\n        # multiple models?\n        if len(self.structure) > 1 or self.use_model_flag:\n            model_flag = 1\n        else:\n            model_flag = 0\n        for model in self.structure.get_list():\n            if not select.accept_model(model):\n                continue\n            # necessary for ENDMDL\n            # do not write ENDMDL if no residues were written\n            # for this model\n            model_residues_written = 0\n            if not preserve_atom_numbering:\n                atom_number = 1\n            if model_flag:\n                fp.write(\"MODEL      %s\\n\" % model.serial_num)\n            for chain in model.get_list():\n                if not select.accept_chain(chain):\n                    continue\n                chain_id = chain.get_id()\n                # necessary for TER\n                # do not write TER if no residues were written\n                # for this chain\n                chain_residues_written = 0\n                for residue in chain.get_unpacked_list():\n                    if not select.accept_residue(residue):\n                        continue\n                    hetfield, resseq, icode = residue.get_id()\n                    resname = residue.get_resname()\n                    segid = residue.get_segid()\n                    for atom in residue.get_unpacked_list():\n                        if select.accept_atom(atom):\n                            chain_residues_written = 1\n                            model_residues_written = 1\n                            if preserve_atom_numbering:\n                                atom_number = atom.get_serial_number()\n                            s = get_atom_line(atom, hetfield, segid, atom_number, resname,\n                                              resseq, icode, chain_id)\n                            fp.write(s)\n                            if not preserve_atom_numbering:\n                                atom_number += 1\n                if chain_residues_written:\n                    fp.write(\"TER   %5i      %3s %c%4i%c                                                      \\n\"\n                             % (atom_number, resname, chain_id, resseq, icode))\n\n            if model_flag and model_residues_written:\n                fp.write(\"ENDMDL\\n\")\n        if write_end:\n            fp.write('END\\n')\n        if close_file:\n            fp.close()\n", "path": "Bio/PDB/PDBIO.py"}], "after_files": [{"content": "# Copyright (C) 2002, Thomas Hamelryck (thamelry@binf.ku.dk)\n# This code is part of the Biopython distribution and governed by its\n# license.  Please see the LICENSE file that should have been included\n# as part of this package.\n\n\"\"\"Output of PDB files.\"\"\"\n\nfrom Bio._py3k import basestring\n\nfrom Bio.PDB.StructureBuilder import StructureBuilder  # To allow saving of chains, residues, etc..\nfrom Bio.Data.IUPACData import atom_weights  # Allowed Elements\n\n\n_ATOM_FORMAT_STRING = \"%s%5i %-4s%c%3s %c%4i%c   %8.3f%8.3f%8.3f%s%6.2f      %4s%2s%2s\\n\"\n\n\nclass Select(object):\n    \"\"\"Select everything for PDB output (for use as a base class).\n\n    Default selection (everything) during writing - can be used as base class\n    to implement selective output. This selects which entities will be written out.\n    \"\"\"\n\n    def __repr__(self):\n        \"\"\"Represent the output as a string for debugging.\"\"\"\n        return \"<Select all>\"\n\n    def accept_model(self, model):\n        \"\"\"Overload this to reject models for output.\"\"\"\n        return 1\n\n    def accept_chain(self, chain):\n        \"\"\"Overload this to reject chains for output.\"\"\"\n        return 1\n\n    def accept_residue(self, residue):\n        \"\"\"Overload this to reject residues for output.\"\"\"\n        return 1\n\n    def accept_atom(self, atom):\n        \"\"\"Overload this to reject atoms for output.\"\"\"\n        return 1\n\n\nclass PDBIO(object):\n    \"\"\"Write a Structure object (or a subset of a Structure object) as a PDB file.\n\n    Examples\n    --------\n    >>> p=PDBParser()\n    >>> s=p.get_structure(\"1fat\", \"1fat.pdb\")\n    >>> io=PDBIO()\n    >>> io.set_structure(s)\n    >>> io.save(\"out.pdb\")\n\n    \"\"\"\n\n    def __init__(self, use_model_flag=0):\n        \"\"\"Create the PDBIO object.\n\n        :param use_model_flag: if 1, force use of the MODEL record in output.\n        :type use_model_flag: int\n        \"\"\"\n        self.use_model_flag = use_model_flag\n\n    # private mathods\n\n    def _get_atom_line(self, atom, hetfield, segid, atom_number, resname,\n                       resseq, icode, chain_id, charge=\"  \"):\n        \"\"\"Return an ATOM PDB string (PRIVATE).\"\"\"\n        if hetfield != \" \":\n            record_type = \"HETATM\"\n        else:\n            record_type = \"ATOM  \"\n\n        if atom.element:\n            element = atom.element.strip().upper()\n            if element.capitalize() not in atom_weights:\n                raise ValueError(\"Unrecognised element %r\" % atom.element)\n            element = element.rjust(2)\n        else:\n            element = \"  \"\n\n        name = atom.get_fullname().strip()\n        # Pad atom name if:\n        #     - smaller than 4 characters\n        # AND - is not C, N, O, S, H, F, P, ..., one letter elements\n        # AND - first character is NOT numeric (funky hydrogen naming rules)\n        if len(name) < 4 and name[:1].isalpha() and len(element.strip()) < 2:\n            name = \" \" + name\n\n        altloc = atom.get_altloc()\n        x, y, z = atom.get_coord()\n        bfactor = atom.get_bfactor()\n        occupancy = atom.get_occupancy()\n        try:\n            occupancy_str = \"%6.2f\" % occupancy\n        except TypeError:\n            if occupancy is None:\n                occupancy_str = \" \" * 6\n                import warnings\n                from Bio import BiopythonWarning\n                warnings.warn(\"Missing occupancy in atom %s written as blank\" %\n                              repr(atom.get_full_id()), BiopythonWarning)\n            else:\n                raise TypeError(\"Invalid occupancy %r in atom %r\"\n                                % (occupancy, atom.get_full_id()))\n\n        args = (record_type, atom_number, name, altloc, resname, chain_id,\n                resseq, icode, x, y, z, occupancy_str, bfactor, segid,\n                element, charge)\n        return _ATOM_FORMAT_STRING % args\n\n    # Public methods\n\n    def set_structure(self, pdb_object):\n        \"\"\"Check what the user is providing and build a structure.\"\"\"\n        if pdb_object.level == \"S\":\n            structure = pdb_object\n        else:\n            sb = StructureBuilder()\n            sb.init_structure('pdb')\n            sb.init_seg(' ')\n            # Build parts as necessary\n            if pdb_object.level == \"M\":\n                sb.structure.add(pdb_object.copy())\n                self.structure = sb.structure\n            else:\n                sb.init_model(0)\n                if pdb_object.level == \"C\":\n                    sb.structure[0].add(pdb_object.copy())\n                else:\n                    sb.init_chain('A')\n                    if pdb_object.level == \"R\":\n                        try:\n                            parent_id = pdb_object.parent.id\n                            sb.structure[0]['A'].id = parent_id\n                        except Exception:\n                            pass\n                        sb.structure[0]['A'].add(pdb_object.copy())\n                    else:\n                        # Atom\n                        sb.init_residue('DUM', ' ', 1, ' ')\n                        try:\n                            parent_id = pdb_object.parent.parent.id\n                            sb.structure[0]['A'].id = parent_id\n                        except Exception:\n                            pass\n                        sb.structure[0]['A'].child_list[0].add(pdb_object.copy())\n\n            # Return structure\n            structure = sb.structure\n        self.structure = structure\n\n    def save(self, file, select=Select(), write_end=True, preserve_atom_numbering=False):\n        \"\"\"Save structure to a file.\n\n        :param file: output file\n        :type file: string or filehandle\n\n        :param select: selects which entities will be written.\n        :type select: object\n\n        Typically select is a subclass of L{Select}, it should\n        have the following methods:\n\n         - accept_model(model)\n         - accept_chain(chain)\n         - accept_residue(residue)\n         - accept_atom(atom)\n\n        These methods should return 1 if the entity is to be\n        written out, 0 otherwise.\n\n        Typically select is a subclass of L{Select}.\n        \"\"\"\n        get_atom_line = self._get_atom_line\n        if isinstance(file, basestring):\n            fp = open(file, \"w\")\n            close_file = 1\n        else:\n            # filehandle, I hope :-)\n            fp = file\n            close_file = 0\n        # multiple models?\n        if len(self.structure) > 1 or self.use_model_flag:\n            model_flag = 1\n        else:\n            model_flag = 0\n        for model in self.structure.get_list():\n            if not select.accept_model(model):\n                continue\n            # necessary for ENDMDL\n            # do not write ENDMDL if no residues were written\n            # for this model\n            model_residues_written = 0\n            if not preserve_atom_numbering:\n                atom_number = 1\n            if model_flag:\n                fp.write(\"MODEL      %s\\n\" % model.serial_num)\n            for chain in model.get_list():\n                if not select.accept_chain(chain):\n                    continue\n                chain_id = chain.get_id()\n                # necessary for TER\n                # do not write TER if no residues were written\n                # for this chain\n                chain_residues_written = 0\n                for residue in chain.get_unpacked_list():\n                    if not select.accept_residue(residue):\n                        continue\n                    hetfield, resseq, icode = residue.get_id()\n                    resname = residue.get_resname()\n                    segid = residue.get_segid()\n                    for atom in residue.get_unpacked_list():\n                        if select.accept_atom(atom):\n                            chain_residues_written = 1\n                            model_residues_written = 1\n                            if preserve_atom_numbering:\n                                atom_number = atom.get_serial_number()\n                            s = get_atom_line(atom, hetfield, segid, atom_number, resname,\n                                              resseq, icode, chain_id)\n                            fp.write(s)\n                            if not preserve_atom_numbering:\n                                atom_number += 1\n                if chain_residues_written:\n                    fp.write(\"TER   %5i      %3s %c%4i%c                                                      \\n\"\n                             % (atom_number, resname, chain_id, resseq, icode))\n\n            if model_flag and model_residues_written:\n                fp.write(\"ENDMDL\\n\")\n        if write_end:\n            fp.write('END\\n')\n        if close_file:\n            fp.close()\n", "path": "Bio/PDB/PDBIO.py"}]}