@@ -1239,3 +1239,88 @@ def get_prefix(ds: xr.Dataset, prefix: str) -> xr.Dataset:
12391239 setattr (m , k , ds .attrs .get (k ))
12401240
12411241 return m
1242+
1243+
1244+ def copy (src : Model , include_solution : bool = False ) -> Model :
1245+ """
1246+ Return a deep copy of this model.
1247+
1248+ Copies variables, constraints, objective, parameters, blocks, and all
1249+ scalar attributes (counters, flags). The copy is fully independent:
1250+ modifying one does not affect the other.
1251+
1252+ Parameters
1253+ ----------
1254+ src : Model
1255+ The model to copy.
1256+ include_solution : bool, optional
1257+ Whether to include the current solution and dual values in the copy.
1258+ If False (default), the copy is returned in an initialized state:
1259+ solution and dual data are excluded, objective value is set to None,
1260+ and status is set to 'initialized'. If True, solution, dual values,
1261+ solve status, and objective value are also copied.
1262+
1263+ Returns
1264+ -------
1265+ Model
1266+ A deep copy of the model.
1267+ """
1268+ from linopy .model import (
1269+ Constraint ,
1270+ Constraints ,
1271+ LinearExpression ,
1272+ Model ,
1273+ Objective ,
1274+ Variable ,
1275+ Variables ,
1276+ )
1277+
1278+ SOLVE_STATE_ATTRS = {"status" , "termination_condition" }
1279+
1280+ m = Model (
1281+ chunk = src ._chunk ,
1282+ force_dim_names = src ._force_dim_names ,
1283+ auto_mask = src ._auto_mask ,
1284+ solver_dir = str (src ._solver_dir ),
1285+ )
1286+
1287+ m ._variables = Variables (
1288+ {
1289+ name : Variable (
1290+ var .data .copy (deep = True )
1291+ if include_solution
1292+ else var .data [src .variables .dataset_attrs ].copy (deep = True ),
1293+ m ,
1294+ name ,
1295+ )
1296+ for name , var in src .variables .items ()
1297+ },
1298+ m ,
1299+ )
1300+
1301+ m ._constraints = Constraints (
1302+ {
1303+ name : Constraint (
1304+ con .data .copy (deep = True )
1305+ if include_solution
1306+ else con .data [src .constraints .dataset_attrs ].copy (deep = True ),
1307+ m ,
1308+ name ,
1309+ )
1310+ for name , con in src .constraints .items ()
1311+ },
1312+ m ,
1313+ )
1314+
1315+ obj_expr = LinearExpression (src .objective .expression .data .copy (deep = True ), m )
1316+ m ._objective = Objective (obj_expr , m , src .objective .sense )
1317+ m ._objective ._value = src .objective .value if include_solution else None
1318+
1319+ m ._parameters = src ._parameters .copy (deep = True )
1320+ m ._blocks = src ._blocks .copy (deep = True ) if src ._blocks is not None else None
1321+
1322+ for attr in src .scalar_attrs :
1323+ if include_solution or attr not in SOLVE_STATE_ATTRS :
1324+ setattr (m , attr , getattr (src , attr ))
1325+
1326+ return m
0 commit comments