Skip to content

Commit 29122f2

Browse files
Merge pull request #86 from softwareengineerprogrammer/redundant-class-cleanup
Redundant class cleanup - SBTEconomics, SUTRAEconomics
2 parents 67bd265 + e9ab8a2 commit 29122f2

File tree

2 files changed

+9
-552
lines changed

2 files changed

+9
-552
lines changed

src/geophires_x/SBTEconomics.py

Lines changed: 2 additions & 327 deletions
Original file line numberDiff line numberDiff line change
@@ -101,67 +101,7 @@ def __init__(self, model: Model):
101101
sclass = str(__class__).replace("<class \'", "")
102102
self.MyClass = sclass.replace("\'>", "")
103103
self.MyPath = os.path.abspath(__file__)
104-
#self.Electricity_rate = None
105-
#self.Discount_rate = None
106-
#self.error = 0
107-
#self.AverageOPEX_Plant = 0
108-
#self.OPEX_Plant = 0
109-
#self.TotalCAPEX = 0
110-
#self.CAPEX_Surface_Plant = 0
111-
#self.CAPEX_Drilling = 0
112-
113-
# Set up all the Parameters that will be predefined by this class using the different types of parameter classes.
114-
# Setting up includes giving it a name, a default value, The Unit Type (length, volume, temperature, etc.)
115-
# and Unit Name of that value, sets it as required (or not), sets allowable range,
116-
# the error message if that range is exceeded, the ToolTip Text, and the name of the class that created it.
117-
# This includes setting up temporary variables that will be available to all the class but noy read in by user,
118-
# or used for Output
119-
# This also includes all Parameters that are calculated and then published using the Printouts function.
120-
# If you choose to subclass this master class, you can do so before or after you create your own parameters.
121-
# If you do, you can also choose to call this method from you class, which will effectively add
122-
# and set all these parameters to your class.
123-
# NB: inputs we already have ("already have it") need to be set at ReadParameter time so values
124-
# are set at the last possible time
125104

126-
"""
127-
self.O_and_M_cost_plant = self.ParameterDict[self.O_and_M_cost_plant.Name] = floatParameter(
128-
"Operation & Maintenance Cost of Surface Plant",
129-
DefaultValue=0.015,
130-
Min=0.0,
131-
Max=0.2,
132-
UnitType=Units.PERCENT,
133-
PreferredUnits=PercentUnit.TENTH,
134-
CurrentUnits=PercentUnit.TENTH,
135-
Required=True,
136-
ErrMessage="assume default Operation & Maintenance cost of surface plant expressed as fraction of total surface plant capital cost (0.015)"
137-
)
138-
self.Direct_use_heat_cost_per_kWth = self.ParameterDict[
139-
self.Direct_use_heat_cost_per_kWth.Name] = floatParameter(
140-
"Capital Cost for Surface Plant for Direct-use System",
141-
DefaultValue=100.0,
142-
Min=0.0,
143-
Max=10000.0,
144-
UnitType=Units.ENERGYCOST,
145-
PreferredUnits=EnergyCostUnit.DOLLARSPERKW,
146-
CurrentUnits=EnergyCostUnit.DOLLARSPERKW,
147-
Required=False,
148-
ErrMessage="assume default Capital cost for surface plant for direct-use system (100 $/kWth)"
149-
)
150-
self.Power_plant_cost_per_kWe = self.ParameterDict[self.Power_plant_cost_per_kWe.Name] = floatParameter(
151-
"Capital Cost for Power Plant for Electricity Generation",
152-
DefaultValue=3000.0,
153-
Min=0.0,
154-
Max=10000.0,
155-
UnitType=Units.ENERGYCOST,
156-
PreferredUnits=EnergyCostUnit.DOLLARSPERKW,
157-
CurrentUnits=EnergyCostUnit.DOLLARSPERKW,
158-
Required=True,
159-
ErrMessage="assume default Power plant capital cost per kWe (3000 USD/kWe)"
160-
)
161-
162-
# results are stored here and in the parent ProducedTemperature array
163-
164-
"""
165105
model.logger.info(f'complete {__class__!s}: {sys._getframe().f_code.co_name}')
166106

167107
def __str__(self):
@@ -183,10 +123,6 @@ def read_parameters(self, model: Model) -> None:
183123
# because the call to the super.readparameters will set all the variables,
184124
# including the ones that are specific to this class
185125

