@@ -101,67 +101,7 @@ def __init__(self, model: Model):
101
101
sclass = str (__class__ ).replace ("<class \' " , "" )
102
102
self .MyClass = sclass .replace ("\' >" , "" )
103
103
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
125
104
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
- """
165
105
model .logger .info (f'complete { __class__ !s} : { sys ._getframe ().f_code .co_name } ' )
166
106
167
107
def __str__ (self ):
@@ -183,10 +123,6 @@ def read_parameters(self, model: Model) -> None:
183
123
# because the call to the super.readparameters will set all the variables,
184
124
# including the ones that are specific to this class
185
125
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
-
190
126
model .logger .info (f'complete { __class__ !s} : { sys ._getframe ().f_code .co_name } ' )
191
127
192
128
def Calculate (self , model : Model ) -> None :
@@ -280,269 +216,8 @@ def Calculate(self, model: Model) -> None:
280
216
self .cost_lateral_section .value + self .cost_to_junction_section .value )
281
217
282
218
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 )
546
221
547
222
if not self .totalcapcost .Valid :
548
223
# exploration costs (same as in Geophires v1.2) (M$)
0 commit comments