Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 37 additions & 13 deletions process/availability.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,11 +356,46 @@ def avail_2(self, output: bool):
1.0e0 - (u_planned + u_unplanned + u_planned * u_unplanned), 0.0e0
)

# Modify lifetimes to take account of the availability
if ifev.ife != 1:
# First wall / blanket
if fwbsv.bktlife < cv.tlife:
fwbsv.bktlife = min(fwbsv.bktlife / cv.cfactr, cv.tlife)
# Current drive system lifetime (assumed equal to first wall and blanket lifetime)
cv.cdrlife = fwbsv.bktlife
Comment thread
chris-ashe marked this conversation as resolved.

# Divertor
if cv.divlife < cv.tlife:
cv.divlife = min(cv.divlife / cv.cfactr, cv.tlife)

# Centrepost
if pv.itart == 1 and cv.cplife < cv.tlife:
cv.cplife = min(cv.cplife / cv.cfactr, cv.tlife)

# Capacity factor
cpfact = cv.cfactr * (tv.tburn / tv.tcycle)
cv.cpfact = cv.cfactr * (tv.tburn / tv.tcycle)

# Output
if output:
po.ovarre(
self.outfile,
"First wall / blanket lifetime (FPY)",
"(bktlife)",
fwbsv.bktlife,
"OP ",
)
po.ovarre(
self.outfile, "Divertor lifetime (FPY)", "(divlife)", cv.divlife, "OP "
)
if pv.itart == 1:
po.ovarre(
self.outfile,
"Centrepost lifetime (FPY)",
"(cplife)",
cv.cplife,
"OP ",
)
po.oblnkl(self.outfile)
po.ocmmnt(self.outfile, "Total unavailability:")
po.oblnkl(self.outfile)
po.ovarre(
Expand Down Expand Up @@ -397,7 +432,7 @@ def avail_2(self, output: bool):
self.outfile,
"Capacity factor: total lifetime elec. energy output / output power",
"(cpfact)",
cpfact,
cv.cpfact,
"OP ",
)

Expand Down Expand Up @@ -501,17 +536,6 @@ def calc_u_planned(self, output: bool) -> float:
"(adivflnc)",
cv.adivflnc,
)
po.ovarre(
self.outfile,
"First wall / blanket lifetime (FPY)",
"(bktlife)",
fwbsv.bktlife,
"OP ",
)
po.ovarre(
self.outfile, "Divertor lifetime (FPY)", "(divlife)", cv.divlife, "OP "
)

