pyconvexity 0.4.9.post1__py3-none-any.whl → 0.5.0.post1__py3-none-any.whl

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/_version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.4.9.post1"
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 special solver configurations.
197
+ Get the actual solver name and options for solver configurations.
273
198
 
274
199
  Args:
275
- solver_name: The solver name (e.g., 'gurobi (barrier)', 'highs', 'custom')
200
+ solver_name: The solver name (e.g., 'highs', 'gurobi', 'custom')
276
201
  solver_options: Optional additional solver options
277
- custom_solver_config: Optional custom solver configuration for solver_name='custom'
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
- # Handle "default" solver
311
- if solver_name == "default":
312
- # Try to read user's default solver preference
313
- actual_solver = self._resolve_default_solver()
314
- return actual_solver, solver_options
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
  """
@@ -829,6 +829,7 @@ class ResultStorage:
829
829
  # 1. GENERATORS - Generation dispatch (including UNMET_LOAD)
830
830
  if hasattr(network, "generators_t") and hasattr(network.generators_t, "p"):
831
831
  # Get generator-carrier mapping (include both GENERATOR and UNMET_LOAD)
832
+ # Use LEFT JOIN to include UNMET_LOAD components even if they don't have a carrier_id
832
833
  cursor = conn.execute(
833
834
  """
834
835
  SELECT c.name as component_name,
@@ -837,7 +838,7 @@ class ResultStorage:
837
838
  ELSE carr.name
838
839
  END as carrier_name
839
840
  FROM components c
840
- JOIN carriers carr ON c.carrier_id = carr.id
841
+ LEFT JOIN carriers carr ON c.carrier_id = carr.id
841
842
  WHERE c.component_type IN ('GENERATOR', 'UNMET_LOAD')
842
843
  """
843
844
  )
@@ -1006,19 +1007,16 @@ class ResultStorage:
1006
1007
  end_year = build_year + lifetime - 1
1007
1008
  return current_year <= end_year
1008
1009
 
1009
- # 1. GENERATORS - Capital costs (including UNMET_LOAD)
1010
+ # 1. GENERATORS - Capital costs (excluding UNMET_LOAD)
1010
1011
  if hasattr(network, "generators") and not network.generators.empty:
1011
- # Get generator info: carrier, capital_cost, build_year, lifetime (include UNMET_LOAD)
1012
+ # Get generator info: carrier, capital_cost, build_year, lifetime
1013
+ # EXCLUDE UNMET_LOAD - their capital cost is not meaningful (usually $0)
1012
1014
  cursor = conn.execute(
1013
1015
  """
1014
- SELECT c.name as component_name,
1015
- CASE
1016
- WHEN c.component_type = 'UNMET_LOAD' THEN 'Unmet Load'
1017
- ELSE carr.name
1018
- END as carrier_name
1016
+ SELECT c.name as component_name, carr.name as carrier_name
1019
1017
  FROM components c
1020
- JOIN carriers carr ON c.carrier_id = carr.id
1021
- WHERE c.component_type IN ('GENERATOR', 'UNMET_LOAD')
1018
+ LEFT JOIN carriers carr ON c.carrier_id = carr.id
1019
+ WHERE c.component_type = 'GENERATOR'
1022
1020
  """
1023
1021
  )
1024
1022
  generator_carriers = {row[0]: row[1] for row in cursor.fetchall()}
@@ -1297,19 +1295,16 @@ class ResultStorage:
1297
1295
  # Operational costs = dispatch (MWh) × marginal_cost (currency/MWh)
1298
1296
  # Only for components that are active in this year
1299
1297
 
1300
- # 1. GENERATORS - Operational costs (including UNMET_LOAD)
1298
+ # 1. GENERATORS - Operational costs (excluding UNMET_LOAD)
1301
1299
  if hasattr(network, "generators_t") and hasattr(network.generators_t, "p"):
1302
- # Get generator info: carrier, marginal_cost, build_year, lifetime (include UNMET_LOAD)
1300
+ # Get generator info: carrier, marginal_cost, build_year, lifetime
1301
+ # EXCLUDE UNMET_LOAD - their marginal cost is a penalty price, not a real operational cost
1303
1302
  cursor = conn.execute(
1304
1303
  """
1305
- SELECT c.name as component_name,
1306
- CASE
1307
- WHEN c.component_type = 'UNMET_LOAD' THEN 'Unmet Load'
1308
- ELSE carr.name
1309
- END as carrier_name
1304
+ SELECT c.name as component_name, carr.name as carrier_name
1310
1305
  FROM components c
1311
- JOIN carriers carr ON c.carrier_id = carr.id
1312
- WHERE c.component_type IN ('GENERATOR', 'UNMET_LOAD')
1306
+ LEFT JOIN carriers carr ON c.carrier_id = carr.id
1307
+ WHERE c.component_type = 'GENERATOR'
1313
1308
  """
1314
1309
  )
1315
1310
  generator_carriers = {row[0]: row[1] for row in cursor.fetchall()}
@@ -1539,19 +1534,16 @@ class ResultStorage:
1539
1534
 
1540
1535
  # Calculate capacity by carrier for this specific year
1541
1536
 
1542
- # 4. GENERATORS - Power capacity (MW) (including UNMET_LOAD)
1537
+ # 4. GENERATORS - Power capacity (MW) (excluding UNMET_LOAD)
1543
1538
  if hasattr(network, "generators") and not network.generators.empty:
1544
- # Get generator-carrier mapping (include UNMET_LOAD)
1539
+ # Get generator-carrier mapping for capacity
1540
+ # EXCLUDE UNMET_LOAD - their capacity (often infinite) is not meaningful for capacity stats
1545
1541
  cursor = conn.execute(
1546
1542
  """
