Repository: Pyomo/pyomo

Issue:
Ports are not cloned correctly
```
import pyomo.environ as pe
import idaes.core
import idaes.generic_models.properties.swco2
import idaes.generic_models.unit_models
from pyomo.network import Arc

m = pe.ConcreteModel()
m.fs = idaes.core.FlowsheetBlock()
m.fs.properties = idaes.generic_models.properties.swco2.SWCO2ParameterBlock()
m.fs.heater = idaes.generic_models.unit_models.Heater(default={'dynamic': False,
                                                               'property_package': m.fs.properties,
                                                               'has_pressure_change': True})
m.fs.heater2 = idaes.generic_models.unit_models.Heater(default={'dynamic': False,
                                                                'property_package': m.fs.properties,
                                                                'has_pressure_change': True})
m.fs.stream = Arc(source=m.fs.heater.outlet,
                  destination=m.fs.heater2.inlet)
m2 = m.clone()
print(m2.fs.heater.outlet._arcs[0]() is m.fs.stream)  # True
```


Discussion:
I think this has to do with the weakref
However, _PortData._component is a weakref, and it clones correctly...

Resolving patch:
diff --git a/pyomo/network/port.py b/pyomo/network/port.py
--- a/pyomo/network/port.py
+++ b/pyomo/network/port.py
@@ -67,8 +67,18 @@ def __getstate__(self):
         state = super(_PortData, self).__getstate__()
         for i in _PortData.__slots__:
             state[i] = getattr(self, i)
+
+        # Remove/resolve weak references
+        for i in ('_arcs', '_sources', '_dests'):
+            state[i] = [ref() for ref in state[i]]
         return state
 
+    def __setstate__(self, state):
+        state['_arcs'] = [weakref_ref(i) for i in state['_arcs']]
+        state['_sources'] = [weakref_ref(i) for i in state['_sources']]
+        state['_dests'] = [weakref_ref(i) for i in state['_dests']]
+        super(_PortData, self).__setstate__(state)
+
     # Note: None of the slots on this class need to be edited, so we
     # don't need to implement a specialized __setstate__ method, and
     # can quietly rely on the super() class's implementation.


Test patch:
diff --git a/pyomo/network/tests/test_arc.py b/pyomo/network/tests/test_arc.py
--- a/pyomo/network/tests/test_arc.py
+++ b/pyomo/network/tests/test_arc.py
@@ -15,8 +15,10 @@
 from six import StringIO
 import logging
 
-from pyomo.environ import ConcreteModel, AbstractModel, Var, Set, Constraint, RangeSet, NonNegativeReals, Reals, Binary, TransformationFactory, Block
+from pyomo.environ import ConcreteModel, AbstractModel, Var, Set, Constraint, RangeSet, NonNegativeReals, Reals, Binary, TransformationFactory, Block, value
 from pyomo.network import Arc, Port
+from pyomo.core.expr.visitor import identify_variables
+from pyomo.common.collections.component_set import ComponentSet
 
 class TestArc(unittest.TestCase):
 
@@ -1876,6 +1878,40 @@ def test_extensive_expansion(self):
 28 Declarations: comp feed tru node1 node2 multi prod stream0 stream1 stream2 stream3 stream4 stream5 stream6 stream7 stream8 stream9 stream10 stream1_expanded stream2_expanded stream3_expanded stream4_expanded stream5_expanded stream6_expanded stream7_expanded stream8_expanded stream9_expanded stream10_expanded
 """)
 
+    def test_clone(self):
+        m = ConcreteModel()
+        m.x = Var()
+        m.y = Var()
+        m.p1 = Port()
+        m.p2 = Port()
+        m.p1.add(m.x, 'v')
+        m.p2.add(m.y, 'v')
+        m.arc = Arc(source=m.p1, destination=m.p2)
+
+        m2 = m.clone()
+        self.assertEqual(len(m2.p1.arcs()), 1)
+        self.assertEqual(len(m2.p2.arcs()), 1)
+        self.assertIs(m2.p1.arcs()[0], m2.arc)
+        self.assertIs(m2.p2.arcs()[0], m2.arc)
+
+        self.assertIsNot(m2.p1.arcs()[0], m.arc)
+        self.assertIsNot(m2.p2.arcs()[0], m.arc)
+
+        TransformationFactory('network.expand_arcs').apply_to(m2)
+        all_cons = list(m2.component_data_objects(Constraint))
+        self.assertEqual(len(all_cons), 1)
+        c = all_cons[0]
+        self.assertAlmostEqual(value(c.lower), 0)
+        self.assertAlmostEqual(value(c.upper), 0)
+        c_vars = ComponentSet(identify_variables(c.body))
+        self.assertIn(m2.x, c_vars)
+        self.assertIn(m2.y, c_vars)
+        self.assertNotIn(m.x, c_vars)
+        self.assertNotIn(m.y, c_vars)
+        m2.x.value = 1.25
+        m2.y.value = 1.25
+        self.assertAlmostEqual(value(c.body), 0)
+
 
 if __name__ == "__main__":
     unittest.main()