po.ovarin(
self.outfile,
"Number of remote handling systems",
Expand Down
56 changes: 41 additions & 15 deletions process/costs.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ def run(self):
to account for Nth-of-a-kind cost reductions.
<P>The code is arranged in the order of the standard accounts.
"""
# Convert FPY component lifetimes to calendar years
# for replacment components
self.convert_fpy_to_calendar()

self.acc21()

# Account 22 : Fusion power island
Expand Down Expand Up @@ -125,23 +129,23 @@ def output(self):
po.ovarrf(
self.outfile,
"First wall / blanket life (years)",
"(fwbllife)",
fwbs_variables.bktlife,
"(bktlife_cal)",
fwbs_variables.bktlife_cal,
)

if ife_variables.ife != 1:
po.ovarrf(
self.outfile,
"Divertor life (years)",
"(divlife.)",
cost_variables.divlife,
"(divlife_cal)",
cost_variables.divlife_cal,
)
if physics_variables.itart == 1:
po.ovarrf(
self.outfile,
"Centrepost life (years)",
"(cplife.)",
cost_variables.cplife,
"(cplife_cal)",
cost_variables.cplife_cal,
)

po.ovarrf(
Expand Down Expand Up @@ -2620,13 +2624,9 @@ def coelc(self):
# Costs due to first wall and blanket renewal
# ===========================================

# Operational life

fwbllife = fwbs_variables.bktlife

# Compound interest factor

feffwbl = (1.0e0 + cost_variables.discount_rate) ** fwbllife
feffwbl = (1.0e0 + cost_variables.discount_rate) ** fwbs_variables.bktlife_cal

# Capital recovery factor

Expand All @@ -2642,7 +2642,7 @@ def coelc(self):
)

if cost_variables.ifueltyp == 2:
annfwbl = annfwbl * (1.0e0 - fwbllife / cost_variables.tlife)
annfwbl = annfwbl * (1.0e0 - fwbs_variables.bktlife / cost_variables.tlife)

# Cost of electricity due to first wall/blanket replacements

Expand All @@ -2657,7 +2657,9 @@ def coelc(self):
else:
# Compound interest factor

fefdiv = (1.0e0 + cost_variables.discount_rate) ** cost_variables.divlife
fefdiv = (
1.0e0 + cost_variables.discount_rate
) ** cost_variables.divlife_cal

# Capital recovery factor

Expand Down Expand Up @@ -2687,7 +2689,7 @@ def coelc(self):
if (physics_variables.itart == 1) and (ife_variables.ife != 1):
# Compound interest factor

fefcp = (1.0e0 + cost_variables.discount_rate) ** cost_variables.cplife
fefcp = (1.0e0 + cost_variables.discount_rate) ** cost_variables.cplife_cal

# Capital recovery factor

Expand Down Expand Up @@ -2717,7 +2719,7 @@ def coelc(self):

# Compound interest factor

fefcdr = (1.0e0 + cost_variables.discount_rate) ** cost_variables.cdrlife
fefcdr = (1.0e0 + cost_variables.discount_rate) ** cost_variables.cdrlife_cal

# Capital recovery factor

Expand Down Expand Up @@ -2870,3 +2872,27 @@ def coelc(self):
+ cost_variables.coeoam
+ coedecom
)

@staticmethod
def convert_fpy_to_calendar() -> None:
"""
Routine to convert component lifetimes in FPY to calendar years.
Required for replacement component costs.
Author: J Foster, CCFE, Culham Campus
"""
# FW/Blanket and HCD
if fwbs_variables.bktlife < cost_variables.tlife:
fwbs_variables.bktlife_cal = fwbs_variables.bktlife * cost_variables.cfactr
# Current drive system lifetime (assumed equal to first wall and blanket lifetime)
cost_variables.cdrlife_cal = fwbs_variables.bktlife_cal

# Divertor
if cost_variables.divlife < cost_variables.tlife:
cost_variables.divlife_cal = cost_variables.divlife * cost_variables.cfactr

# Centrepost
if (
physics_variables.itart == 1
and cost_variables.cplife < cost_variables.tlife
):
cost_variables.cplife_cal = cost_variables.cplife * cost_variables.cfactr
14 changes: 13 additions & 1 deletion source/fortran/cost_variables.f90
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ module cost_variables
!! total plant direct cost (M$)

real(dp) :: cdrlife
!! lifetime of heating/current drive system (y)
!! Full power year lifetime of heating/current drive system (y)

real(dp) :: cdrlife_cal
!! Calendar year lifetime of heating/current drive system (y)

real(dp) :: cfactr
!! Total plant availability fraction; input if `iavail=0`
Expand Down Expand Up @@ -140,6 +143,9 @@ module cost_variables
real(dp) :: cplife
!! Calculated full power year lifetime of centrepost (years)

real(dp) :: cplife_cal
!! Calculated calendar year lifetime of centrepost (years)

real(dp) :: cpstcst
!! ST centrepost direct cost (M$)

Expand Down Expand Up @@ -167,6 +173,9 @@ module cost_variables
real(dp) :: divlife
!! Full power lifetime of divertor (y)

real(dp) :: divlife_cal
!! Calendar year lifetime of divertor (y)

real(dp) :: dtlife
!! period prior to the end of the plant life that the decommissioning fund is used (years)

Expand Down Expand Up @@ -627,6 +636,7 @@ subroutine init_cost_variables
cdcost = 0.0D0
cdirt = 0.0D0
cdrlife = 0.0D0
cdrlife_cal = 0.0D0
cfactr = 0.75D0
cpfact = 0.0D0
cfind = (/0.244D0, 0.244D0, 0.244D0, 0.29D0/)
Expand All @@ -652,6 +662,7 @@ subroutine init_cost_variables
cost_model = 1
cowner = 0.15D0
cplife = 0.0D0
cplife_cal = 0.0D0
cpstcst = 0.0D0
cpstflnc = 10.0D0
crctcore = 0.0D0
Expand All @@ -661,6 +672,7 @@ subroutine init_cost_variables
dintrt = 0.0D0
divcst = 0.0D0
divlife = 0.0D0
divlife_cal = 0.0D0
dtlife = 0.0D0
fcap0 = 1.165D0
fcap0cp = 1.08D0
Expand Down
4 changes: 4 additions & 0 deletions source/fortran/fwbs_variables.f90
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ module fwbs_variables
real(dp) :: bktlife
!! Full power blanket lifetime (years)

real(dp) :: bktlife_cal
!! Calendar year blanket lifetime (years)

real(dp) :: coolmass
!! mass of water coolant (in shield, blanket, first wall, divertor) [kg]

Expand Down Expand Up @@ -663,6 +666,7 @@ subroutine init_fwbs_variables
implicit none

bktlife = 0.0D0
bktlife_cal = 0.0D0
coolmass = 0.0D0
vvmass = 0.0D0
denstl = 7800.0D0
Expand Down
80 changes: 80 additions & 0 deletions tests/unit/test_availability.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,86 @@ def test_calc_u_unplanned_fwbs(calc_u_unplanned_fwbs_fix, availability):
assert result == calc_u_unplanned_fwbs_fix


def test_avail_2(monkeypatch, availability):
"""Test avail_2 routine

:param monkeypatch: Mock fixture
:type monkeypatch: object

:param availability: fixture containing an initialised `Availability` object
:type availability: tests.unit.test_availability.availability (functional fixture)
"""
# Mock return values for for functions called in avail_2
def mock_calc_u_planned(*args, **kwargs):
return 0.01

def mock_calc_u_unplanned_magnets(*args, **kwargs):
return 0.02

def mock_calc_u_unplanned_divertor(*args, **kwargs):
return 0.03

def mock_calc_u_unplanned_fwbs(*args, **kwargs):
return 0.04

def mock_calc_u_unplanned_bop(*args, **kwargs):
return 0.05

def mock_calc_u_unplanned_hcd(*args, **kwargs):
return 0.06

def mock_calc_u_unplanned_vacuum(*args, **kwargs):
return 0.07

# Mock module functions
monkeypatch.setattr(availability, "calc_u_planned", mock_calc_u_planned)
monkeypatch.setattr(
availability, "calc_u_unplanned_magnets", mock_calc_u_unplanned_magnets
)
monkeypatch.setattr(
availability, "calc_u_unplanned_divertor", mock_calc_u_unplanned_divertor
)
monkeypatch.setattr(
availability, "calc_u_unplanned_fwbs", mock_calc_u_unplanned_fwbs
)
monkeypatch.setattr(availability, "calc_u_unplanned_bop", mock_calc_u_unplanned_bop)
monkeypatch.setattr(availability, "calc_u_unplanned_hcd", mock_calc_u_unplanned_hcd)
monkeypatch.setattr(
availability, "calc_u_unplanned_vacuum", mock_calc_u_unplanned_vacuum
)

# Mock module variables
monkeypatch.setattr(tv, "tburn", 5.0)
monkeypatch.setattr(tv, "tcycle", 50.0)
monkeypatch.setattr(ifev, "ife", 0)
monkeypatch.setattr(pv, "itart", 1)
monkeypatch.setattr(fwbsv, "bktlife", 5.0)
monkeypatch.setattr(cv, "divlife", 10.0)
monkeypatch.setattr(cv, "cplife", 15.0)

availability.avail_2(False)

cfactr_obs = cv.cfactr
cfactr_exp = 0.7173
assert pytest.approx(cfactr_obs) == cfactr_exp

cpfact_obs = cv.cpfact
cpfact_exp = 0.07173
assert pytest.approx(cpfact_obs) == cpfact_exp

bktlife_obs = fwbsv.bktlife
bktlife_exp = 6.97058413
assert pytest.approx(bktlife_obs) == bktlife_exp

divlife_obs = cv.divlife
divlife_exp = 13.94116827
assert pytest.approx(divlife_obs) == divlife_exp

cplife_obs = cv.cplife
cplife_exp = 20.9117524
assert pytest.approx(cplife_obs) == cplife_exp


def test_avail_st(monkeypatch, availability):
"""Test avail_st routine

Expand Down
Loading