Skip to content

Commit

Permalink
Merge pull request #8 from softwareengineerprogrammer/main
Browse files Browse the repository at this point in the history
README cleanup

See https://github.com/softwareengineerprogrammer/python-geophires-x-nrel/actions/runs/6474840514 for passing Actions - GitHub is glacially slow to provision macOS runners today
  • Loading branch information
softwareengineerprogrammer authored Oct 10, 2023
2 parents 01b9798 + 9da3bf7 commit 90f93a2
Show file tree
Hide file tree
Showing 9 changed files with 68 additions and 295 deletions.
5 changes: 4 additions & 1 deletion AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@
Authors
=======

* softwareengineerprogrammer - https://github.com/softwareengineerprogrammer
* Koenraad Beckers - https://github.com/kfbeckers
* Malcolm Ross - https://github.com/malcolm-dsider
* Jonathan Pezzino - https://jonathanpezzino.com
* Kevin McCabe (GEOPHIRES v2.0) - [email protected]
3 changes: 2 additions & 1 deletion CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ Development
To set up `python-geophires-x` for local development:

1. Fork `python-geophires-x <https://github.com/NREL/python-geophires-x>`_
(look for the "Fork" button).
(look for the "Fork" button). Enable Actions on your fork.

2. Clone your fork locally::

git clone [email protected]:NREL/python-geophires-x.git
Expand Down
File renamed without changes.
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ include CHANGELOG.rst
include CONTRIBUTING.rst
include LICENSE
include README.rst
include How-to-extend-GEOPHIRES-X.md