1547
- SELECT c.name as component_name,
1548
- CASE
1549
- WHEN c.component_type = 'UNMET_LOAD' THEN 'Unmet Load'
1550
- ELSE carr.name
1551
- END as carrier_name
1543
+ SELECT c.name as component_name, carr.name as carrier_name
1552
1544
  FROM components c
1553
- JOIN carriers carr ON c.carrier_id = carr.id
1554
- WHERE c.component_type IN ('GENERATOR', 'UNMET_LOAD')
1545
+ LEFT JOIN carriers carr ON c.carrier_id = carr.id
1546
+ WHERE c.component_type = 'GENERATOR'
1555
1547
  """
1556
1548
  )
1557
1549
  generator_carriers = {row[0]: row[1] for row in cursor.fetchall()}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyconvexity
3
- Version: 0.4.9.post1
3
+ Version: 0.5.0.post1
4
4
  Summary: Python library for energy system modeling and optimization with PyPSA
5
5
  Author-email: Convexity Team <info@convexity.com>
6
6
  License: MIT
@@ -1,5 +1,5 @@
1
1
  pyconvexity/__init__.py,sha256=P67QJ8npf-QWmBX12im__eICLoRz8cByQ5OJXiyIBmA,5706
2
- pyconvexity/_version.py,sha256=zFQM77wgAmWBTKJkmd0BFXV3JGA69nyLLIJtFnFJo6Q,28
2
+ pyconvexity/_version.py,sha256=J1kBZAgpuK-WFUqCCzGZrBt454s_I5sVWXeEXZ7DyLQ,28
3
3
  pyconvexity/dashboard.py,sha256=7x04Hr-EwzTAf-YJdHzfV83Gf2etltwtzwh_bCYJ5lk,8579
4
4
  pyconvexity/timeseries.py,sha256=QdKbiqjAlxkJATyKm2Kelx1Ea2PsAnnCYfVLU5VER1Y,11085
5
5
  pyconvexity/core/__init__.py,sha256=gdyyHNqOc4h9Nfe9u6NA936GNzH6coGNCMgBvvvOnGE,1196
@@ -34,11 +34,11 @@ pyconvexity/solvers/pypsa/batch_loader.py,sha256=ZgOcZqMnMS3TOYTq2Ly2O4cuwhNNAic
34
34
  pyconvexity/solvers/pypsa/builder.py,sha256=1ZU68Wtl_jQSXHzspKQDkR6bxAVU1nKvPfnPUl0aO3k,23256
35
35
  pyconvexity/solvers/pypsa/clearing_price.py,sha256=HdAk7GPfJFVI4t6mL0zQGEOMAvuyfpl0yNCnah1ZGH0,29164
36
36
  pyconvexity/solvers/pypsa/constraints.py,sha256=20WliFDhPQGMAsS4VOTU8LZJpsFpLVRHpNsZW49GTcc,16397
37
- pyconvexity/solvers/pypsa/solver.py,sha256=iVgDsmYK7AvutkpuNoW_DoLAKODFJTFA2W1cCo1yhPQ,60427
38
- pyconvexity/solvers/pypsa/storage.py,sha256=KcHIK3wgYjs7oV4tAqWU3HnxJcC_5MoylLoPuAECmDU,95195
37
+ pyconvexity/solvers/pypsa/solver.py,sha256=pNI9ch0vO5q-8mWc3RHTscWB_ymj4s2lVJQ_e2nbzHY,44417
38
+ pyconvexity/solvers/pypsa/storage.py,sha256=nbONOBnunq3tyexa5yDUsT9xdxieUfrqhoM76_2HIGg,94956
39
39
  pyconvexity/validation/__init__.py,sha256=VJNZlFoWABsWwUKktNk2jbtXIepH5omvC0WtsTS7o3o,583
40
40
  pyconvexity/validation/rules.py,sha256=GiNadc8hvbWBr09vUkGiLLTmSdvtNSeGLFwvCjlikYY,9241
41
- pyconvexity-0.4.9.post1.dist-info/METADATA,sha256=IcOxAbx3C6EXGDxn6LkqQzCrxagzhY26F_aNg0VhX_g,4973
42
- pyconvexity-0.4.9.post1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
43
- pyconvexity-0.4.9.post1.dist-info/top_level.txt,sha256=wFPEDXVaebR3JO5Tt3HNse-ws5aROCcxEco15d6j64s,12
44
- pyconvexity-0.4.9.post1.dist-info/RECORD,,
41
+ pyconvexity-0.5.0.post1.dist-info/METADATA,sha256=ww2Vp1jYb4upZigNRSZ4augdGBQWBGfokrvFyPm_Vq8,4973
42
+ pyconvexity-0.5.0.post1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
43
+ pyconvexity-0.5.0.post1.dist-info/top_level.txt,sha256=wFPEDXVaebR3JO5Tt3HNse-ws5aROCcxEco15d6j64s,12
44
+ pyconvexity-0.5.0.post1.dist-info/RECORD,,