186-
# inputs we already have - needs to be set at ReadParameter time so values set at the latest possible time
187-
# self.Discount_rate = model.economics.discountrate.value # same units are GEOPHIRES
188-
# self.Electricity_rate = model.surfaceplant.electricity_cost_to_buy.value # same units are GEOPHIRES
189-
190126
model.logger.info(f'complete {__class__!s}: {sys._getframe().f_code.co_name}')
191127

192128
def Calculate(self, model: Model) -> None:
@@ -280,269 +216,8 @@ def Calculate(self, model: Model) -> None:
280216
self.cost_lateral_section.value + self.cost_to_junction_section.value)
281217

282218
self.Cstim.value = self.calculate_stimulation_costs(model).to(self.Cstim.CurrentUnits).magnitude
283-
284-
# field gathering system costs (M$)
285-
if self.ccgathfixed.Valid:
286-
self.Cgath.value = self.ccgathfixed.value
287-
else:
288-
self.Cgath.value = self.ccgathadjfactor.value * 50 - 6 * np.max(
289-
model.surfaceplant.HeatExtracted.value) * 1000. # (GEOPHIRES v1 correlation)
290-
if model.wellbores.impedancemodelused.value:
291-
pumphp = np.max(model.wellbores.PumpingPower.value) * 1341
292-
numberofpumps = np.ceil(pumphp / 2000) # pump can be maximum 2,000 hp
293-
if numberofpumps == 0:
294-
self.Cpumps = 0.0
295-
else:
296-
pumphpcorrected = pumphp / numberofpumps
297-
self.Cpumps = numberofpumps * 1.5 * (
298-
(1750 * pumphpcorrected ** 0.7) * 3 * pumphpcorrected ** (-0.11))
299-
else:
300-
if model.wellbores.productionwellpumping.value:
301-
prodpumphp = np.max(model.wellbores.PumpingPowerProd.value) / model.wellbores.nprod.value * 1341
302-
Cpumpsprod = model.wellbores.nprod.value * 1.5 * (1750 * prodpumphp ** 0.7 + 5750 *
303-
prodpumphp ** 0.2 + 10000 + np.max(
304-
model.wellbores.pumpdepth.value) * 50 * 3.281) # see page 46 in user's manual assuming rental of rig for 1 day.
305-
else:
306-
Cpumpsprod = 0
307-
308-
injpumphp = np.max(model.wellbores.PumpingPowerInj.value) * 1341
309-
numberofinjpumps = np.ceil(injpumphp / 2000) # pump can be maximum 2,000 hp
310-
if numberofinjpumps == 0:
311-
Cpumpsinj = 0
312-
else:
313-
injpumphpcorrected = injpumphp / numberofinjpumps
314-
Cpumpsinj = numberofinjpumps * 1.5 * (
315-
1750 * injpumphpcorrected ** 0.7) * 3 * injpumphpcorrected ** (-0.11)
316-
self.Cpumps = Cpumpsinj + Cpumpsprod
317-
318-
# Based on GETEM 2016 #1.15 for 15% contingency
319-
self.Cgath.value = 1.15 * self.ccgathadjfactor.value * self._indirect_cost_factor * (
320-
(model.wellbores.nprod.value + model.wellbores.ninj.value) * 750 * 500. + self.Cpumps) / 1E6
321-
322-
# plant costs
323-
if (model.surfaceplant.enduse_option.value == EndUseOptions.HEAT
324-
and model.surfaceplant.plant_type.value not in [PlantType.ABSORPTION_CHILLER, PlantType.HEAT_PUMP, PlantType.DISTRICT_HEATING]): # direct-use
325-
if self.ccplantfixed.Valid:
326-
self.Cplant.value = self.ccplantfixed.value
327-
else:
328-
self.Cplant.value = self._indirect_cost_factor * 1.15 * self.ccplantadjfactor.value * 250E-6 * np.max(
329-
model.surfaceplant.HeatExtracted.value) * 1000. # 1.15 for 15% contingency
330-
331-
# absorption chiller
332-
elif model.surfaceplant.enduse_option.value == EndUseOptions.HEAT and model.surfaceplant.plant_type.value == PlantType.ABSORPTION_CHILLER: # absorption chiller
333-
if self.ccplantfixed.Valid:
334-
self.Cplant.value = self.ccplantfixed.value
335-
else:
336-
# this is for the direct-use part all the way up to the absorption chiller
337-
self.Cplant.value = self._indirect_cost_factor * 1.15 * self.ccplantadjfactor.value * 250E-6 * np.max(
338-
model.surfaceplant.HeatExtracted.value) * 1000. # 1.15 for 15% contingency
339-
if self.chillercapex.value == -1: # no value provided by user, use built-in correlation ($2500/ton)
340-
self.chillercapex.value = self._indirect_cost_factor * 1.15 * np.max(
341-
model.surfaceplant.cooling_produced.value) * 1000 / 3.517 * 2500 / 1e6 # $2,500/ton of cooling. 1.15 for 15% contingency
342-
343-
# now add chiller cost to surface plant cost
344-
self.Cplant.value += self.chillercapex.value
345-
346-
# heat pump
347-
elif model.surfaceplant.enduse_option.value == EndUseOptions.HEAT and model.surfaceplant.plant_type.value == PlantType.HEAT_PUMP:
348-
if self.ccplantfixed.Valid:
349-
self.Cplant.value = self.ccplantfixed.value
350-
else:
351-
# this is for the direct-use part all the way up to the heat pump
352-
self.Cplant.value = self._indirect_cost_factor * 1.15 * self.ccplantadjfactor.value * 250E-6 * np.max(
353-
model.surfaceplant.HeatExtracted.value) * 1000. # 1.15 for 15% contingency
354-
if self.heatpumpcapex.value == -1: # no value provided by user, use built-in correlation ($150/kWth)
355-
self.heatpumpcapex.value = self._indirect_cost_factor * 1.15 * np.max(
356-
model.surfaceplant.HeatProduced.value) * 1000 * 150 / 1e6 # $150/kW. 1.15 for 15% contingency
357-
358-
# now add heat pump cost to surface plant cost
359-
self.Cplant.value += self.heatpumpcapex.value
360-
361-
# district heating
362-
elif model.surfaceplant.enduse_option.value == EndUseOptions.HEAT and model.surfaceplant.plant_type.value == PlantType.DISTRICT_HEATING:
363-
if self.ccplantfixed.Valid:
364-
self.Cplant.value = self.ccplantfixed.value
365-
else:
366-
self.Cplant.value = self._indirect_cost_factor * 1.15 * self.ccplantadjfactor.value * 250E-6 * np.max(
367-
model.surfaceplant.HeatExtracted.value) * 1000. # 1.15 for 15% contingency
368-
369-
self.peakingboilercost.value = (self.peaking_boiler_cost_per_kW.quantity()
370-
.to('USD / kilowatt').magnitude
371-
* model.surfaceplant.max_peaking_boiler_demand.value
372-
/ 1000)
373-
374-
self.Cplant.value += self.peakingboilercost.value # add peaking boiler cost to surface plant cost
375-
376-
377-
else: # all other options have power plant
378-
if model.surfaceplant.plant_type.value == PlantType.SUB_CRITICAL_ORC:
379-
MaxProducedTemperature = np.max(model.surfaceplant.TenteringPP.value)
380-
if MaxProducedTemperature < 150.:
381-
C3 = -1.458333E-3
382-
C2 = 7.6875E-1
383-
C1 = -1.347917E2
384-
C0 = 1.0075E4
385-
CCAPP1 = C3 * MaxProducedTemperature ** 3 + C2 * MaxProducedTemperature ** 2 + C1 * MaxProducedTemperature + C0
386-
else:
387-
CCAPP1 = 2231 - 2 * (MaxProducedTemperature - 150.)
388-
x = np.max(model.surfaceplant.ElectricityProduced.value)
389-
y = np.max(model.surfaceplant.ElectricityProduced.value)
390-
if y == 0.0:
391-
y = 15.0
392-
z = math.pow(y / 15., -0.06)
393-
self.Cplantcorrelation = CCAPP1 * z * x * 1000. / 1E6
394-
395-
elif model.surfaceplant.plant_type.value == PlantType.SUPER_CRITICAL_ORC:
396-
MaxProducedTemperature = np.max(model.surfaceplant.TenteringPP.value)
397-
if MaxProducedTemperature < 150.:
398-
C3 = -1.458333E-3
399-
C2 = 7.6875E-1
400-
C1 = -1.347917E2
401-
C0 = 1.0075E4
402-
CCAPP1 = C3 * MaxProducedTemperature ** 3 + C2 * MaxProducedTemperature ** 2 + C1 * MaxProducedTemperature + C0
403-
else:
404-
CCAPP1 = 2231 - 2 * (MaxProducedTemperature - 150.)
405-
# factor 1.1 to make supercritical 10% more expansive than subcritical
406-
self.Cplantcorrelation = 1.1 * CCAPP1 * math.pow(
407-
np.max(model.surfaceplant.ElectricityProduced.value) / 15., -0.06) * np.max(
408-
model.surfaceplant.ElectricityProduced.value) * 1000. / 1E6
409-
410-
elif model.surfaceplant.plant_type.value == PlantType.SINGLE_FLASH:
411-
if np.max(model.surfaceplant.ElectricityProduced.value) < 10.:
412-
C2 = 4.8472E-2
413-
C1 = -35.2186
414-
C0 = 8.4474E3
415-
D2 = 4.0604E-2
416-
D1 = -29.3817
417-
D0 = 6.9911E3
418-
PLL = 5.
419-
PRL = 10.
420-
elif np.max(model.surfaceplant.ElectricityProduced.value) < 25.:
421-
C2 = 4.0604E-2
422-
C1 = -29.3817
423-
C0 = 6.9911E3
424-
D2 = 3.2773E-2
425-
D1 = -23.5519
426-
D0 = 5.5263E3
427-
PLL = 10.
428-
PRL = 25.
429-
elif np.max(model.surfaceplant.ElectricityProduced.value) < 50.:
430-
C2 = 3.2773E-2
431-
C1 = -23.5519
432-
C0 = 5.5263E3
433-
D2 = 3.4716E-2
434-
D1 = -23.8139
435-
D0 = 5.1787E3
436-
PLL = 25.
437-
PRL = 50.
438-
elif np.max(model.surfaceplant.ElectricityProduced.value) < 75.:
439-
C2 = 3.4716E-2
440-
C1 = -23.8139
441-
C0 = 5.1787E3
442-
D2 = 3.5271E-2
443-
D1 = -24.3962
444-
D0 = 5.1972E3
445-
PLL = 50.
446-
PRL = 75.
447-
else:
448-
C2 = 3.5271E-2
449-
C1 = -24.3962
450-
C0 = 5.1972E3
451-
D2 = 3.3908E-2
452-
D1 = -23.4890
453-
D0 = 5.0238E3
454-
PLL = 75.
455-
PRL = 100.
456-
maxProdTemp = np.max(model.surfaceplant.TenteringPP.value)
457-
CCAPPLL = C2 * maxProdTemp ** 2 + C1 * maxProdTemp + C0
458-
CCAPPRL = D2 * maxProdTemp ** 2 + D1 * maxProdTemp + D0
459-
b = math.log(CCAPPRL / CCAPPLL) / math.log(PRL / PLL)
460-
a = CCAPPRL / PRL ** b
461-
# factor 0.75 to make double flash 25% more expansive than single flash
462-
self.Cplantcorrelation = (0.8 * a * math.pow(np.max(model.surfaceplant.ElectricityProduced.value), b) *
463-
np.max(model.surfaceplant.ElectricityProduced.value) * 1000. / 1E6)
464-
465-
elif model.surfaceplant.plant_type.value == PlantType.DOUBLE_FLASH:
466-
if np.max(model.surfaceplant.ElectricityProduced.value) < 10.:
467-
C2 = 4.8472E-2
468-
C1 = -35.2186
469-
C0 = 8.4474E3
470-
D2 = 4.0604E-2
471-
D1 = -29.3817
472-
D0 = 6.9911E3
473-
PLL = 5.
474-
PRL = 10.
475-
elif np.max(model.surfaceplant.ElectricityProduced.value) < 25.:
476-
C2 = 4.0604E-2
477-
C1 = -29.3817
478-
C0 = 6.9911E3
479-
D2 = 3.2773E-2
480-
D1 = -23.5519
481-
D0 = 5.5263E3
482-
PLL = 10.
483-
PRL = 25.
484-
elif np.max(model.surfaceplant.ElectricityProduced.value) < 50.:
485-
C2 = 3.2773E-2
486-
C1 = -23.5519
487-
C0 = 5.5263E3
488-
D2 = 3.4716E-2
489-
D1 = -23.8139
490-
D0 = 5.1787E3
491-
PLL = 25.
492-
PRL = 50.
493-
elif np.max(model.surfaceplant.ElectricityProduced.value) < 75.:
494-
C2 = 3.4716E-2
495-
C1 = -23.8139
496-
C0 = 5.1787E3
497-
D2 = 3.5271E-2
498-
D1 = -24.3962
499-
D0 = 5.1972E3
500-
PLL = 50.
501-
PRL = 75.
502-
else:
503-
C2 = 3.5271E-2
504-
C1 = -24.3962
505-
C0 = 5.1972E3
506-
D2 = 3.3908E-2
507-
D1 = -23.4890
508-
D0 = 5.0238E3
509-
PLL = 75.
510-
PRL = 100.
511-
maxProdTemp = np.max(model.surfaceplant.TenteringPP.value)
512-
CCAPPLL = C2 * maxProdTemp ** 2 + C1 * maxProdTemp + C0
513-
CCAPPRL = D2 * maxProdTemp ** 2 + D1 * maxProdTemp + D0
514-
b = math.log(CCAPPRL / CCAPPLL) / math.log(PRL / PLL)
515-
a = CCAPPRL / PRL ** b
516-
self.Cplantcorrelation = (a * math.pow(np.max(model.surfaceplant.ElectricityProduced.value), b) *
517-
np.max(model.surfaceplant.ElectricityProduced.value) * 1000. / 1E6)
518-
519-
if self.ccplantfixed.Valid:
520-
self.Cplant.value = self.ccplantfixed.value
521-
self.CAPEX_cost_electricity_plant = self.Cplant.value * self.CAPEX_heat_electricity_plant_ratio.value
522-
self.CAPEX_cost_heat_plant = self.Cplant.value * (1.0 - self.CAPEX_heat_electricity_plant_ratio.value)
523-
else:
524-
# 1.02 to convert cost from 2012 to 2016 #factor 1.15 for 15% contingency and factor 1.10 to convert from 2016 to 2022
525-
self.Cplant.value = self._indirect_cost_factor * 1.15 * self.ccplantadjfactor.value * self.Cplantcorrelation * 1.02 * 1.10
526-
self.CAPEX_cost_electricity_plant = self.Cplant.value
527-
528-
# add direct-use plant cost of co-gen system to Cplant (only of no total Cplant was provided)
529-
if not self.ccplantfixed.Valid: # 1.15 below for contingency
530-
if model.surfaceplant.enduse_option.value in [EndUseOptions.COGENERATION_TOPPING_EXTRA_ELECTRICITY,
531-
EndUseOptions.COGENERATION_TOPPING_EXTRA_HEAT]: # enduse_option = 3: cogen topping cycle
532-
self.CAPEX_cost_heat_plant = self._indirect_cost_factor * 1.15 * self.ccplantadjfactor.value * 250E-6 * np.max(
533-
model.surfaceplant.HeatProduced.value / model.surfaceplant.enduse_efficiency_factor.value) * 1000.
534-
elif model.surfaceplant.enduse_option.value in [EndUseOptions.COGENERATION_BOTTOMING_EXTRA_HEAT,
535-
EndUseOptions.COGENERATION_BOTTOMING_EXTRA_ELECTRICITY]: # enduse_option = 4: cogen bottoming cycle
536-
self.CAPEX_cost_heat_plant = self._indirect_cost_factor * 1.15 * self.ccplantadjfactor.value * 250E-6 * np.max(
537-
model.surfaceplant.HeatProduced.value / model.surfaceplant.enduse_efficiency_factor.value) * 1000.
538-
elif model.surfaceplant.enduse_option.value in [EndUseOptions.COGENERATION_PARALLEL_EXTRA_ELECTRICITY,
539-
EndUseOptions.COGENERATION_PARALLEL_EXTRA_HEAT]: # cogen parallel cycle
540-
self.CAPEX_cost_heat_plant = self._indirect_cost_factor * 1.15 * self.ccplantadjfactor.value * 250E-6 * np.max(
541-
model.surfaceplant.HeatProduced.value / model.surfaceplant.enduse_efficiency_factor.value) * 1000.
542-
543-
self.Cplant.value = self.Cplant.value + self.CAPEX_cost_heat_plant
544-
if not self.CAPEX_heat_electricity_plant_ratio.Provided:
545-
self.CAPEX_heat_electricity_plant_ratio.value = self.CAPEX_cost_electricity_plant/self.Cplant.value
219+
self.calculate_field_gathering_costs(model)
220+
self.calculate_plant_costs(model)
546221

547222
if not self.totalcapcost.Valid:
548223
# exploration costs (same as in Geophires v1.2) (M$)

0 commit comments

Comments
 (0)