We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Appropriate behavior for activate() and deactivate() for nested disjunctions?
```
from pyomo.environ import *
m = ConcreteModel()
m.d1 = Disjunct()
m.d2 = Disjunct()
m.d1.sub1 = Disjunct()
m.d1.sub2 = Disjunct()
m.d1.disj = Disjunction(expr=[m.d1.sub1, m.d1.sub2])
m.disj = Disjunction(expr=[m.d1, m.d2])
```

What should happen when disjuncts containing nested disjunctions are deactivated? `m.d1.deactivate()`.
The reclassifier hack complains about `m.d1.sub1` and `m.d1.sub2` not being expanded.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pyomo/gdp/plugins/gdp_var_mover.py`
Content:
```
1 #  ___________________________________________________________________________
2 #
3 #  Pyomo: Python Optimization Modeling Objects
4 #  Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC
5 #  Under the terms of Contract DE-NA0003525 with National Technology and
6 #  Engineering Solutions of Sandia, LLC, the U.S. Government retains certain
7 #  rights in this software.
8 #  This software is distributed under the 3-clause BSD License.
9 #  ___________________________________________________________________________
10 
11 """Collection of GDP-related hacks.
12 
13 Hacks for dealing with the fact that solver writers may sometimes fail to
14 detect variables inside of Disjuncts or deactivated Blocks.
15 """
16 
17 import logging
18 import textwrap
19 from pyomo.common.plugin import alias
20 from pyomo.core.base import Transformation, Block, Constraint
21 from pyomo.gdp import Disjunct
22 
23 from six import itervalues
24 
25 logger = logging.getLogger('pyomo.gdp')
26 
27 
28 class HACK_GDP_Var_Mover(Transformation):
29     """Move indicator vars to top block.
30 
31     HACK: this will move all indicator variables on the model to the top block
32     so the writers can find them.
33 
34     """
35 
36     alias('gdp.varmover', doc=textwrap.fill(textwrap.dedent(__doc__.strip())))
37 
38     def _apply_to(self, instance, **kwds):
39         assert not kwds
40         count = 0
41         disjunct_generator = instance.component_data_objects(
42             Disjunct, descend_into=(Block, Disjunct))
43         for disjunct in disjunct_generator:
44             count += 1
45             var = disjunct.indicator_var
46             var.doc = "%s(Moved from %s)" % (
47                 var.doc + " " if var.doc else "", var.name, )
48             disjunct.del_component(var)
49             instance.add_component("_gdp_moved_IV_%s" % (count,), var)
50 
51 
52 class HACK_GDP_Disjunct_Reclassifier(Transformation):
53     """Reclassify Disjuncts to Blocks.
54 
55     HACK: this will reclassify all Disjuncts to Blocks so the current writers
56     can find the variables
57 
58     """
59 
60     alias('gdp.reclassify',
61           doc=textwrap.fill(textwrap.dedent(__doc__.strip())))
62 
63     def _apply_to(self, instance, **kwds):
64         assert not kwds
65         disjunct_generator = instance.component_objects(
66             Disjunct, descend_into=(Block, Disjunct))
67         for disjunct_component in disjunct_generator:
68             for disjunct in itervalues(disjunct_component._data):
69                 if disjunct.active:
70                     logger.error("""
71                     Reclassifying active Disjunct "%s" as a Block.  This
72                     is generally an error as it indicates that the model
73                     was not completely relaxed before applying the
74                     gdp.reclassify transformation""" % (disjunct.name,))
75 
76             # Reclassify this disjunct as a block
77             disjunct_component.parent_block().reclassify_component_type(
78                 disjunct_component, Block)
79             disjunct_component._activate_without_unfixing_indicator()
80 
81             # Deactivate all constraints.  Note that we only need to
82             # descend into blocks: we will catch disjuncts in the outer
83             # loop.
84             #
85             # Note that we defer this until AFTER we reactivate the
86             # block, as the component_objects generator will not
87             # return anything when active=True and the block is
88             # deactivated.
89             for disjunct in itervalues(disjunct_component._data):
90                 cons_in_disjunct = disjunct.component_objects(
91                     Constraint, descend_into=Block, active=True)
92                 for con in cons_in_disjunct:
93                     con.deactivate()
94
```
--- 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/pyomo/gdp/plugins/gdp_var_mover.py b/pyomo/gdp/plugins/gdp_var_mover.py
--- a/pyomo/gdp/plugins/gdp_var_mover.py
+++ b/pyomo/gdp/plugins/gdp_var_mover.py
@@ -18,7 +18,9 @@
 import textwrap
 from pyomo.common.plugin import alias
 from pyomo.core.base import Transformation, Block, Constraint
-from pyomo.gdp import Disjunct
+from pyomo.gdp import Disjunct, GDP_Error
+from pyomo.core import TraversalStrategy
+from pyomo.common.deprecation import deprecated
 
 from six import itervalues
 
@@ -35,6 +37,8 @@
 
     alias('gdp.varmover', doc=textwrap.fill(textwrap.dedent(__doc__.strip())))
 
+    @deprecated(msg="The gdp.varmover transformation has been deprecated in "
+                "favor of the gdp.reclassify transformation.")
     def _apply_to(self, instance, **kwds):
         assert not kwds
         count = 0
@@ -61,13 +65,19 @@
           doc=textwrap.fill(textwrap.dedent(__doc__.strip())))
 
     def _apply_to(self, instance, **kwds):
-        assert not kwds
+        assert not kwds  # no keywords expected to the transformation
         disjunct_generator = instance.component_objects(
-            Disjunct, descend_into=(Block, Disjunct))
+            Disjunct, descend_into=(Block, Disjunct),
+            descent_order=TraversalStrategy.PostfixDFS)
         for disjunct_component in disjunct_generator:
+            # Check that the disjuncts being reclassified are all relaxed or
+            # are not on an active block.
             for disjunct in itervalues(disjunct_component._data):
-                if disjunct.active:
-                    logger.error("""
+                if (disjunct.active and
+                        self._disjunct_not_relaxed(disjunct) and
+                        self._disjunct_on_active_block(disjunct) and
+                        self._disjunct_not_fixed_true(disjunct)):
+                    raise GDP_Error("""
                     Reclassifying active Disjunct "%s" as a Block.  This
                     is generally an error as it indicates that the model
                     was not completely relaxed before applying the
@@ -91,3 +101,31 @@
                     Constraint, descend_into=Block, active=True)
                 for con in cons_in_disjunct:
                     con.deactivate()
+
+    def _disjunct_not_fixed_true(self, disjunct):
+        # Return true if the disjunct indicator variable is not fixed to True
+        return not (disjunct.indicator_var.fixed and
+                    disjunct.indicator_var.value == 1)
+
+    def _disjunct_not_relaxed(self, disjunct):
+        # Return True if the disjunct was not relaxed by a transformation.
+        return not getattr(
+            disjunct, '_gdp_transformation_info', {}).get('relaxed', False)
+
+    def _disjunct_on_active_block(self, disjunct):
+        # Check first to make sure that the disjunct is not a
+        # descendent of an inactive Block or fixed and deactivated
+        # Disjunct, before raising a warning.
+        parent_block = disjunct.parent_block()
+        while parent_block is not None:
+            if parent_block.type() is Block and not parent_block.active:
+                return False
+            elif (parent_block.type() is Disjunct and not parent_block.active
+                  and parent_block.indicator_var.value == 0
+                  and parent_block.indicator_var.fixed):
+                return False
+            else:
+                # Step up one level in the hierarchy
+                parent_block = parent_block.parent_block()
+                continue
+        return True

{"golden_diff": "diff --git a/pyomo/gdp/plugins/gdp_var_mover.py b/pyomo/gdp/plugins/gdp_var_mover.py\n--- a/pyomo/gdp/plugins/gdp_var_mover.py\n+++ b/pyomo/gdp/plugins/gdp_var_mover.py\n@@ -18,7 +18,9 @@\n import textwrap\n from pyomo.common.plugin import alias\n from pyomo.core.base import Transformation, Block, Constraint\n-from pyomo.gdp import Disjunct\n+from pyomo.gdp import Disjunct, GDP_Error\n+from pyomo.core import TraversalStrategy\n+from pyomo.common.deprecation import deprecated\n \n from six import itervalues\n \n@@ -35,6 +37,8 @@\n \n     alias('gdp.varmover', doc=textwrap.fill(textwrap.dedent(__doc__.strip())))\n \n+    @deprecated(msg=\"The gdp.varmover transformation has been deprecated in \"\n+                \"favor of the gdp.reclassify transformation.\")\n     def _apply_to(self, instance, **kwds):\n         assert not kwds\n         count = 0\n@@ -61,13 +65,19 @@\n           doc=textwrap.fill(textwrap.dedent(__doc__.strip())))\n \n     def _apply_to(self, instance, **kwds):\n-        assert not kwds\n+        assert not kwds  # no keywords expected to the transformation\n         disjunct_generator = instance.component_objects(\n-            Disjunct, descend_into=(Block, Disjunct))\n+            Disjunct, descend_into=(Block, Disjunct),\n+            descent_order=TraversalStrategy.PostfixDFS)\n         for disjunct_component in disjunct_generator:\n+            # Check that the disjuncts being reclassified are all relaxed or\n+            # are not on an active block.\n             for disjunct in itervalues(disjunct_component._data):\n-                if disjunct.active:\n-                    logger.error(\"\"\"\n+                if (disjunct.active and\n+                        self._disjunct_not_relaxed(disjunct) and\n+                        self._disjunct_on_active_block(disjunct) and\n+                        self._disjunct_not_fixed_true(disjunct)):\n+                    raise GDP_Error(\"\"\"\n                     Reclassifying active Disjunct \"%s\" as a Block.  This\n                     is generally an error as it indicates that the model\n                     was not completely relaxed before applying the\n@@ -91,3 +101,31 @@\n                     Constraint, descend_into=Block, active=True)\n                 for con in cons_in_disjunct:\n                     con.deactivate()\n+\n+    def _disjunct_not_fixed_true(self, disjunct):\n+        # Return true if the disjunct indicator variable is not fixed to True\n+        return not (disjunct.indicator_var.fixed and\n+                    disjunct.indicator_var.value == 1)\n+\n+    def _disjunct_not_relaxed(self, disjunct):\n+        # Return True if the disjunct was not relaxed by a transformation.\n+        return not getattr(\n+            disjunct, '_gdp_transformation_info', {}).get('relaxed', False)\n+\n+    def _disjunct_on_active_block(self, disjunct):\n+        # Check first to make sure that the disjunct is not a\n+        # descendent of an inactive Block or fixed and deactivated\n+        # Disjunct, before raising a warning.\n+        parent_block = disjunct.parent_block()\n+        while parent_block is not None:\n+            if parent_block.type() is Block and not parent_block.active:\n+                return False\n+            elif (parent_block.type() is Disjunct and not parent_block.active\n+                  and parent_block.indicator_var.value == 0\n+                  and parent_block.indicator_var.fixed):\n+                return False\n+            else:\n+                # Step up one level in the hierarchy\n+                parent_block = parent_block.parent_block()\n+                continue\n+        return True\n", "issue": "Appropriate behavior for activate() and deactivate() for nested disjunctions?\n```\r\nfrom pyomo.environ import *\r\nm = ConcreteModel()\r\nm.d1 = Disjunct()\r\nm.d2 = Disjunct()\r\nm.d1.sub1 = Disjunct()\r\nm.d1.sub2 = Disjunct()\r\nm.d1.disj = Disjunction(expr=[m.d1.sub1, m.d1.sub2])\r\nm.disj = Disjunction(expr=[m.d1, m.d2])\r\n```\r\n\r\nWhat should happen when disjuncts containing nested disjunctions are deactivated? `m.d1.deactivate()`.\r\nThe reclassifier hack complains about `m.d1.sub1` and `m.d1.sub2` not being expanded.\n", "before_files": [{"content": "#  ___________________________________________________________________________\n#\n#  Pyomo: Python Optimization Modeling Objects\n#  Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC\n#  Under the terms of Contract DE-NA0003525 with National Technology and\n#  Engineering Solutions of Sandia, LLC, the U.S. Government retains certain\n#  rights in this software.\n#  This software is distributed under the 3-clause BSD License.\n#  ___________________________________________________________________________\n\n\"\"\"Collection of GDP-related hacks.\n\nHacks for dealing with the fact that solver writers may sometimes fail to\ndetect variables inside of Disjuncts or deactivated Blocks.\n\"\"\"\n\nimport logging\nimport textwrap\nfrom pyomo.common.plugin import alias\nfrom pyomo.core.base import Transformation, Block, Constraint\nfrom pyomo.gdp import Disjunct\n\nfrom six import itervalues\n\nlogger = logging.getLogger('pyomo.gdp')\n\n\nclass HACK_GDP_Var_Mover(Transformation):\n    \"\"\"Move indicator vars to top block.\n\n    HACK: this will move all indicator variables on the model to the top block\n    so the writers can find them.\n\n    \"\"\"\n\n    alias('gdp.varmover', doc=textwrap.fill(textwrap.dedent(__doc__.strip())))\n\n    def _apply_to(self, instance, **kwds):\n        assert not kwds\n        count = 0\n        disjunct_generator = instance.component_data_objects(\n            Disjunct, descend_into=(Block, Disjunct))\n        for disjunct in disjunct_generator:\n            count += 1\n            var = disjunct.indicator_var\n            var.doc = \"%s(Moved from %s)\" % (\n                var.doc + \" \" if var.doc else \"\", var.name, )\n            disjunct.del_component(var)\n            instance.add_component(\"_gdp_moved_IV_%s\" % (count,), var)\n\n\nclass HACK_GDP_Disjunct_Reclassifier(Transformation):\n    \"\"\"Reclassify Disjuncts to Blocks.\n\n    HACK: this will reclassify all Disjuncts to Blocks so the current writers\n    can find the variables\n\n    \"\"\"\n\n    alias('gdp.reclassify',\n          doc=textwrap.fill(textwrap.dedent(__doc__.strip())))\n\n    def _apply_to(self, instance, **kwds):\n        assert not kwds\n        disjunct_generator = instance.component_objects(\n            Disjunct, descend_into=(Block, Disjunct))\n        for disjunct_component in disjunct_generator:\n            for disjunct in itervalues(disjunct_component._data):\n                if disjunct.active:\n                    logger.error(\"\"\"\n                    Reclassifying active Disjunct \"%s\" as a Block.  This\n                    is generally an error as it indicates that the model\n                    was not completely relaxed before applying the\n                    gdp.reclassify transformation\"\"\" % (disjunct.name,))\n\n            # Reclassify this disjunct as a block\n            disjunct_component.parent_block().reclassify_component_type(\n                disjunct_component, Block)\n            disjunct_component._activate_without_unfixing_indicator()\n\n            # Deactivate all constraints.  Note that we only need to\n            # descend into blocks: we will catch disjuncts in the outer\n            # loop.\n            #\n            # Note that we defer this until AFTER we reactivate the\n            # block, as the component_objects generator will not\n            # return anything when active=True and the block is\n            # deactivated.\n            for disjunct in itervalues(disjunct_component._data):\n                cons_in_disjunct = disjunct.component_objects(\n                    Constraint, descend_into=Block, active=True)\n                for con in cons_in_disjunct:\n                    con.deactivate()\n", "path": "pyomo/gdp/plugins/gdp_var_mover.py"}], "after_files": [{"content": "#  ___________________________________________________________________________\n#\n#  Pyomo: Python Optimization Modeling Objects\n#  Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC\n#  Under the terms of Contract DE-NA0003525 with National Technology and\n#  Engineering Solutions of Sandia, LLC, the U.S. Government retains certain\n#  rights in this software.\n#  This software is distributed under the 3-clause BSD License.\n#  ___________________________________________________________________________\n\n\"\"\"Collection of GDP-related hacks.\n\nHacks for dealing with the fact that solver writers may sometimes fail to\ndetect variables inside of Disjuncts or deactivated Blocks.\n\"\"\"\n\nimport logging\nimport textwrap\nfrom pyomo.common.plugin import alias\nfrom pyomo.core.base import Transformation, Block, Constraint\nfrom pyomo.gdp import Disjunct, GDP_Error\nfrom pyomo.core import TraversalStrategy\nfrom pyomo.common.deprecation import deprecated\n\nfrom six import itervalues\n\nlogger = logging.getLogger('pyomo.gdp')\n\n\nclass HACK_GDP_Var_Mover(Transformation):\n    \"\"\"Move indicator vars to top block.\n\n    HACK: this will move all indicator variables on the model to the top block\n    so the writers can find them.\n\n    \"\"\"\n\n    alias('gdp.varmover', doc=textwrap.fill(textwrap.dedent(__doc__.strip())))\n\n    @deprecated(msg=\"The gdp.varmover transformation has been deprecated in \"\n                \"favor of the gdp.reclassify transformation.\")\n    def _apply_to(self, instance, **kwds):\n        assert not kwds\n        count = 0\n        disjunct_generator = instance.component_data_objects(\n            Disjunct, descend_into=(Block, Disjunct))\n        for disjunct in disjunct_generator:\n            count += 1\n            var = disjunct.indicator_var\n            var.doc = \"%s(Moved from %s)\" % (\n                var.doc + \" \" if var.doc else \"\", var.name, )\n            disjunct.del_component(var)\n            instance.add_component(\"_gdp_moved_IV_%s\" % (count,), var)\n\n\nclass HACK_GDP_Disjunct_Reclassifier(Transformation):\n    \"\"\"Reclassify Disjuncts to Blocks.\n\n    HACK: this will reclassify all Disjuncts to Blocks so the current writers\n    can find the variables\n\n    \"\"\"\n\n    alias('gdp.reclassify',\n          doc=textwrap.fill(textwrap.dedent(__doc__.strip())))\n\n    def _apply_to(self, instance, **kwds):\n        assert not kwds  # no keywords expected to the transformation\n        disjunct_generator = instance.component_objects(\n            Disjunct, descend_into=(Block, Disjunct),\n            descent_order=TraversalStrategy.PostfixDFS)\n        for disjunct_component in disjunct_generator:\n            # Check that the disjuncts being reclassified are all relaxed or\n            # are not on an active block.\n            for disjunct in itervalues(disjunct_component._data):\n                if (disjunct.active and\n                        self._disjunct_not_relaxed(disjunct) and\n                        self._disjunct_on_active_block(disjunct) and\n                        self._disjunct_not_fixed_true(disjunct)):\n                    raise GDP_Error(\"\"\"\n                    Reclassifying active Disjunct \"%s\" as a Block.  This\n                    is generally an error as it indicates that the model\n                    was not completely relaxed before applying the\n                    gdp.reclassify transformation\"\"\" % (disjunct.name,))\n\n            # Reclassify this disjunct as a block\n            disjunct_component.parent_block().reclassify_component_type(\n                disjunct_component, Block)\n            disjunct_component._activate_without_unfixing_indicator()\n\n            # Deactivate all constraints.  Note that we only need to\n            # descend into blocks: we will catch disjuncts in the outer\n            # loop.\n            #\n            # Note that we defer this until AFTER we reactivate the\n            # block, as the component_objects generator will not\n            # return anything when active=True and the block is\n            # deactivated.\n            for disjunct in itervalues(disjunct_component._data):\n                cons_in_disjunct = disjunct.component_objects(\n                    Constraint, descend_into=Block, active=True)\n                for con in cons_in_disjunct:\n                    con.deactivate()\n\n    def _disjunct_not_fixed_true(self, disjunct):\n        # Return true if the disjunct indicator variable is not fixed to True\n        return not (disjunct.indicator_var.fixed and\n                    disjunct.indicator_var.value == 1)\n\n    def _disjunct_not_relaxed(self, disjunct):\n        # Return True if the disjunct was not relaxed by a transformation.\n        return not getattr(\n            disjunct, '_gdp_transformation_info', {}).get('relaxed', False)\n\n    def _disjunct_on_active_block(self, disjunct):\n        # Check first to make sure that the disjunct is not a\n        # descendent of an inactive Block or fixed and deactivated\n        # Disjunct, before raising a warning.\n        parent_block = disjunct.parent_block()\n        while parent_block is not None:\n            if parent_block.type() is Block and not parent_block.active:\n                return False\n            elif (parent_block.type() is Disjunct and not parent_block.active\n                  and parent_block.indicator_var.value == 0\n                  and parent_block.indicator_var.fixed):\n                return False\n            else:\n                # Step up one level in the hierarchy\n                parent_block = parent_block.parent_block()\n                continue\n        return True\n", "path": "pyomo/gdp/plugins/gdp_var_mover.py"}]}