d0fus 2.3.0__tar.gz → 2.3.2__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.
@@ -60,6 +60,7 @@ if __name__ != "__main__":
60
60
  f_CS_ACAD, f_CS_refined, f_CS_CIRCE,
61
61
  F_CIRCE0D, compute_von_mises_stress,
62
62
  calculate_E_mag_TF,
63
+ Number_TF_coils,
63
64
  )
64
65
  from .D0FUS_parameterization import DEFAULT_CONFIG, E_ELEM
65
66
 
@@ -90,6 +91,7 @@ else:
90
91
  f_CS_ACAD, f_CS_refined, f_CS_CIRCE,
91
92
  F_CIRCE0D, compute_von_mises_stress,
92
93
  calculate_E_mag_TF,
94
+ Number_TF_coils,
93
95
  )
94
96
  from D0FUS_BIB.D0FUS_parameterization import DEFAULT_CONFIG, E_ELEM
95
97
 
@@ -4278,6 +4280,193 @@ def plot_CICC_cross_section(
4278
4280
  # Stand-alone execution — smoke test
4279
4281
  # =============================================================================
4280
4282
 
4283
+
4284
+ # =============================================================================
4285
+ # 7. TF coil number - ripple and port-access constraints
4286
+ # =============================================================================
4287
+
4288
+ # D0FUS-consistent palette (matches the hex codes used elsewhere in this file)
4289
+ _RIP_C_PLASMA = "#f4a582" # plasma fill
4290
+ _RIP_C_COIL = "#2166ac" # TF coil footprint
4291
+ _RIP_C_ACCESS = "#4dac26" # maintenance / heating access sector
4292
+
4293
+
4294
+ def plot_tf_ripple(
4295
+ R0: float = 6.2,
4296
+ a: float = 2.0,
4297
+ b: float = 1.2,
4298
+ ripple_adm: float = 0.01,
4299
+ L_min: float = 3.6,
4300
+ grid: int = 460,
4301
+ save_dir: str | None = None,
4302
+ ) -> None:
4303
+ """
4304
+ Top-view map of the toroidal field magnitude produced by the discrete TF
4305
+ coils, illustrating the field ripple.
4306
+
4307
+ The coil number, ripple and outboard standoff (N_coil, ripple, Delta_ext)
4308
+ are taken from the package routine ``Number_TF_coils`` so the figure stays
4309
+ consistent with the D0FUS sizing. Each coil is modelled as a pair of
4310
+ infinite straight filaments (inner leg at R0 - a - b carrying +I, outer leg
4311
+ at R0 + a + b + Delta_ext carrying -I, at the same toroidal angle); the field
4312
+ magnitude |B| is evaluated on a Cartesian grid in the toroidal mid-plane.
4313
+ The prefactor mu0*I/(2*pi) is dropped, so |B| is in arbitrary units. The
4314
+ iso-|B| contours scallop near the coils: that scalloping is the ripple.
4315
+
4316
+ Parameters
4317
+ ----------
4318
+ R0, a : float Major and minor radius [m].
4319
+ b : float Aggregated inboard build (FW + blanket + shield + gaps) [m].
4320
+ ripple_adm : float Admissible ripple fraction (default 1 %).
4321
+ L_min : float Minimum toroidal access per coil [m] (default 3.6 m, the
4322
+ ITER-reproducing value; the manuscript also uses 3.0 m).
4323
+ grid : int Number of grid points per axis for the field map.
4324
+ save_dir : str or None
4325
+
4326
+ References
4327
+ ----------
4328
+ Wesson, Tokamaks, 4th ed., p.169 - ripple model.
4329
+ Goldston & Rutherford, Introduction to Plasma Physics, IOP (1995), ch.14.
4330
+ """
4331
+ n_coil, ripple_sel, Delta_ext = Number_TF_coils(R0, a, b, ripple_adm, L_min)
4332
+ R_in = R0 - a - b
4333
+ R_out = R0 + a + b + Delta_ext
4334
+ theta = 2.0 * np.pi * np.arange(n_coil) / n_coil
4335
+
4336
+ # filament positions (inner + outer legs) and signed currents
4337
+ fx = np.concatenate([R_in * np.cos(theta), R_out * np.cos(theta)])
4338
+ fy = np.concatenate([R_in * np.sin(theta), R_out * np.sin(theta)])
4339
+ cur = np.concatenate([np.ones(n_coil), -np.ones(n_coil)])
4340
+
4341
+ # |B| on a Cartesian grid (loop over filaments to limit memory)
4342
+ L = R_out + 0.7
4343
+ gx = np.linspace(-L, L, grid)
4344
+ X, Y = np.meshgrid(gx, gx)
4345
+ Bx = np.zeros_like(X)
4346
+ By = np.zeros_like(X)
4347
+ dmin = np.full(X.shape, np.inf)
4348
+ for xi, yi, ci in zip(fx, fy, cur):
4349
+ dx = X - xi
4350
+ dy = Y - yi
4351
+ d2 = dx * dx + dy * dy
4352
+ Bx += ci * (-dy) / d2
4353
+ By += ci * (dx) / d2
4354
+ dmin = np.minimum(dmin, np.sqrt(d2))
4355
+ rr = np.sqrt(X ** 2 + Y ** 2)
4356
+ Bmag = np.sqrt(Bx ** 2 + By ** 2)
4357
+ # cap the colour scale to the plasma-region field, then hide near-wire
4358
+ # spikes and the central column for legibility
4359
+ vmax = np.percentile(Bmag[(rr > R0 - a) & (rr < R0 + a) & (dmin > 0.22)], 99)
4360
+ Bmag = np.ma.array(Bmag, mask=(dmin < 0.22) | (rr < R0 - a))
4361
+
4362
+ th = np.linspace(0, 2 * np.pi, 400)
4363
+ fig, ax = plt.subplots(figsize=(6.4, 5.9))
4364
+ ax.set_aspect("equal")
4365
+ pcm = ax.pcolormesh(X, Y, Bmag, shading="auto", cmap="magma", vmin=0, vmax=vmax)
4366
+ ax.contour(X, Y, Bmag, levels=np.linspace(0.35 * vmax, vmax, 7),
4367
+ colors="white", linewidths=0.45, alpha=0.55)
4368
+ ax.add_patch(mpatches.Circle((0, 0), R0 - a, facecolor="0.75",
4369
+ edgecolor="0.5", lw=0.8, zorder=4))
4370
+ ax.text(0, 0, "central\ncolumn", ha="center", va="center", fontsize=8,
4371
+ color="0.25", zorder=5)
4372
+ ax.plot((R0 + a) * np.cos(th), (R0 + a) * np.sin(th), color="cyan",
4373
+ lw=1.3, ls="--", alpha=0.9, zorder=4)
4374
+ ax.plot(R_out * np.cos(th), R_out * np.sin(th), color="white", lw=0.7,
4375
+ ls=":", alpha=0.5, zorder=4)
4376
+ ax.plot(R_out * np.cos(theta), R_out * np.sin(theta), "o", color="white",
4377
+ ms=5, mec="k", mew=0.5, zorder=5)
4378
+ ax.text(0, (R0 + a) + 0.15, "plasma edge", color="cyan", fontsize=8,
4379
+ ha="center", va="bottom", zorder=6)
4380
+ ax.text(-L + 0.3, L - 0.5,
4381
+ rf"$\delta_\mathrm{{ripple}} = {ripple_sel * 100:.2f}\,\%$",
4382
+ color="white", fontsize=11, va="top")
4383
+ ax.set_xlim(-L, L)
4384
+ ax.set_ylim(-L, L)
4385
+ ax.set_xlabel("x [m]", fontsize=12)
4386
+ ax.set_ylabel("y [m]", fontsize=12)
4387
+ ax.set_title(rf"Toroidal field magnitude, top view ($N_\mathrm{{coil}} = {n_coil}$)",
4388
+ fontsize=11)
4389
+ fig.colorbar(pcm, ax=ax, shrink=0.85, label=r"$|B|$ (arb. units)")
4390
+ plt.tight_layout()
4391
+ _save_or_show(fig, save_dir, "tf_ripple")
4392
+
4393
+
4394
+ def plot_port_access(
4395
+ R0: float = 6.2,
4396
+ a: float = 2.0,
4397
+ b: float = 1.2,
4398
+ ripple_adm: float = 0.01,
4399
+ L_min: float = 3.6,
4400
+ save_dir: str | None = None,
4401
+ ) -> None:
4402
+ """
4403
+ Clean top view of the tokamak showing the TF coils and the angular sectors
4404
+ left free between them for maintenance and heating access.
4405
+
4406
+ The coil number and outboard standoff come from ``Number_TF_coils`` (same
4407
+ call as plot_tf_ripple), so the layout matches the D0FUS sizing.
4408
+
4409
+ Parameters
4410
+ ----------
4411
+ R0, a, b : float Plasma geometry and aggregated inboard build [m].
4412
+ ripple_adm : float Admissible ripple fraction (for the N_coil selection).
4413
+ L_min : float Minimum toroidal pitch per coil for maintenance [m].
4414
+ save_dir : str or None
4415
+ """
4416
+ n_coil, ripple_sel, Delta_ext = Number_TF_coils(R0, a, b, ripple_adm, L_min)
4417
+ R_out = R0 + a + b + Delta_ext
4418
+ theta_deg = np.degrees(2.0 * np.pi * np.arange(n_coil) / n_coil)
4419
+ pitch = 360.0 / n_coil
4420
+ coil_hw = 0.30 * pitch # illustrative coil half-width
4421
+ band = (R_out + 0.6) - (R0 + a)
4422
+
4423
+ fig, ax = plt.subplots(figsize=(6.2, 6.0))
4424
+ ax.set_aspect("equal")
4425
+
4426
+ # plasma annulus + central column
4427
+ ax.add_patch(mpatches.Wedge((0, 0), R0 + a, 0, 360, width=2 * a,
4428
+ facecolor=_RIP_C_PLASMA, alpha=0.45, lw=0, zorder=1))
4429
+ ax.add_patch(mpatches.Circle((0, 0), R0 - a, facecolor="0.85",
4430
+ edgecolor="0.6", lw=0.8, zorder=2))
4431
+ ax.text(0, 0, "central\ncolumn", ha="center", va="center", fontsize=8.5,
4432
+ color="0.3", zorder=3)
4433
+ ax.text(0, -R0, "plasma", ha="center", va="center", fontsize=9,
4434
+ color="#9c4a2f", zorder=3,
4435
+ bbox=dict(boxstyle="round,pad=0.15", fc="white", ec="none", alpha=0.7))
4436
+
4437
+ # access corridors (gaps) and coil footprints
4438
+ for t in theta_deg:
4439
+ ax.add_patch(mpatches.Wedge((0, 0), R_out + 0.6, t + coil_hw,
4440
+ t + pitch - coil_hw, width=band,
4441
+ facecolor=_RIP_C_ACCESS, alpha=0.16, lw=0,
4442
+ zorder=2))
4443
+ ax.add_patch(mpatches.Wedge((0, 0), R_out + 0.6, t - coil_hw,
4444
+ t + coil_hw, width=1.7,
4445
+ facecolor=_RIP_C_COIL, edgecolor="none",
4446
+ alpha=0.95, zorder=3))
4447
+
4448
+ # single neutral label on one access sector
4449
+ tlab = theta_deg[0] + pitch / 2
4450
+ r_lab = R0 + a + 0.9
4451
+ ax.annotate("maintenance /\nheating access",
4452
+ xy=(r_lab * np.cos(np.radians(tlab)), r_lab * np.sin(np.radians(tlab))),
4453
+ xytext=(1.12 * R_out, 1.02 * R_out), fontsize=9, color="#2f6b16",
4454
+ ha="left", arrowprops=dict(arrowstyle="->", color="#2f6b16", lw=1.0))
4455
+
4456
+ ax.plot([], [], color=_RIP_C_COIL, lw=6, label=rf"TF coils ($N = {n_coil}$)")
4457
+ ax.plot([], [], color=_RIP_C_ACCESS, lw=6, alpha=0.4, label="access sectors")
4458
+ lim = R_out + 1.6
4459
+ ax.set_xlim(-lim, lim)
4460
+ ax.set_ylim(-lim, lim)
4461
+ ax.set_xlabel("x [m]", fontsize=12)
4462
+ ax.set_ylabel("y [m]", fontsize=12)
4463
+ ax.set_title(rf"TF coils and maintenance access, top view ($N_\mathrm{{coil}} = {n_coil}$)",
4464
+ fontsize=11)
4465
+ ax.legend(fontsize=9, loc="upper left")
4466
+ plt.tight_layout()
4467
+ _save_or_show(fig, save_dir, "port_access")
4468
+
4469
+
4281
4470
  if __name__ == "__main__":
4282
4471
 
4283
4472
  parser = argparse.ArgumentParser(
@@ -101,6 +101,7 @@ class GlobalConfig:
101
101
  tau_i_e : float = 1.0 # Ion-to-electron temperature ratio T_i/T_e [-]
102
102
  # 1.0 -> single-temperature plasma (T_i = T_e).
103
103
  # Prescribed: T_i(rho) = tau_i_e * T_e(rho),
104
+ H : float = 1.0 # Confinement enhancement factor (H-factor) [-]
104
105
 
105
106
  # ── 2. Physics and operation ─────────────────────────────────────────────
106
107
  Operation_mode : str = 'Pulsed' # 'Steady-State' or 'Pulsed'
@@ -171,6 +172,7 @@ class GlobalConfig:
171
172
  # kink_parameter='q95' → q_limit ≈ 3.0–3.5 (ITER/EU-DEMO practice)
172
173
  q_limit : float = 3.0 # Kink safety factor threshold [-]
173
174
  Greenwald_limit : float = 1.0 # Greenwald density fraction limit [-]
175
+ Ip_limit : float = None # Upper bound on plasma current [MA]; None = no ceiling (GA)
174
176
  ms : float = 0.3 # Vertical stability margin parameter [-]
175
177
 
176
178
  # ── 4. Plasma composition ────────────────────────────────────────────────
@@ -395,7 +395,10 @@ def _summarize_cloud_per_gen(ga_cloud):
395
395
 
396
396
  def compute_stability_penalty(nbar_line, nG, betaT, betaN, q_kink,
397
397
  q_min=DEFAULT_CONFIG.q_limit,
398
+ betaN_limit=DEFAULT_CONFIG.betaN_limit,
399
+ Ip=None, Ip_limit=None,
398
400
  penalty_step=2.0,
401
+ penalty_lin=10.0,
399
402
  penalty_slope=20.0,
400
403
  margin_width=0.05,
401
404
  margin_amplitude=0.15):
@@ -425,12 +428,12 @@ def compute_stability_penalty(nbar_line, nG, betaT, betaN, q_kink,
425
428
  fitness is multiplied by 1.15 (a 15% cost surcharge).
426
429
  At 5% margin, penalty → 0 (no surcharge).
427
430
 
428
- penalty_per_constraint (violation) = penalty_step + penalty_slope × excess²
431
+ penalty_per_constraint (violation) = penalty_step + penalty_lin × excess + penalty_slope × excess²
429
432
  penalty_per_constraint (margin zone) = margin_amplitude × (1 − margin)³
430
433
 
431
434
  Examples (per constraint, violation side):
432
435
  At boundary (ε → 0⁺): 2.0 → multiplier ≈ 3.0
433
- At 10% excess: 2.0 + 0.2 = 2.2 → multiplier ≈ 3.2
436
+ At 10% excess: 2.0 + 1.0 + 0.2 = 3.2 → multiplier ≈ 4.2
434
437
 
435
438
  Examples (per constraint, margin zone):
436
439
  At 0% margin (just feasible): 0.15 → multiplier ≈ 1.15
@@ -456,6 +459,11 @@ def compute_stability_penalty(nbar_line, nG, betaT, betaN, q_kink,
456
459
  penalty_step : float
457
460
  Discontinuity cost at the stability boundary. A value of 2.0
458
461
  means the fitness is multiplied by ≥ 3 for any violation.
462
+ penalty_lin : float
463
+ Linear growth coefficient beyond the boundary. Provides a non-zero
464
+ slope AT the boundary so a barely-infeasible design is pulled back to
465
+ the limit; must exceed ~3x the cost elasticity to remove the infeasible
466
+ local minimum.
459
467
  penalty_slope : float
460
468
  Quadratic growth coefficient beyond the boundary.
461
469
  margin_width : float
@@ -471,7 +479,7 @@ def compute_stability_penalty(nbar_line, nG, betaT, betaN, q_kink,
471
479
  density_ratio = nbar_line / nG
472
480
  if density_ratio > 1.0:
473
481
  excess = density_ratio - 1.0
474
- penalty = penalty_step + penalty_slope * excess ** 2
482
+ penalty = penalty_step + penalty_lin * excess + penalty_slope * excess ** 2
475
483
  penalty_terms.append(penalty)
476
484
  violations['density'] = {
477
485
  'value': float(nbar_line), 'limit': float(nG),
@@ -481,26 +489,40 @@ def compute_stability_penalty(nbar_line, nG, betaT, betaN, q_kink,
481
489
  margin_used = (density_ratio - (1.0 - margin_width)) / margin_width
482
490
  penalty_terms.append(margin_amplitude * margin_used ** 3)
483
491
 
484
- # Beta limit: betaT (in %) < betaN (in %)
485
- betaT_percent = betaT * 100
486
- if betaN > 0:
487
- beta_ratio = betaT_percent / betaN
492
+ # Troyon beta limit: normalised beta_N must stay below betaN_limit
493
+ if betaN_limit > 0:
494
+ beta_ratio = betaN / betaN_limit
488
495
  if beta_ratio > 1.0:
489
496
  excess = beta_ratio - 1.0
490
- penalty = penalty_step + penalty_slope * excess ** 2
497
+ penalty = penalty_step + penalty_lin * excess + penalty_slope * excess ** 2
491
498
  penalty_terms.append(penalty)
492
499
  violations['beta'] = {
493
- 'value': float(betaT_percent), 'limit': float(betaN),
500
+ 'value': float(betaN), 'limit': float(betaN_limit),
494
501
  'ratio': float(beta_ratio)
495
502
  }
496
503
  elif beta_ratio > (1.0 - margin_width):
497
504
  margin_used = (beta_ratio - (1.0 - margin_width)) / margin_width
498
505
  penalty_terms.append(margin_amplitude * margin_used ** 3)
499
506
 
507
+ # Plasma-current ceiling: Ip must stay below Ip_limit (disruption headroom)
508
+ if Ip is not None and isinstance(Ip_limit, (int, float)) and Ip_limit > 0:
509
+ ip_ratio = Ip / Ip_limit
510
+ if ip_ratio > 1.0:
511
+ excess = ip_ratio - 1.0
512
+ penalty = penalty_step + penalty_lin * excess + penalty_slope * excess ** 2
513
+ penalty_terms.append(penalty)
514
+ violations['Ip'] = {
515
+ 'value': float(Ip), 'limit': float(Ip_limit),
516
+ 'ratio': float(ip_ratio)
517
+ }
518
+ elif ip_ratio > (1.0 - margin_width):
519
+ margin_used = (ip_ratio - (1.0 - margin_width)) / margin_width
520
+ penalty_terms.append(margin_amplitude * margin_used ** 3)
521
+
500
522
  # Kink safety factor: q_kink > q_min (q_kink = q* or q95)
501
523
  if q_kink < q_min:
502
524
  deficit = (q_min - q_kink) / q_min
503
- penalty = penalty_step + penalty_slope * deficit ** 2
525
+ penalty = penalty_step + penalty_lin * deficit + penalty_slope * deficit ** 2
504
526
  penalty_terms.append(penalty)
505
527
  violations['q_kink'] = {
506
528
  'value': float(q_kink), 'limit': float(q_min),
@@ -609,6 +631,7 @@ def evaluate_individual(individual, verbose=False):
609
631
  R0_abcd = _safe_real(output[_IDX['r_d']])
610
632
  c_TF = _safe_real(output[_IDX['c']])
611
633
  d_CS = _safe_real(output[_IDX['d']])
634
+ Ip = _safe_real(output[_IDX['Ip']])
612
635
 
613
636
  # Select kink parameter (q* or q95) for stability checks.
614
637
  _kink_param = static_inputs.get('kink_parameter', DEFAULT_CONFIG.kink_parameter)
@@ -628,7 +651,9 @@ def evaluate_individual(individual, verbose=False):
628
651
  q_lim = static_inputs.get('q_limit', DEFAULT_CONFIG.q_limit)
629
652
  is_stable, penalty_multiplier, violations = compute_stability_penalty(
630
653
  nbar_line, nG, betaT, betaN, q_kink,
631
- q_min=q_lim
654
+ q_min=q_lim,
655
+ betaN_limit=static_inputs.get('betaN_limit', DEFAULT_CONFIG.betaN_limit),
656
+ Ip=Ip, Ip_limit=static_inputs.get('Ip_limit', DEFAULT_CONFIG.Ip_limit)
632
657
  )
633
658
 
634
659
  # ── Compute fitness value based on selected objective ────────────
@@ -2602,11 +2627,11 @@ def run_genetic_optimization(input_file,
2602
2627
 
2603
2628
  # Check stability (using line-averaged density for Greenwald comparison)
2604
2629
  q_lim = static_inputs.get('q_limit', DEFAULT_CONFIG.q_limit)
2630
+ betaN_lim = static_inputs.get('betaN_limit', DEFAULT_CONFIG.betaN_limit)
2631
+ Ip_lim = static_inputs.get('Ip_limit', DEFAULT_CONFIG.Ip_limit)
2605
2632
  is_stable, _, violations = compute_stability_penalty(
2606
- nbar_line, nG, betaT, betaN, q_kink, q_min=q_lim)
2607
-
2608
- # betaT is fraction, convert to % for display
2609
- betaT_percent = betaT * 100
2633
+ nbar_line, nG, betaT, betaN, q_kink, q_min=q_lim, betaN_limit=betaN_lim,
2634
+ Ip=Ip, Ip_limit=Ip_lim)
2610
2635
 
2611
2636
  # Compute Sheffield COE for the best design (regardless of objective)
2612
2637
  _COE_best = np.nan
@@ -2650,7 +2675,9 @@ def run_genetic_optimization(input_file,
2650
2675
  print(f" Ip: {Ip:.2f} MA")
2651
2676
  print(f" P_elec: {P_elec:.1f} MW")
2652
2677
  print(f" n_line/nG: {nbar_line/nG:.3f} ({(1-nbar_line/nG)*100:+.1f}% margin)")
2653
- print(f" betaT/betaN: {betaT_percent/betaN:.3f} ({(1-betaT_percent/betaN)*100:+.1f}% margin)")
2678
+ print(f" betaN/betaN_limit: {betaN/betaN_lim:.3f} ({(1-betaN/betaN_lim)*100:+.1f}% margin)")
2679
+ if Ip_lim is not None:
2680
+ print(f" Ip/Ip_limit: {Ip/Ip_lim:.3f} ({(1-Ip/Ip_lim)*100:+.1f}% margin)")
2654
2681
  q_lim = static_inputs.get('q_limit', DEFAULT_CONFIG.q_limit)
2655
2682
  print(f" {_q_label}/q_limit: {q_kink/q_lim:.3f} ({(q_kink/q_lim-1)*100:+.1f}% margin)")
2656
2683
  print(f" c_TF: {c_TF:.3f} m d_CS: {d_CS:.3f} m r_d: {r_d:.3f} m")
@@ -2764,7 +2791,8 @@ def run_genetic_optimization(input_file,
2764
2791
  "n_line_over_nG": to_serializable(nbar_line/nG),
2765
2792
  "q_kink_over_qlim": to_serializable(q_kink/q_lim),
2766
2793
  "kink_parameter": _kink_param,
2767
- "betaT_over_betaN": to_serializable(betaT_percent/betaN) if betaN > 0 else None
2794
+ "betaN_over_betaN_limit": to_serializable(betaN/betaN_lim) if betaN_lim > 0 else None,
2795
+ "Ip_over_Ip_limit": to_serializable(Ip/Ip_lim) if Ip_lim is not None else None
2768
2796
  },
2769
2797
  "radial_build": {
2770
2798
  "c_TF_m": to_serializable(c_TF),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: d0fus
3
- Version: 2.3.0
3
+ Version: 2.3.2
4
4
  Summary: Design 0-dimensional for Fusion Systems - Tokamak power plant design and optimization tool
5
5
  Author-email: Timothé Auclair <timothe.auclair@gmail.com>
6
6
  Maintainer-email: Timothé Auclair <timothe.auclair@gmail.com>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: d0fus
3
- Version: 2.3.0
3
+ Version: 2.3.2
4
4
  Summary: Design 0-dimensional for Fusion Systems - Tokamak power plant design and optimization tool
5
5
  Author-email: Timothé Auclair <timothe.auclair@gmail.com>
6
6
  Maintainer-email: Timothé Auclair <timothe.auclair@gmail.com>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "d0fus"
7
- version = "2.3.0"
7
+ version = "2.3.2"
8
8
  description = "Design 0-dimensional for Fusion Systems - Tokamak power plant design and optimization tool"
9
9
  readme = "README.md"
10
10
  license = {text = "CeCILL-C"}
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