pyconvexity 0.5.0__tar.gz → 0.5.0.post1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pyconvexity might be problematic. Click here for more details.
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/PKG-INFO +1 -1
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/pyproject.toml +2 -2
- pyconvexity-0.5.0.post1/src/pyconvexity/_version.py +1 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/solvers/pypsa/solver.py +11 -373
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity.egg-info/PKG-INFO +1 -1
- pyconvexity-0.5.0/src/pyconvexity/_version.py +0 -1
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/README.md +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/setup.cfg +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/__init__.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/core/__init__.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/core/database.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/core/errors.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/core/types.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/dashboard.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/data/README.md +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/data/__init__.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/data/loaders/__init__.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/data/loaders/cache.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/data/schema/01_core_schema.sql +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/data/schema/02_data_metadata.sql +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/data/schema/03_validation_data.sql +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/data/sources/__init__.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/data/sources/gem.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/io/__init__.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/io/excel_exporter.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/io/excel_importer.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/io/netcdf_exporter.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/io/netcdf_importer.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/models/__init__.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/models/attributes.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/models/carriers.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/models/components.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/models/network.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/models/results.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/models/scenarios.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/solvers/__init__.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/solvers/pypsa/__init__.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/solvers/pypsa/api.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/solvers/pypsa/batch_loader.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/solvers/pypsa/builder.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/solvers/pypsa/clearing_price.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/solvers/pypsa/constraints.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/solvers/pypsa/storage.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/timeseries.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/validation/__init__.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/validation/rules.py +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity.egg-info/SOURCES.txt +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity.egg-info/dependency_links.txt +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity.egg-info/requires.txt +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity.egg-info/top_level.txt +0 -0
- {pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/tests/test_core_types.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pyconvexity"
|
|
7
|
-
version = "0.5.0"
|
|
7
|
+
version = "0.5.0.post1"
|
|
8
8
|
description = "Python library for energy system modeling and optimization with PyPSA"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -81,7 +81,7 @@ profile = "black"
|
|
|
81
81
|
line_length = 88
|
|
82
82
|
|
|
83
83
|
[tool.mypy]
|
|
84
|
-
python_version = "0.5.0"
|
|
84
|
+
python_version = "0.5.0.post1"
|
|
85
85
|
warn_return_any = true
|
|
86
86
|
warn_unused_configs = true
|
|
87
87
|
disallow_untyped_defs = true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.5.0.post1"
|
|
@@ -38,81 +38,6 @@ class NetworkSolver:
|
|
|
38
38
|
"Please ensure it is installed correctly in the environment."
|
|
39
39
|
) from e
|
|
40
40
|
|
|
41
|
-
def _get_user_settings_path(self):
|
|
42
|
-
"""Get the path to the user settings file (same location as Tauri uses)"""
|
|
43
|
-
try:
|
|
44
|
-
import platform
|
|
45
|
-
import os
|
|
46
|
-
from pathlib import Path
|
|
47
|
-
|
|
48
|
-
system = platform.system()
|
|
49
|
-
if system == "Darwin": # macOS
|
|
50
|
-
home = Path.home()
|
|
51
|
-
app_data_dir = (
|
|
52
|
-
home / "Library" / "Application Support" / "com.convexity.desktop"
|
|
53
|
-
)
|
|
54
|
-
elif system == "Windows":
|
|
55
|
-
app_data_dir = (
|
|
56
|
-
Path(os.environ.get("APPDATA", "")) / "com.convexity.desktop"
|
|
57
|
-
)
|
|
58
|
-
else: # Linux
|
|
59
|
-
home = Path.home()
|
|
60
|
-
app_data_dir = home / ".local" / "share" / "com.convexity.desktop"
|
|
61
|
-
|
|
62
|
-
settings_file = app_data_dir / "user_settings.json"
|
|
63
|
-
return settings_file if settings_file.exists() else None
|
|
64
|
-
|
|
65
|
-
except Exception as e:
|
|
66
|
-
return None
|
|
67
|
-
|
|
68
|
-
def _resolve_default_solver(self) -> str:
|
|
69
|
-
"""Resolve 'default' solver to user's preferred solver"""
|
|
70
|
-
try:
|
|
71
|
-
import json
|
|
72
|
-
|
|
73
|
-
settings_path = self._get_user_settings_path()
|
|
74
|
-
if not settings_path:
|
|
75
|
-
return "highs"
|
|
76
|
-
|
|
77
|
-
with open(settings_path, "r") as f:
|
|
78
|
-
user_settings = json.load(f)
|
|
79
|
-
|
|
80
|
-
# Get default solver from user settings
|
|
81
|
-
default_solver = user_settings.get("default_solver", "highs")
|
|
82
|
-
|
|
83
|
-
# Validate that it's a known solver
|
|
84
|
-
known_solvers = [
|
|
85
|
-
"highs",
|
|
86
|
-
"gurobi",
|
|
87
|
-
"gurobi (barrier)",
|
|
88
|
-
"gurobi (barrier homogeneous)",
|
|
89
|
-
"gurobi (barrier+crossover balanced)",
|
|
90
|
-
"gurobi (dual simplex)",
|
|
91
|
-
"mosek",
|
|
92
|
-
"mosek (default)",
|
|
93
|
-
"mosek (barrier)",
|
|
94
|
-
"mosek (barrier+crossover)",
|
|
95
|
-
"mosek (dual simplex)",
|
|
96
|
-
"copt",
|
|
97
|
-
"copt (barrier)",
|
|
98
|
-
"copt (barrier homogeneous)",
|
|
99
|
-
"copt (barrier+crossover)",
|
|
100
|
-
"copt (dual simplex)",
|
|
101
|
-
"copt (concurrent)",
|
|
102
|
-
"cplex",
|
|
103
|
-
"glpk",
|
|
104
|
-
"cbc",
|
|
105
|
-
"scip",
|
|
106
|
-
]
|
|
107
|
-
|
|
108
|
-
if default_solver in known_solvers:
|
|
109
|
-
return default_solver
|
|
110
|
-
else:
|
|
111
|
-
return "highs"
|
|
112
|
-
|
|
113
|
-
except Exception as e:
|
|
114
|
-
return "highs"
|
|
115
|
-
|
|
116
41
|
def solve_network(
|
|
117
42
|
self,
|
|
118
43
|
network: "pypsa.Network",
|
|
@@ -269,24 +194,19 @@ class NetworkSolver:
|
|
|
269
194
|
custom_solver_config: Optional[Dict[str, Any]] = None,
|
|
270
195
|
) -> tuple[str, Optional[Dict[str, Any]]]:
|
|
271
196
|
"""
|
|
272
|
-
Get the actual solver name and options for
|
|
197
|
+
Get the actual solver name and options for solver configurations.
|
|
273
198
|
|
|
274
199
|
Args:
|
|
275
|
-
solver_name: The solver name (e.g., '
|
|
200
|
+
solver_name: The solver name (e.g., 'highs', 'gurobi', 'custom')
|
|
276
201
|
solver_options: Optional additional solver options
|
|
277
|
-
custom_solver_config:
|
|
202
|
+
custom_solver_config: Custom solver configuration (from frontend)
|
|
278
203
|
Format: {"solver": "actual_solver_name", "solver_options": {...}}
|
|
279
204
|
|
|
280
205
|
Returns:
|
|
281
206
|
Tuple of (actual_solver_name, solver_options_dict)
|
|
282
207
|
"""
|
|
283
|
-
# Handle "custom" solver with custom configuration
|
|
284
|
-
if solver_name == "custom":
|
|
285
|
-
if not custom_solver_config:
|
|
286
|
-
raise ValueError(
|
|
287
|
-
"custom_solver_config must be provided when solver_name='custom'"
|
|
288
|
-
)
|
|
289
|
-
|
|
208
|
+
# Handle "custom" solver with custom configuration from frontend
|
|
209
|
+
if solver_name == "custom" and custom_solver_config:
|
|
290
210
|
if "solver" not in custom_solver_config:
|
|
291
211
|
raise ValueError(
|
|
292
212
|
"custom_solver_config must contain 'solver' key with the actual solver name"
|
|
@@ -307,294 +227,12 @@ class NetworkSolver:
|
|
|
307
227
|
|
|
308
228
|
return actual_solver, merged_options
|
|
309
229
|
|
|
310
|
-
#
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
# Handle special Gurobi configurations
|
|
317
|
-
if solver_name == "gurobi (barrier)":
|
|
318
|
-
gurobi_barrier_options = {
|
|
319
|
-
"solver_options": {
|
|
320
|
-
"Method": 2, # Barrier
|
|
321
|
-
"Crossover": 0, # Skip crossover
|
|
322
|
-
"MIPGap": 0.05, # 5% gap
|
|
323
|
-
"Threads": 0, # Use all cores (0 = auto)
|
|
324
|
-
"Presolve": 2, # Aggressive presolve
|
|
325
|
-
"ConcurrentMIP": 1, # Parallel root strategies
|
|
326
|
-
"BarConvTol": 1e-4, # Relaxed barrier convergence
|
|
327
|
-
"FeasibilityTol": 1e-5,
|
|
328
|
-
"OptimalityTol": 1e-5,
|
|
329
|
-
"NumericFocus": 1, # Improve stability
|
|
330
|
-
"PreSparsify": 1,
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
# Merge with any additional options
|
|
334
|
-
if solver_options:
|
|
335
|
-
gurobi_barrier_options.update(solver_options)
|
|
336
|
-
return "gurobi", gurobi_barrier_options
|
|
337
|
-
|
|
338
|
-
elif solver_name == "gurobi (barrier homogeneous)":
|
|
339
|
-
gurobi_barrier_homogeneous_options = {
|
|
340
|
-
"solver_options": {
|
|
341
|
-
"Method": 2, # Barrier
|
|
342
|
-
"Crossover": 0, # Skip crossover
|
|
343
|
-
"MIPGap": 0.05,
|
|
344
|
-
"Threads": 0, # Use all cores (0 = auto)
|
|
345
|
-
"Presolve": 2,
|
|
346
|
-
"ConcurrentMIP": 1,
|
|
347
|
-
"BarConvTol": 1e-4,
|
|
348
|
-
"FeasibilityTol": 1e-5,
|
|
349
|
-
"OptimalityTol": 1e-5,
|
|
350
|
-
"NumericFocus": 1,
|
|
351
|
-
"PreSparsify": 1,
|
|
352
|
-
"BarHomogeneous": 1, # Enable homogeneous barrier algorithm
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
if solver_options:
|
|
356
|
-
gurobi_barrier_homogeneous_options.update(solver_options)
|
|
357
|
-
return "gurobi", gurobi_barrier_homogeneous_options
|
|
358
|
-
|
|
359
|
-
elif solver_name == "gurobi (barrier+crossover balanced)":
|
|
360
|
-
gurobi_options_balanced = {
|
|
361
|
-
"solver_options": {
|
|
362
|
-
"Method": 2,
|
|
363
|
-
"Crossover": 1, # Dual crossover
|
|
364
|
-
"MIPGap": 0.01,
|
|
365
|
-
"Threads": 0, # Use all cores (0 = auto)
|
|
366
|
-
"Presolve": 2,
|
|
367
|
-
"Heuristics": 0.1,
|
|
368
|
-
"Cuts": 2,
|
|
369
|
-
"ConcurrentMIP": 1,
|
|
370
|
-
"BarConvTol": 1e-6,
|
|
371
|
-
"FeasibilityTol": 1e-6,
|
|
372
|
-
"OptimalityTol": 1e-6,
|
|
373
|
-
"NumericFocus": 1,
|
|
374
|
-
"PreSparsify": 1,
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
if solver_options:
|
|
378
|
-
gurobi_options_balanced.update(solver_options)
|
|
379
|
-
return "gurobi", gurobi_options_balanced
|
|
380
|
-
|
|
381
|
-
elif solver_name == "gurobi (dual simplex)":
|
|
382
|
-
gurobi_dual_options = {
|
|
383
|
-
"solver_options": {
|
|
384
|
-
"Method": 1, # Dual simplex method
|
|
385
|
-
"Threads": 0, # Use all available cores
|
|
386
|
-
"Presolve": 2, # Aggressive presolve
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
if solver_options:
|
|
390
|
-
gurobi_dual_options.update(solver_options)
|
|
391
|
-
return "gurobi", gurobi_dual_options
|
|
392
|
-
|
|
393
|
-
# Handle special Mosek configurations
|
|
394
|
-
elif solver_name == "mosek (default)":
|
|
395
|
-
# No custom options - let Mosek use its default configuration
|
|
396
|
-
mosek_default_options = {
|
|
397
|
-
"solver_options": {
|
|
398
|
-
"MSK_DPAR_MIO_REL_GAP_CONST": 0.05, # MIP relative gap tolerance (5% to match Gurobi)
|
|
399
|
-
"MSK_DPAR_MIO_MAX_TIME": 36000, # Max time 10 hours
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
if solver_options:
|
|
403
|
-
mosek_default_options["solver_options"].update(solver_options)
|
|
404
|
-
return "mosek", mosek_default_options
|
|
405
|
-
|
|
406
|
-
elif solver_name == "mosek (barrier)":
|
|
407
|
-
mosek_barrier_options = {
|
|
408
|
-
"solver_options": {
|
|
409
|
-
"MSK_IPAR_INTPNT_BASIS": 0, # Skip crossover (barrier-only) - 0 = MSK_BI_NEVER
|
|
410
|
-
"MSK_DPAR_INTPNT_TOL_REL_GAP": 1e-4, # Match Gurobi barrier tolerance
|
|
411
|
-
"MSK_DPAR_INTPNT_TOL_PFEAS": 1e-5, # Match Gurobi primal feasibility
|
|
412
|
-
"MSK_DPAR_INTPNT_TOL_DFEAS": 1e-5, # Match Gurobi dual feasibility
|
|
413
|
-
# Removed MSK_DPAR_INTPNT_TOL_INFEAS - was 1000x tighter than other tolerances!
|
|
414
|
-
"MSK_IPAR_NUM_THREADS": 0, # Use all available cores (0 = auto)
|
|
415
|
-
"MSK_IPAR_PRESOLVE_USE": 2, # Aggressive presolve (match Gurobi Presolve=2)
|
|
416
|
-
"MSK_DPAR_MIO_REL_GAP_CONST": 0.05, # Match Gurobi 5% MIP gap
|
|
417
|
-
"MSK_IPAR_MIO_ROOT_OPTIMIZER": 4, # Use interior-point for MIP root
|
|
418
|
-
"MSK_DPAR_MIO_MAX_TIME": 36000, # Max time 10 hour
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
if solver_options:
|
|
422
|
-
mosek_barrier_options["solver_options"].update(solver_options)
|
|
423
|
-
return "mosek", mosek_barrier_options
|
|
424
|
-
|
|
425
|
-
elif solver_name == "mosek (barrier+crossover)":
|
|
426
|
-
mosek_barrier_crossover_options = {
|
|
427
|
-
"solver_options": {
|
|
428
|
-
"MSK_IPAR_INTPNT_BASIS": 1, # Always crossover (1 = MSK_BI_ALWAYS)
|
|
429
|
-
"MSK_DPAR_INTPNT_TOL_REL_GAP": 1e-4, # Match Gurobi barrier tolerance (was 1e-6)
|
|
430
|
-
"MSK_DPAR_INTPNT_TOL_PFEAS": 1e-5, # Match Gurobi (was 1e-6)
|
|
431
|
-
"MSK_DPAR_INTPNT_TOL_DFEAS": 1e-5, # Match Gurobi (was 1e-6)
|
|
432
|
-
"MSK_IPAR_NUM_THREADS": 0, # Use all available cores (0 = auto)
|
|
433
|
-
"MSK_DPAR_MIO_REL_GAP_CONST": 0.05, # Match Gurobi 5% MIP gap (was 1e-6)
|
|
434
|
-
"MSK_IPAR_MIO_ROOT_OPTIMIZER": 4, # Use interior-point for MIP root
|
|
435
|
-
"MSK_DPAR_MIO_MAX_TIME": 36000, # Max time 10 hour (safety limit)
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
if solver_options:
|
|
439
|
-
mosek_barrier_crossover_options["solver_options"].update(solver_options)
|
|
440
|
-
return "mosek", mosek_barrier_crossover_options
|
|
441
|
-
|
|
442
|
-
elif solver_name == "mosek (dual simplex)":
|
|
443
|
-
mosek_dual_options = {
|
|
444
|
-
"solver_options": {
|
|
445
|
-
"MSK_IPAR_NUM_THREADS": 0, # Use all available cores (0 = automatic)
|
|
446
|
-
"MSK_IPAR_PRESOLVE_USE": 1, # Force presolve
|
|
447
|
-
"MSK_DPAR_MIO_REL_GAP_CONST": 0.05, # Match Gurobi 5% MIP gap (was 1e-6)
|
|
448
|
-
"MSK_IPAR_MIO_ROOT_OPTIMIZER": 1, # Use dual simplex for MIP root
|
|
449
|
-
"MSK_DPAR_MIO_MAX_TIME": 36000, # Max time 10 hour (safety limit)
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
if solver_options:
|
|
453
|
-
mosek_dual_options["solver_options"].update(solver_options)
|
|
454
|
-
return "mosek", mosek_dual_options
|
|
455
|
-
|
|
456
|
-
# Check if this is a known valid solver name
|
|
457
|
-
elif solver_name == "mosek":
|
|
458
|
-
# Add default MILP-friendly settings for plain Mosek
|
|
459
|
-
mosek_defaults = {
|
|
460
|
-
"solver_options": {
|
|
461
|
-
"MSK_DPAR_MIO_REL_GAP_CONST": 0.05, # Match Gurobi 5% MIP gap (was 1e-4)
|
|
462
|
-
"MSK_DPAR_MIO_MAX_TIME": 36000, # Max time 10 hours
|
|
463
|
-
"MSK_IPAR_NUM_THREADS": 0, # Use all cores (0 = auto)
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
if solver_options:
|
|
467
|
-
mosek_defaults["solver_options"].update(solver_options)
|
|
468
|
-
return solver_name, mosek_defaults
|
|
469
|
-
|
|
470
|
-
elif solver_name == "gurobi":
|
|
471
|
-
# Add default MILP-friendly settings for plain Gurobi (for consistency)
|
|
472
|
-
gurobi_defaults = {
|
|
473
|
-
"solver_options": {
|
|
474
|
-
"MIPGap": 1e-4, # 0.01% gap
|
|
475
|
-
"TimeLimit": 3600, # 1 hour
|
|
476
|
-
"Threads": 0, # Use all cores
|
|
477
|
-
"OutputFlag": 1, # Enable output
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
if solver_options:
|
|
481
|
-
gurobi_defaults["solver_options"].update(solver_options)
|
|
482
|
-
return solver_name, gurobi_defaults
|
|
483
|
-
|
|
484
|
-
# Handle special COPT configurations
|
|
485
|
-
elif solver_name == "copt (barrier)":
|
|
486
|
-
copt_barrier_options = {
|
|
487
|
-
"solver_options": {
|
|
488
|
-
"LpMethod": 2, # Barrier method
|
|
489
|
-
"Crossover": 0, # Skip crossover for speed
|
|
490
|
-
"RelGap": 0.05, # 5% MIP gap (match Gurobi)
|
|
491
|
-
"TimeLimit": 7200, # 1 hour time limit
|
|
492
|
-
"Threads": -1, # 4 threads (memory-conscious)
|
|
493
|
-
"Presolve": 3, # Aggressive presolve
|
|
494
|
-
"Scaling": 1, # Enable scaling
|
|
495
|
-
"FeasTol": 1e-5, # Match Gurobi feasibility
|
|
496
|
-
"DualTol": 1e-5, # Match Gurobi dual tolerance
|
|
497
|
-
# MIP performance settings
|
|
498
|
-
"CutLevel": 2, # Normal cut generation
|
|
499
|
-
"HeurLevel": 3, # Aggressive heuristics
|
|
500
|
-
"StrongBranching": 1, # Fast strong branching
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
if solver_options:
|
|
504
|
-
copt_barrier_options["solver_options"].update(solver_options)
|
|
505
|
-
return "copt", copt_barrier_options
|
|
506
|
-
|
|
507
|
-
elif solver_name == "copt (barrier homogeneous)":
|
|
508
|
-
copt_barrier_homogeneous_options = {
|
|
509
|
-
"solver_options": {
|
|
510
|
-
"LpMethod": 2, # Barrier method
|
|
511
|
-
"Crossover": 0, # Skip crossover
|
|
512
|
-
"BarHomogeneous": 1, # Use homogeneous self-dual form
|
|
513
|
-
"RelGap": 0.05, # 5% MIP gap
|
|
514
|
-
"TimeLimit": 3600, # 1 hour
|
|
515
|
-
"Threads": -1, # 4 threads (memory-conscious)
|
|
516
|
-
"Presolve": 3, # Aggressive presolve
|
|
517
|
-
"Scaling": 1, # Enable scaling
|
|
518
|
-
"FeasTol": 1e-5,
|
|
519
|
-
"DualTol": 1e-5,
|
|
520
|
-
# MIP performance settings
|
|
521
|
-
"CutLevel": 2, # Normal cuts
|
|
522
|
-
"HeurLevel": 3, # Aggressive heuristics
|
|
523
|
-
"StrongBranching": 1, # Fast strong branching
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
if solver_options:
|
|
527
|
-
copt_barrier_homogeneous_options["solver_options"].update(
|
|
528
|
-
solver_options
|
|
529
|
-
)
|
|
530
|
-
return "copt", copt_barrier_homogeneous_options
|
|
531
|
-
|
|
532
|
-
elif solver_name == "copt (barrier+crossover)":
|
|
533
|
-
copt_barrier_crossover_options = {
|
|
534
|
-
"solver_options": {
|
|
535
|
-
"LpMethod": 2, # Barrier method
|
|
536
|
-
"Crossover": 1, # Enable crossover for better solutions
|
|
537
|
-
"RelGap": 0.05, # 5% MIP gap (relaxed for faster solves)
|
|
538
|
-
"TimeLimit": 36000, # 10 hour
|
|
539
|
-
"Threads": -1, # Use all cores
|
|
540
|
-
"Presolve": 2, # Aggressive presolve
|
|
541
|
-
"Scaling": 1, # Enable scaling
|
|
542
|
-
"FeasTol": 1e-4, # Tighter feasibility
|
|
543
|
-
"DualTol": 1e-4, # Tighter dual tolerance
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
if solver_options:
|
|
547
|
-
copt_barrier_crossover_options["solver_options"].update(solver_options)
|
|
548
|
-
return "copt", copt_barrier_crossover_options
|
|
549
|
-
|
|
550
|
-
elif solver_name == "copt (dual simplex)":
|
|
551
|
-
copt_dual_simplex_options = {
|
|
552
|
-
"solver_options": {
|
|
553
|
-
"LpMethod": 1, # Dual simplex method
|
|
554
|
-
"RelGap": 0.05, # 5% MIP gap
|
|
555
|
-
"TimeLimit": 3600, # 1 hour
|
|
556
|
-
"Threads": -1, # Use all cores
|
|
557
|
-
"Presolve": 3, # Aggressive presolve
|
|
558
|
-
"Scaling": 1, # Enable scaling
|
|
559
|
-
"FeasTol": 1e-6,
|
|
560
|
-
"DualTol": 1e-6,
|
|
561
|
-
# MIP performance settings
|
|
562
|
-
"CutLevel": 2, # Normal cuts
|
|
563
|
-
"HeurLevel": 2, # Normal heuristics
|
|
564
|
-
"StrongBranching": 1, # Fast strong branching
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
if solver_options:
|
|
568
|
-
copt_dual_simplex_options["solver_options"].update(solver_options)
|
|
569
|
-
return "copt", copt_dual_simplex_options
|
|
570
|
-
|
|
571
|
-
elif solver_name == "copt (concurrent)":
|
|
572
|
-
copt_concurrent_options = {
|
|
573
|
-
"solver_options": {
|
|
574
|
-
"LpMethod": 4, # Concurrent (simplex + barrier)
|
|
575
|
-
"RelGap": 0.05, # 5% MIP gap
|
|
576
|
-
"TimeLimit": 3600, # 1 hour
|
|
577
|
-
"Threads": -1, # Use all cores
|
|
578
|
-
"Presolve": 3, # Aggressive presolve
|
|
579
|
-
"Scaling": 1, # Enable scaling
|
|
580
|
-
"FeasTol": 1e-5,
|
|
581
|
-
"DualTol": 1e-5,
|
|
582
|
-
# MIP performance settings
|
|
583
|
-
"CutLevel": 2, # Normal cuts
|
|
584
|
-
"HeurLevel": 3, # Aggressive heuristics
|
|
585
|
-
"StrongBranching": 1, # Fast strong branching
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
if solver_options:
|
|
589
|
-
copt_concurrent_options["solver_options"].update(solver_options)
|
|
590
|
-
return "copt", copt_concurrent_options
|
|
591
|
-
|
|
592
|
-
elif solver_name in ["highs", "cplex", "glpk", "cbc", "scip", "copt"]:
|
|
593
|
-
return solver_name, solver_options
|
|
594
|
-
|
|
595
|
-
else:
|
|
596
|
-
# Unknown solver name - fall back to highs
|
|
597
|
-
return "highs", solver_options
|
|
230
|
+
# For all other cases, pass through solver name and options directly
|
|
231
|
+
# The frontend is responsible for resolving presets and defaults
|
|
232
|
+
if solver_options:
|
|
233
|
+
return solver_name, {"solver_options": solver_options}
|
|
234
|
+
|
|
235
|
+
return solver_name, None
|
|
598
236
|
|
|
599
237
|
def _detect_constraint_type(self, constraint_code: str) -> str:
|
|
600
238
|
"""
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.5.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/data/schema/01_core_schema.sql
RENAMED
|
File without changes
|
{pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/data/schema/02_data_metadata.sql
RENAMED
|
File without changes
|
{pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/data/schema/03_validation_data.sql
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyconvexity-0.5.0 → pyconvexity-0.5.0.post1}/src/pyconvexity/solvers/pypsa/clearing_price.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|