global-exclude *.py[cod] __pycache__/* *.so *.dylib
49 changes: 30 additions & 19 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ Overview

GEOPHIRES is a free and open-source geothermal techno-economic simulator. GEOPHIRES combines reservoir, wellbore, surface plant, and economic models to estimate the capital and operation and maintenance costs, instantaneous and lifetime energy production, and overall levelized cost of energy of a geothermal plant. Various reservoir conditions (EGS, doublets, etc.) and end-use options (electricity, direct-use heat, cogeneration) can be modeled. Users are encouraged to build upon to the GEOPHIRES framework to implement their own correlations and models.

Ported from https://github.com/malcolm-dsider/GEOPHIRES-X and https://github.com/softwareengineerprogrammer/python-geophires-x using https://github.com/ionelmc/cookiecutter-pylibrary/.
GEOPHIRES-X is the successor version to `GEOPHIRES v2.0 <https://github.com/NREL/GEOPHIRES-v2>`_.
Ported from `malcolm-dsider/GEOPHIRES-X <https://github.com/malcolm-dsider/GEOPHIRES-X>`_
and `softwareengineerprogrammer/python-geophires-x <https://github.com/softwareengineerprogrammer/python-geophires-x>`_
using `ionelmc/cookiecutter-pylibrary <https://github.com/ionelmc/cookiecutter-pylibrary/>`_.

* Free software: MIT license
Free software: `MIT license <LICENSE>`_

.. start-badges
Expand All @@ -17,8 +20,9 @@ Ported from https://github.com/malcolm-dsider/GEOPHIRES-X and https://github.com
- | |github-actions|
|
* - package
- | |version| |wheel| |supported-versions| |supported-implementations|
| |commits-since|
- | |commits-since|

.. TODO add the following to package badge list once PyPy distribution enabled: |version| |wheel| |supported-versions| |supported-implementations|
.. |github-actions| image:: https://github.com/NREL/python-geophires-x/actions/workflows/github-actions.yml/badge.svg
:alt: GitHub Actions Build Status
Expand All @@ -40,7 +44,7 @@ Ported from https://github.com/malcolm-dsider/GEOPHIRES-X and https://github.com
:alt: Supported implementations
:target: https://pypi.org/project/geophires-x

.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/python-geophires-x/v3.1.2.svg
.. |commits-since| image:: https://img.shields.io/github/commits-since/NREL/python-geophires-x/v3.1.2.svg
:alt: Commits since latest release
:target: https://github.com/NREL/python-geophires-x/compare/v3.1.2...main

Expand All @@ -58,33 +62,40 @@ Install the in-development version with::

(Eventually package will be published to PyPi, enabling ``pip install geophires-x``)

Documentation
=============

Dev Setup
=========
See the `GEOPHIRES v2 user manual <https://github.com/NREL/GEOPHIRES-v2/blob/master/GEOPHIRES%20v2.0%20User%20Manual.pdf>`_
(A GEOPHIRES-X-specific manual is pending as of 2023-10-10).

1. Setup and activate virtualenv (https://virtualenv.pypa.io/en/latest/installation.html#via-pip)::
See `test_geophires_x.py <https://github.com/NREL/python-geophires-x/blob/main/tests/test_geophires_x.py>`_ for example usage of the client.

python -m venv venv
source venv/bin/activate
`How to extend GEOPHIRES-X <How-to-extend-GEOPHIRES-X.md>`_

2. Install dependencies in setup.py::
Development
===========

pip install .
Local Setup
-----------

3. Setup pre-commit (https://pre-commit.com/)::
Prerequisite: Follow fork & clone instructions in `CONTRIBUTING.rst <CONTRIBUTING.rst>`_. Then:

pre-commit install
1. Set up and activate `virtualenv <https://virtualenv.pypa.io/en/latest/installation.html#via-pip>`_::

python -m venv venv
source venv/bin/activate

Documentation
=============
2. Install dependencies in setup.py::

pip install -e .

See https://github.com/NREL/python-geophires-x/blob/main/tests/test_geophires_x.py for example usage
3. Set up `pre-commit <https://pre-commit.com/>`_::

pre-commit install

Development
===========

Tox tests
---------

To run all the tests run::

Expand Down
62 changes: 31 additions & 31 deletions src/geophires_x/EconomicsS_DAC_GT.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
import os
import math
import numpy as np
from Parameter import floatParameter, OutputParameter, ReadParameter
from Units import *
from OptionList import EndUseOptions
import Model
from .Parameter import floatParameter, OutputParameter, ReadParameter
from .Units import *
from .OptionList import EndUseOptions
import geophires_x.Model as Model
import numpy as np
import Economics
import geophires_x.Economics as Economics

class EconomicsS_DAC_GT(Economics.Economics):
"""
Expand All @@ -25,7 +25,7 @@ class EconomicsS_DAC_GT(Economics.Economics):
def __init__(self, model:Model):
"""
The __init__ function is the constructor for a class. It is called whenever an instance of the class is created. The __init__ function can take arguments, but self is always the first one. Self refers to the instance of the object that has already been created and it's used to access variables that belong to that object.
:param self: Reference the class object itself
:param model: The container class of the application, giving access to everything else, including the logger
Expand All @@ -39,7 +39,7 @@ def __init__(self, model:Model):
#This also includes all Parameters that are calculated and then published using the Printouts function.
#If you choose to sublass this master class, you can do so before or after you create your own parameters. If you do, you can also choose to call this method from you class, which will effectively add and set all these parameters to your class.

#These disctionaries contains a list of all the parameters set in this object, stored as "Parameter" and OutputParameter Objects. This will alow us later to access them in a user interface and get that list, along with unit type, preferred units, etc.
#These disctionaries contains a list of all the parameters set in this object, stored as "Parameter" and OutputParameter Objects. This will alow us later to access them in a user interface and get that list, along with unit type, preferred units, etc.
self.ParameterDict = {}
self.OutputParameterDict = {}
self.wacc = self.ParameterDict[self.wacc.Name] = floatParameter("WACC", value = 10.0, DefaultValue = 10.0, Min=0.1, Max=30.0, UnitType = Units.PERCENT, PreferredUnits = PercentUnit.PERCENT, CurrentUnits = PercentUnit.PERCENT, ErrMessage = "assume default Weighted Average Cost of Capital (10%)", ToolTipText="Weighted Average Cost of Capital (percent)")
Expand Down Expand Up @@ -95,7 +95,7 @@ def __str__(self):
def read_parameters(self, model:Model) -> None:
"""
The read_parameters function reads in the parameters from a dictionary and stores them in the aparmeters. It also handles special cases that need to be handled after a value has been read in and checked. If you choose to sublass this master class, you can also choose to override this method (or not), and if you do
:param self: Access variables that belong to a class
:param model: The container class of the application, giving access to everything else, including the logger
Expand Down Expand Up @@ -160,58 +160,58 @@ def range_check(self)->tuple:
if not (wacc_min <= self.wacc.value <= wacc_max):
error_message = "S-DAC-GT ERROR: WACC should be between {}% and {}%".format(wacc_min, wacc_max)
return(True, error_message)

if not (CAPEX_min <= self.CAPEX.value <= CAPEX_max):
error_message = "S-DAC-GT ERROR: CAPEX should be between {} and {}".format(CAPEX_min, CAPEX_max)
return(True, error_message)

if not (OPEX_min <= self.OPEX.value <= OPEX_max):
error_message = "S-DAC-GT ERROR: OPEX should be between {} and {}".format(OPEX_min, OPEX_max)
return(True, error_message)

if not (elec_min <= self.elec.value <= elec_max):
error_message = "S-DAC-GT ERROR: Electrical Energy should be between {} and {}".format(elec_min, elec_max)
return(True, error_message)

if not (therm_min <= self.therm.value <= therm_max):
error_message = "S-DAC-GT ERROR: Thermal Energy should be between {} and {}".format(therm_min, therm_max)
return(True, error_message)

if not (NG_price_min <= self.NG_price.value <= NG_price_max):
error_message = "S-DAC-GT ERROR: Natural Gas Price should be between {} and {}".format(NG_price_min, NG_price_max)
return(True, error_message)

if not (power_co2intensity_min <= self.power_co2intensity.value <= power_co2intensity_max):
error_message = "S-DAC-GT ERROR: CO2 Intensity of Electricity should be between {} and {}".format(power_co2intensity_min, power_co2intensity_max)
return(True, error_message)

if not (CAPEX_mult_min <= self.CAPEX_mult.value <= CAPEX_mult_max):
error_message = "S-DAC-GT ERROR: CAPEX Multiplier should be between {} and {}".format(CAPEX_mult_min, CAPEX_mult_max)
return(True, error_message)

if not (OPEX_mult_min <= self.OPEX_mult.value <= OPEX_mult_max):
error_message = "S-DAC-GT ERROR: OPEX Multiplier should be between {} and {}".format(OPEX_mult_min, OPEX_mult_max)
return(True, error_message)

if not (therm_index_min <= self.therm_index.value <= therm_index_max):
error_message = "S-DAC-GT ERROR: S-DAC Thermal Energy Multiplier should be between {} and {}".format(therm_index_min, therm_index_max)
return(True, error_message)

if not (transport_min <= self.transport.value <= transport_max):
error_message = "S-DAC-GT ERROR: CO2 Transportation Cost should be between {} and {}".format(transport_min, transport_max)
return(True, error_message)

if not (storage_min <= self.storage.value <= storage_max):
error_message = "S-DAC-GT ERROR: CO2 Storage Cost should be between {} and {}".format(storage_min, storage_max)
return(True, error_message)

return(False,"")

def geo_therm_cost(self, power_cost:float, CAPEX_mult:float, OPEX_mult:float, depth:float, Production_temp:float, Injection_temp:float, Flow_rate:float)->tuple:
# Calculate Levelized cost of heat and ratio of electric power to heat power
# LCOH calculated in USD
# Power ratio calculated as kWh_e / kWh_th --> used for calculating CO2 footprint of geothermal energy
# inputs are cost of electricity, regional capex and opex multipliers,
# inputs are cost of electricity, regional capex and opex multipliers,
# depth of geothermal reservoir, Average Production Temperature, Injection Temperature, and Flow Rate
#recoded by Malcolm Ross when integrated with GEOPHIRES - GEOPHIRES has more information, so fewer assumptions are made
# Update NREL 2016 model for 2022
Expand All @@ -236,14 +236,14 @@ def geo_therm_cost(self, power_cost:float, CAPEX_mult:float, OPEX_mult:float, de
NREL_reinjection = 127130 * Inflation # USD

# Normalize for region
CAPEX = NREL_CAPEX * CAPEX_mult
CAPEX = NREL_CAPEX * CAPEX_mult
CAPEX_drill = depth * NREL_drill_per_foot / Drilling_efficiency_factor
pump_kwh = depth * NREL_pump_per_foot
pump_cost = pump_kwh * power_cost
inhibitor = NREL_inhibitor * OPEX_mult
labor = NREL_labor * OPEX_mult
reinjection = NREL_reinjection / NREL_depth * depth * OPEX_mult

# total costs
CAPEX_total = CAPEX + CAPEX_drill
OPEX_total = pump_cost + inhibitor + labor + reinjection
Expand All @@ -252,19 +252,19 @@ def geo_therm_cost(self, power_cost:float, CAPEX_mult:float, OPEX_mult:float, de
Thermal_capacity = (Production_temp-Injection_temp)*Flow_rate*H2O_thermal_capacity*60*60 # kW
Annual_op_hrs = 365*24*Capacity_factor # hours
Therm_total = Thermal_capacity * Annual_op_hrs # kWh

# Levelized cost of heat (LCOH)
LCOH = (CAPEX_total*self.CRF + OPEX_total)/Therm_total # $/kWh_therm

kWh_e_per_kWh_th = pump_kwh / Therm_total

return (LCOH, kWh_e_per_kWh_th)

def Calculate(self, model:Model)->None:
"""
The Calculate function is where all the calculations are done.
This function can be called multiple times, and will only recalculate what has changed each time it is called.
:param self: Access variables that belongs to the class
:param model: The container class of the application, giving access to everything else, including the logger
:return: Nothing, but it does make calculations and set values in the model
Expand Down Expand Up @@ -298,11 +298,11 @@ def Calculate(self, model:Model)->None:
co2_elec_heat = self.therm.value/1000*self.power_co2intensity.value
co2_ng = self.therm.value/1000*self.NG_co2intensity.value
co2_geothermal = self.therm.value*self.kWh_e_per_kWh_th.value/1000*self.power_co2intensity.value

self.LCOD_elec.value = CAPEX+self.OPEX.value+power_totalcost+elec_heat_totalcost+self.storage.value+self.transport.value
self.LCOD_ng.value = CAPEX+self.OPEX.value+power_totalcost+NG_totalcost+self.storage.value+self.transport.value
self.LCOD_geo.value = CAPEX+self.OPEX.value+power_totalcost+geothermal_totalcost+self.storage.value+self.transport.value

self.CO2total_elec.value = co2_power + co2_elec_heat
self.CO2total_ng.value = co2_power + co2_ng
self.CO2total_geo.value = co2_power + co2_geothermal
Expand All @@ -320,7 +320,7 @@ def Calculate(self, model:Model)->None:
self.CummCostPerTonne.value = [0.0] * model.surfaceplant.plantlifetime.value
self.CarbonExtractedTotal.value = 0.0

#Figure out how much energy is being produced each year, and the amount of carbon that would have been produced if that energy had been made using the grdi average carbon production. That then gives us the revenue, since we have a carbon price model
#Figure out how much energy is being produced each year, and the amount of carbon that would have been produced if that energy had been made using the grdi average carbon production. That then gives us the revenue, since we have a carbon price model
#We can also get annual cash flow from it.
for i in range(0,model.surfaceplant.plantlifetime.value,1):
self.CarbonExtractedAnnually.value[i] = (self.EnergySplit.value * model.surfaceplant.HeatkWhExtracted.value[i]) / self.tot_heat_energy_consumed_per_tonne.value
Expand Down
37 changes: 0 additions & 37 deletions src/geophires_x/LICENSE

This file was deleted.

17 changes: 0 additions & 17 deletions src/geophires_x/README.md

This file was deleted.

Loading

0 comments on commit 90f93a2

Please sign in to comment.