From 446f15a419541568039201aa834676f4c81ea421 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 27 Mar 2025 10:21:16 -0700 Subject: [PATCH 01/24] WIP to generate geophires-result.json --- src/geophires_x_schema_generator/__init__.py | 67 +++++++++++++++++-- src/geophires_x_schema_generator/main.py | 22 +++--- .../test_geophires_x_schema_generator.py | 6 ++ 3 files changed, 83 insertions(+), 12 deletions(-) diff --git a/src/geophires_x_schema_generator/__init__.py b/src/geophires_x_schema_generator/__init__.py index 9c90e48ea..d5b2bd5f1 100644 --- a/src/geophires_x_schema_generator/__init__.py +++ b/src/geophires_x_schema_generator/__init__.py @@ -27,6 +27,7 @@ from geophires_x.SUTRAWellBores import SUTRAWellBores from geophires_x.TDPReservoir import TDPReservoir from geophires_x.TOUGH2Reservoir import TOUGH2Reservoir +from geophires_x_client import GeophiresXResult from hip_ra_x.hip_ra_x import HIP_RA_X @@ -96,7 +97,11 @@ def _with_cat(p: Parameter, cat: str): return json_dumpse(input_params), json_dumpse(output_params) - def generate_json_schema(self) -> dict: + def generate_json_schema(self) -> Tuple[dict, dict]: + """ + :return: request schema, result schema + :rtype: Tuple[dict, dict] + """ input_params_json, output_params_json = self.get_parameters_json() input_params = json.loads(input_params_json) @@ -124,16 +129,67 @@ def generate_json_schema(self) -> dict: if param['ValuesEnum']: properties[param_name]['enum_values'] = param['ValuesEnum'] - schema = { + request_schema = { + 'definitions': {}, + '$schema': 'http://json-schema.org/draft-04/schema#', + 'type': 'object', + 'title': f'{self.get_schema_title()} Request Schema', + 'required': required, + 'properties': properties, + } + + return request_schema, self.get_result_json_schema(output_params_json) + + def get_result_json_schema(self, output_params_json) -> dict: + properties = {} + required = [] + + output_params = json.loads(output_params_json) + + # noinspection PyProtectedMember + for category in GeophiresXResult._RESULT_FIELDS_BY_CATEGORY: + # noinspection PyProtectedMember + for field in GeophiresXResult._RESULT_FIELDS_BY_CATEGORY[category]: + param_name = field if isinstance(field, str) else field.field_name + param = {} + + if param_name in output_params: + output_param = output_params[param_name] + param['description'] = output_param['ToolTipText'] + + properties[param_name] = param.copy() + + # for param_name in output_params: + # param = output_params[param_name] + # + # units_val = param['CurrentUnits'] if isinstance(param['CurrentUnits'], str) else None + # min_val, max_val = _get_min_and_max(param, default_val=None) + # properties[param_name] = { + # 'description': param['ToolTipText'], + # 'type': param['json_parameter_type'], + # 'units': units_val, + # 'category': param['parameter_category'], + # 'default': _fix_floating_point_error(param['DefaultValue']), + # 'minimum': min_val, + # 'maximum': max_val, + # } + # + # if param['Required']: + # required.append(param_name) + # + # if param['ValuesEnum']: + # properties[param_name]['enum_values'] = param['ValuesEnum'] + + result_schema = { 'definitions': {}, '$schema': 'http://json-schema.org/draft-04/schema#', 'type': 'object', - 'title': f'{self.get_schema_title()} Schema', + 'title': f'{self.get_schema_title()} Result Schema', 'required': required, 'properties': properties, } - return schema + return result_schema def generate_parameters_reference_rst(self) -> str: input_params_json, output_params_json = self.get_parameters_json() @@ -271,3 +327,6 @@ def get_parameter_sources(self) -> list: def get_schema_title(self) -> str: return 'HIP-RA-X' + + def get_result_json_schema(self, output_params_json) -> dict: + return None # FIXME TODO diff --git a/src/geophires_x_schema_generator/main.py b/src/geophires_x_schema_generator/main.py index 7dfeea9a8..4db172f7c 100644 --- a/src/geophires_x_schema_generator/main.py +++ b/src/geophires_x_schema_generator/main.py @@ -21,13 +21,19 @@ build_dir.mkdir(exist_ok=True) - def build(json_file_name: str, generator: GeophiresXSchemaGenerator, rst_file_name: str): - build_path = Path(build_dir, json_file_name) - schema_json = generator.generate_json_schema() + def build(json_file_name_prefix: str, generator: GeophiresXSchemaGenerator, rst_file_name: str): + request_schema_json, result_schema_json = generator.generate_json_schema() - with open(build_path, 'w') as f: - f.write(json.dumps(schema_json, indent=2)) - print(f'Wrote JSON schema file to {build_path}.') + request_build_path = Path(build_dir, f'{json_file_name_prefix}request.json') + with open(request_build_path, 'w') as f: + f.write(json.dumps(request_schema_json, indent=2)) + print(f'Wrote request JSON schema file to {request_build_path}.') + + if result_schema_json is not None: + result_build_path = Path(build_dir, f'{json_file_name_prefix}result.json') + with open(result_build_path, 'w') as f: + f.write(json.dumps(result_schema_json, indent=2)) + print(f'Wrote result JSON schema file to {result_build_path}.') rst = generator.generate_parameters_reference_rst() @@ -36,5 +42,5 @@ def build(json_file_name: str, generator: GeophiresXSchemaGenerator, rst_file_na f.write(rst) print(f'Wrote RST file to {build_path_rst}.') - build('geophires-request.json', GeophiresXSchemaGenerator(), 'parameters.rst') - build('hip-ra-x-request.json', HipRaXSchemaGenerator(), 'hip_ra_x_parameters.rst') + build('geophires-', GeophiresXSchemaGenerator(), 'parameters.rst') + build('hip-ra-x-', HipRaXSchemaGenerator(), 'hip_ra_x_parameters.rst') diff --git a/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py b/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py index e86cd4d9b..d4dd17edd 100644 --- a/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py +++ b/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py @@ -10,6 +10,12 @@ def test_parameters_rst(self): rst = g.generate_parameters_reference_rst() self.assertIsNotNone(rst) # TODO sanity checks on content + def test_get_json_schema(self): + g = GeophiresXSchemaGenerator() + req_schema, result_schema = g.generate_json_schema() + self.assertIsNotNone(req_schema) # TODO sanity checks on content + self.assertIsNotNone(result_schema) # TODO sanity checks on content + if __name__ == '__main__': unittest.main() From 4d89d3fad931035d89897faecd209fe968f29c13 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 27 Mar 2025 10:32:10 -0700 Subject: [PATCH 02/24] initial result schema generation implementation --- src/geophires_x_schema_generator/__init__.py | 27 ++++++++++++++++++- .../test_geophires_x_schema_generator.py | 3 +++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/geophires_x_schema_generator/__init__.py b/src/geophires_x_schema_generator/__init__.py index d5b2bd5f1..34b1994ab 100644 --- a/src/geophires_x_schema_generator/__init__.py +++ b/src/geophires_x_schema_generator/__init__.py @@ -1,4 +1,5 @@ import json +import logging import os import sys from pathlib import Path @@ -151,12 +152,19 @@ def get_result_json_schema(self, output_params_json) -> dict: # noinspection PyProtectedMember for field in GeophiresXResult._RESULT_FIELDS_BY_CATEGORY[category]: param_name = field if isinstance(field, str) else field.field_name - param = {} + param = { + 'category': category, + } if param_name in output_params: output_param = output_params[param_name] param['description'] = output_param['ToolTipText'] + param['units'] = ( + output_param['CurrentUnits'] if isinstance(output_param['CurrentUnits'], str) else None + ) + if param_name in properties: + _log.warning(f'Param {param_name} is already in properties: {properties[param_name]}') properties[param_name] = param.copy() # for param_name in output_params: @@ -330,3 +338,20 @@ def get_schema_title(self) -> str: def get_result_json_schema(self, output_params_json) -> dict: return None # FIXME TODO + + +def _get_logger(logger_name=None): + sh = logging.StreamHandler(sys.stdout) + sh.setLevel(logging.INFO) + sh.setFormatter(logging.Formatter(fmt='[%(asctime)s][%(levelname)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S')) + + if logger_name is None: + logger_name = __name__ + + _l = logging.getLogger(logger_name) + _l.addHandler(sh) + + return _l + + +_log = _get_logger() diff --git a/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py b/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py index d4dd17edd..8e5c65167 100644 --- a/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py +++ b/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py @@ -1,3 +1,4 @@ +import json import unittest from geophires_x_schema_generator import GeophiresXSchemaGenerator @@ -16,6 +17,8 @@ def test_get_json_schema(self): self.assertIsNotNone(req_schema) # TODO sanity checks on content self.assertIsNotNone(result_schema) # TODO sanity checks on content + print(f'Generated result schema: {json.dumps(result_schema, indent=2)}') + if __name__ == '__main__': unittest.main() From e498705d77f4dd5f40f15fa4cc6f9732e09ffa53 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 27 Mar 2025 10:40:35 -0700 Subject: [PATCH 03/24] support multiple categories for result (output) parameters --- src/geophires_x_schema_generator/__init__.py | 39 +++++++------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/src/geophires_x_schema_generator/__init__.py b/src/geophires_x_schema_generator/__init__.py index 34b1994ab..4aaea2781 100644 --- a/src/geophires_x_schema_generator/__init__.py +++ b/src/geophires_x_schema_generator/__init__.py @@ -152,9 +152,19 @@ def get_result_json_schema(self, output_params_json) -> dict: # noinspection PyProtectedMember for field in GeophiresXResult._RESULT_FIELDS_BY_CATEGORY[category]: param_name = field if isinstance(field, str) else field.field_name - param = { - 'category': category, - } + + if param_name in properties: + _log.warning(f'Param {param_name} is already in properties: {properties[param_name]}') + + param = ( + { + 'categories': [], + } + if param_name not in properties + else properties[param_name] + ) + + param['categories'].append(category) if param_name in output_params: output_param = output_params[param_name] @@ -163,31 +173,8 @@ def get_result_json_schema(self, output_params_json) -> dict: output_param['CurrentUnits'] if isinstance(output_param['CurrentUnits'], str) else None ) - if param_name in properties: - _log.warning(f'Param {param_name} is already in properties: {properties[param_name]}') properties[param_name] = param.copy() - # for param_name in output_params: - # param = output_params[param_name] - # - # units_val = param['CurrentUnits'] if isinstance(param['CurrentUnits'], str) else None - # min_val, max_val = _get_min_and_max(param, default_val=None) - # properties[param_name] = { - # 'description': param['ToolTipText'], - # 'type': param['json_parameter_type'], - # 'units': units_val, - # 'category': param['parameter_category'], - # 'default': _fix_floating_point_error(param['DefaultValue']), - # 'minimum': min_val, - # 'maximum': max_val, - # } - # - # if param['Required']: - # required.append(param_name) - # - # if param['ValuesEnum']: - # properties[param_name]['enum_values'] = param['ValuesEnum'] - result_schema = { 'definitions': {}, '$schema': 'http://json-schema.org/draft-04/schema#', From 2ba21480beb6857f1c4a123f61945bdabdbde9ed Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 27 Mar 2025 10:42:03 -0700 Subject: [PATCH 04/24] add generated geophires-result.json; updated schema names for request json --- src/geophires_x_schema_generator/.gitignore | 1 + .../geophires-request.json | 2 +- .../geophires-result.json | 740 ++++++++++++++++++ .../hip-ra-x-request.json | 2 +- 4 files changed, 743 insertions(+), 2 deletions(-) create mode 100644 src/geophires_x_schema_generator/geophires-result.json diff --git a/src/geophires_x_schema_generator/.gitignore b/src/geophires_x_schema_generator/.gitignore index a1de5eca0..e739df7bb 100644 --- a/src/geophires_x_schema_generator/.gitignore +++ b/src/geophires_x_schema_generator/.gitignore @@ -1,4 +1,5 @@ *parameters.rst all_messages_conf.log !geophires-request.json +!geophires-result.json !hip-ra-x-request.json diff --git a/src/geophires_x_schema_generator/geophires-request.json b/src/geophires_x_schema_generator/geophires-request.json index 04338a7ef..8496c46d5 100644 --- a/src/geophires_x_schema_generator/geophires-request.json +++ b/src/geophires_x_schema_generator/geophires-request.json @@ -2,7 +2,7 @@ "definitions": {}, "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", - "title": "GEOPHIRES-X Schema", + "title": "GEOPHIRES-X Request Schema", "required": [ "Reservoir Model", "Reservoir Depth", diff --git a/src/geophires_x_schema_generator/geophires-result.json b/src/geophires_x_schema_generator/geophires-result.json new file mode 100644 index 000000000..bc2c10cde --- /dev/null +++ b/src/geophires_x_schema_generator/geophires-result.json @@ -0,0 +1,740 @@ +{ + "definitions": {}, + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "title": "GEOPHIRES-X Result Schema", + "required": [], + "properties": { + "End-Use Option": { + "category": "SUMMARY OF RESULTS" + }, + "End-Use": { + "category": "SUMMARY OF RESULTS" + }, + "Surface Application": { + "category": "SUMMARY OF RESULTS" + }, + "Average Net Electricity Production": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Electricity breakeven price": { + "category": "SUMMARY OF RESULTS" + }, + "Average Direct-Use Heat Production": { + "category": "SUMMARY OF RESULTS" + }, + "Direct-Use heat breakeven price": { + "category": "SUMMARY OF RESULTS" + }, + "Direct-Use heat breakeven price (LCOH)": { + "category": "SUMMARY OF RESULTS" + }, + "Direct-Use Cooling Breakeven Price (LCOC)": { + "category": "SUMMARY OF RESULTS" + }, + "Annual District Heating Demand": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Average Cooling Production": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Average Annual Geothermal Heat Production": { + "category": "SUMMARY OF RESULTS" + }, + "Average Annual Peaking Fuel Heat Production": { + "category": "SUMMARY OF RESULTS" + }, + "Direct-Use Cooling Breakeven Price": { + "category": "SUMMARY OF RESULTS" + }, + "Number of production wells": { + "category": "SUMMARY OF RESULTS" + }, + "Number of injection wells": { + "category": "SUMMARY OF RESULTS" + }, + "Flowrate per production well": { + "category": "ENGINEERING PARAMETERS" + }, + "Well depth": { + "category": "ENGINEERING PARAMETERS" + }, + "Well depth (or total length, if not vertical)": { + "category": "ENGINEERING PARAMETERS" + }, + "Geothermal gradient": { + "category": "RESOURCE CHARACTERISTICS" + }, + "Segment 1 Geothermal gradient": { + "category": "RESOURCE CHARACTERISTICS" + }, + "Segment 1 Thickness": { + "category": "RESOURCE CHARACTERISTICS" + }, + "Segment 2 Geothermal gradient": { + "category": "RESOURCE CHARACTERISTICS" + }, + "Segment 2 Thickness": { + "category": "RESOURCE CHARACTERISTICS" + }, + "Segment 3 Geothermal gradient": { + "category": "RESOURCE CHARACTERISTICS" + }, + "Segment 3 Thickness": { + "category": "RESOURCE CHARACTERISTICS" + }, + "Segment 4 Geothermal gradient": { + "category": "RESOURCE CHARACTERISTICS" + }, + "LCOE": { + "category": "SUMMARY OF RESULTS", + "description": "", + "units": "cents/kWh" + }, + "LCOH": { + "category": "SUMMARY OF RESULTS", + "description": "", + "units": "USD/MMBTU" + }, + "Lifetime Average Well Flow Rate": { + "category": "ENGINEERING PARAMETERS" + }, + "Total Avoided Carbon Emissions": { + "category": "SUMMARY OF RESULTS" + }, + "Economic Model": { + "category": "ECONOMIC PARAMETERS" + }, + "Interest Rate": { + "category": "ECONOMIC PARAMETERS", + "description": "", + "units": "%" + }, + "Accrued financing during construction": { + "category": "ECONOMIC PARAMETERS" + }, + "Project lifetime": { + "category": "ECONOMIC PARAMETERS" + }, + "Capacity factor": { + "category": "ECONOMIC PARAMETERS" + }, + "Project NPV": { + "category": "ECONOMIC PARAMETERS" + }, + "Project IRR": { + "category": "ECONOMIC PARAMETERS" + }, + "Project VIR=PI=PIR": { + "category": "ECONOMIC PARAMETERS" + }, + "Project MOIC": { + "category": "ECONOMIC PARAMETERS", + "description": "Project Multiple of Invested Capital", + "units": "" + }, + "Fixed Charge Rate (FCR)": { + "category": "ECONOMIC PARAMETERS" + }, + "Project Payback Period": { + "category": "ECONOMIC PARAMETERS", + "description": "", + "units": "yr" + }, + "CHP: Percent cost allocation for electrical plant": { + "category": "ECONOMIC PARAMETERS" + }, + "Estimated Jobs Created": { + "category": "ECONOMIC PARAMETERS", + "description": "", + "units": null + }, + "Adjusted Project LCOE (after incentives, grants, AddOns,etc)": { + "category": "EXTENDED ECONOMICS" + }, + "Adjusted Project LCOH (after incentives, grants, AddOns,etc)": { + "category": "EXTENDED ECONOMICS" + }, + "Adjusted Project CAPEX (after incentives, grants, AddOns, etc)": { + "category": "EXTENDED ECONOMICS" + }, + "Adjusted Project OPEX (after incentives, grants, AddOns, etc)": { + "category": "EXTENDED ECONOMICS" + }, + "Project NPV (including AddOns)": { + "category": "EXTENDED ECONOMICS" + }, + "Project IRR (including AddOns)": { + "category": "EXTENDED ECONOMICS" + }, + "Project VIR=PI=PIR (including AddOns)": { + "category": "EXTENDED ECONOMICS" + }, + "Project MOIC (including AddOns)": { + "category": "EXTENDED ECONOMICS" + }, + "Project Payback Period (including AddOns)": { + "category": "EXTENDED ECONOMICS" + }, + "Total Add-on CAPEX": { + "category": "EXTENDED ECONOMICS" + }, + "Total Add-on OPEX": { + "category": "EXTENDED ECONOMICS" + }, + "Total Add-on Net Elec": { + "category": "EXTENDED ECONOMICS" + }, + "Total Add-on Net Heat": { + "category": "EXTENDED ECONOMICS" + }, + "Total Add-on Profit": { + "category": "EXTENDED ECONOMICS" + }, + "AddOns Payback Period": { + "category": "EXTENDED ECONOMICS" + }, + "Total Avoided Carbon Production": { + "category": "CCUS ECONOMICS" + }, + "Project NPV (including carbon credit)": { + "category": "CCUS ECONOMICS" + }, + "Project IRR (including carbon credit)": { + "category": "CCUS ECONOMICS" + }, + "Project VIR=IR=PIR (including carbon credit)": { + "category": "CCUS ECONOMICS" + }, + "Project MOIC (including carbon credit)": { + "category": "CCUS ECONOMICS" + }, + "Project Payback Period (including carbon credit)": { + "category": "CCUS ECONOMICS" + }, + "LCOD using grid-based electricity only": { + "category": "S-DAC-GT ECONOMICS" + }, + "LCOD using natural gas only": { + "category": "S-DAC-GT ECONOMICS" + }, + "LCOD using geothermal energy only": { + "category": "S-DAC-GT ECONOMICS" + }, + "CO2 Intensity using grid-based electricity only": { + "category": "S-DAC-GT ECONOMICS" + }, + "CO2 Intensity using natural gas only": { + "category": "S-DAC-GT ECONOMICS" + }, + "CO2 Intensity using geothermal energy only": { + "category": "S-DAC-GT ECONOMICS" + }, + "Geothermal LCOH": { + "category": "S-DAC-GT ECONOMICS" + }, + "Geothermal Ratio (electricity vs heat)": { + "category": "S-DAC-GT ECONOMICS" + }, + "Percent Energy Devoted To Process": { + "category": "S-DAC-GT ECONOMICS" + }, + "Total Cost of Capture": { + "category": "S-DAC-GT ECONOMICS" + }, + "Number of Production Wells": { + "category": "ENGINEERING PARAMETERS" + }, + "Number of Injection Wells": { + "category": "ENGINEERING PARAMETERS" + }, + "Water loss rate": { + "category": "ENGINEERING PARAMETERS" + }, + "Pump efficiency": { + "category": "ENGINEERING PARAMETERS" + }, + "Injection temperature": { + "category": "ENGINEERING PARAMETERS" + }, + "Injection Temperature": { + "category": "ENGINEERING PARAMETERS", + "description": "", + "units": "degC" + }, + "Average production well temperature drop": { + "category": "ENGINEERING PARAMETERS" + }, + "Injection well casing ID": { + "category": "ENGINEERING PARAMETERS" + }, + "Production well casing ID": { + "category": "ENGINEERING PARAMETERS" + }, + "Number of times redrilling": { + "category": "ENGINEERING PARAMETERS" + }, + "Power plant type": { + "category": "ENGINEERING PARAMETERS" + }, + "Fluid": { + "category": "ENGINEERING PARAMETERS" + }, + "Design": { + "category": "ENGINEERING PARAMETERS" + }, + "Flow rate": { + "category": "ENGINEERING PARAMETERS" + }, + "Lateral Length": { + "category": "ENGINEERING PARAMETERS" + }, + "Vertical Depth": { + "category": "ENGINEERING PARAMETERS" + }, + "Wellbore Diameter": { + "category": "ENGINEERING PARAMETERS" + }, + "Maximum reservoir temperature": { + "category": "RESOURCE CHARACTERISTICS" + }, + "Number of segments": { + "category": "RESOURCE CHARACTERISTICS" + }, + "Reservoir Model": { + "category": "RESERVOIR PARAMETERS" + }, + "Fracture model": { + "category": "RESERVOIR PARAMETERS" + }, + "Bottom-hole temperature": { + "category": "RESERVOIR PARAMETERS", + "description": "", + "units": "degC" + }, + "Well separation: fracture diameter": { + "category": "RESERVOIR PARAMETERS" + }, + "Well separation: fracture height": { + "category": "RESERVOIR PARAMETERS" + }, + "Fracture width": { + "category": "RESERVOIR PARAMETERS" + }, + "Fracture area": { + "category": "RESERVOIR PARAMETERS" + }, + "Number of fractures": { + "category": "RESERVOIR PARAMETERS" + }, + "Fracture separation": { + "category": "RESERVOIR PARAMETERS" + }, + "Reservoir volume": { + "category": "RESERVOIR PARAMETERS" + }, + "Reservoir impedance": { + "category": "RESERVOIR PARAMETERS" + }, + "Reservoir hydrostatic pressure": { + "category": "RESERVOIR PARAMETERS" + }, + "Average reservoir pressure": { + "category": "RESERVOIR PARAMETERS" + }, + "Plant outlet pressure": { + "category": "RESERVOIR PARAMETERS" + }, + "Production wellhead pressure": { + "category": "RESERVOIR PARAMETERS", + "description": "", + "units": "kPa" + }, + "Productivity Index": { + "category": "RESERVOIR PARAMETERS" + }, + "Injectivity Index": { + "category": "RESERVOIR PARAMETERS" + }, + "Reservoir density": { + "category": "RESERVOIR PARAMETERS" + }, + "Reservoir thermal conductivity": { + "category": "RESERVOIR PARAMETERS" + }, + "Reservoir heat capacity": { + "category": "RESERVOIR PARAMETERS" + }, + "Reservoir porosity": { + "category": "RESERVOIR PARAMETERS" + }, + "Thermal Conductivity": { + "category": "RESERVOIR PARAMETERS" + }, + "Maximum Production Temperature": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Average Production Temperature": { + "category": "RESERVOIR SIMULATION RESULTS", + "description": "", + "units": "degC" + }, + "Minimum Production Temperature": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Initial Production Temperature": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Average Reservoir Heat Extraction": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Production Wellbore Heat Transmission Model": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Wellbore Heat Transmission Model": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Average Production Well Temperature Drop": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Total Average Pressure Drop": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Average Injection Well Pressure Drop": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Average Production Pressure": { + "category": "RESERVOIR SIMULATION RESULTS", + "description": "", + "units": "bar" + }, + "Average Reservoir Pressure Drop": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Average Production Well Pressure Drop": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Average Buoyancy Pressure Drop": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Average Injection Well Pump Pressure Drop": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Average Production Well Pump Pressure Drop": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Average Heat Production": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "First Year Heat Production": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "First Year Electricity Production": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Maximum Storage Well Temperature": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Average Storage Well Temperature": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Minimum Storage Well Temperature": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Maximum Balance Well Temperature": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Average Balance Well Temperature": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Minimum Balance Well Temperature": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Maximum Annual Heat Stored": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Average Annual Heat Stored": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Minimum Annual Heat Stored": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Maximum Annual Heat Supplied": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Average Annual Heat Supplied": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Minimum Annual Heat Supplied": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Average Round-Trip Efficiency": { + "category": "RESERVOIR SIMULATION RESULTS" + }, + "Drilling and completion costs": { + "category": "CAPITAL COSTS (M$)" + }, + "Drilling and completion costs per well": { + "category": "CAPITAL COSTS (M$)" + }, + "Drilling and completion costs per production well": { + "category": "CAPITAL COSTS (M$)" + }, + "Drilling and completion costs per injection well": { + "category": "CAPITAL COSTS (M$)" + }, + "Drilling and completion costs per vertical production well": { + "category": "CAPITAL COSTS (M$)" + }, + "Drilling and completion costs per vertical injection well": { + "category": "CAPITAL COSTS (M$)" + }, + "Drilling and completion costs per non-vertical section": { + "category": "CAPITAL COSTS (M$)", + "description": "", + "units": "MUSD" + }, + "Drilling and completion costs (for redrilling)": { + "category": "CAPITAL COSTS (M$)" + }, + "Drilling and completion costs per redrilled well": { + "category": "CAPITAL COSTS (M$)" + }, + "Stimulation costs": { + "category": "CAPITAL COSTS (M$)" + }, + "Stimulation costs (for redrilling)": { + "category": "CAPITAL COSTS (M$)" + }, + "Surface power plant costs": { + "category": "CAPITAL COSTS (M$)" + }, + "of which Absorption Chiller Cost": { + "category": "CAPITAL COSTS (M$)" + }, + "of which Heat Pump Cost": { + "category": "CAPITAL COSTS (M$)" + }, + "of which Peaking Boiler Cost": { + "category": "CAPITAL COSTS (M$)" + }, + "Transmission pipeline cost": { + "category": "CAPITAL COSTS (M$)" + }, + "District Heating System Cost": { + "category": "CAPITAL COSTS (M$)", + "description": "", + "units": "MUSD" + }, + "Field gathering system costs": { + "category": "CAPITAL COSTS (M$)" + }, + "Total surface equipment costs": { + "category": "CAPITAL COSTS (M$)" + }, + "Exploration costs": { + "category": "CAPITAL COSTS (M$)" + }, + "Investment Tax Credit": { + "category": "CAPITAL COSTS (M$)" + }, + "Total capital costs": { + "category": "CAPITAL COSTS (M$)" + }, + "Annualized capital costs": { + "category": "CAPITAL COSTS (M$)" + }, + "Total CAPEX": { + "category": "CAPITAL COSTS (M$)" + }, + "Drilling Cost": { + "category": "CAPITAL COSTS (M$)" + }, + "Drilling and Completion Costs": { + "category": "CAPITAL COSTS (M$)" + }, + "Drilling and Completion Costs per Well": { + "category": "CAPITAL COSTS (M$)" + }, + "Auxiliary Heater Cost": { + "category": "CAPITAL COSTS (M$)" + }, + "Pump Cost": { + "category": "CAPITAL COSTS (M$)" + }, + "Total Capital Costs": { + "category": "CAPITAL COSTS (M$)" + }, + "Wellfield maintenance costs": { + "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)" + }, + "Power plant maintenance costs": { + "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)" + }, + "Water costs": { + "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)" + }, + "Average Reservoir Pumping Cost": { + "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)" + }, + "Absorption Chiller O&M Cost": { + "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)" + }, + "Average Heat Pump Electricity Cost": { + "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)" + }, + "Annual District Heating O&M Cost": { + "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)", + "description": "", + "units": "MUSD/yr" + }, + "Average Annual Peaking Fuel Cost": { + "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)", + "description": "", + "units": "MUSD/yr" + }, + "Average annual pumping costs": { + "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)" + }, + "Total operating and maintenance costs": { + "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)" + }, + "OPEX": { + "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)" + }, + "Average annual auxiliary fuel cost": { + "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)" + }, + "Average annual pumping cost": { + "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)" + }, + "Total average annual O&M costs": { + "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)" + }, + "Initial geofluid availability": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Maximum Total Electricity Generation": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Average Total Electricity Generation": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Minimum Total Electricity Generation": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Initial Total Electricity Generation": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Maximum Net Electricity Generation": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Average Net Electricity Generation": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Minimum Net Electricity Generation": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Initial Net Electricity Generation": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Average Annual Total Electricity Generation": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Average Annual Net Electricity Generation": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Maximum Net Heat Production": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Average Net Heat Production": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Minimum Net Heat Production": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Initial Net Heat Production": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Average Annual Heat Production": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Average Pumping Power": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Average Annual Heat Pump Electricity Use": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Maximum Cooling Production": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Minimum Cooling Production": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Initial Cooling Production": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Average Annual Cooling Production": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Maximum Daily District Heating Demand": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Average Daily District Heating Demand": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Minimum Daily District Heating Demand": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Maximum Geothermal Heating Production": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Average Geothermal Heating Production": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Minimum Geothermal Heating Production": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Maximum Peaking Boiler Heat Production": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Average Peaking Boiler Heat Production": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Minimum Peaking Boiler Heat Production": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Initial pumping power/net installed power": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Heat to Power Conversion Efficiency": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS", + "description": "First law efficiency average over project lifetime", + "units": "%" + }, + "Surface Plant Cost": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Average RTES Heating Production": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Average Auxiliary Heating Production": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Average Annual RTES Heating Production": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Average Annual Auxiliary Heating Production": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Average Annual Total Heating Production": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "Average Annual Electricity Use for Pumping": { + "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + }, + "GEOPHIRES Version": { + "category": "Simulation Metadata" + } + } +} diff --git a/src/geophires_x_schema_generator/hip-ra-x-request.json b/src/geophires_x_schema_generator/hip-ra-x-request.json index 8420f0be1..054a49dd3 100644 --- a/src/geophires_x_schema_generator/hip-ra-x-request.json +++ b/src/geophires_x_schema_generator/hip-ra-x-request.json @@ -2,7 +2,7 @@ "definitions": {}, "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", - "title": "HIP-RA-X Schema", + "title": "HIP-RA-X Request Schema", "required": [ "Reservoir Temperature", "Rejection Temperature", From 0d7451a4ffe7e79e99953bd80288df037e615d03 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 27 Mar 2025 10:49:35 -0700 Subject: [PATCH 05/24] test result schema moic description --- .../test_geophires_x_schema_generator.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py b/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py index 8e5c65167..b802e2591 100644 --- a/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py +++ b/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py @@ -19,6 +19,10 @@ def test_get_json_schema(self): print(f'Generated result schema: {json.dumps(result_schema, indent=2)}') + self.assertIn( + 'multiple of invested capital', result_schema['properties']['Project MOIC']['description'].lower() + ) + if __name__ == '__main__': unittest.main() From bd9b53e4355bcc849358892602f7c1d95e26a386 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 27 Mar 2025 11:08:15 -0700 Subject: [PATCH 06/24] define OutputParameter.display_name, use to generate schema description for Project NPV --- src/geophires_x/Economics.py | 1 + src/geophires_x/Outputs.py | 4 +- src/geophires_x/Parameter.py | 4 + src/geophires_x_schema_generator/__init__.py | 9 + .../geophires-result.json | 953 +++++++++++++----- 5 files changed, 736 insertions(+), 235 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index 3ea381e1a..c1a7a768d 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -1806,6 +1806,7 @@ def __init__(self, model: Model): self.ProjectNPV = self.OutputParameterDict[self.ProjectNPV.Name] = OutputParameter( "Project Net Present Value", + display_name='Project NPV', UnitType=Units.CURRENCY, PreferredUnits=CurrencyUnit.MDOLLARS, CurrentUnits=CurrencyUnit.MDOLLARS, diff --git a/src/geophires_x/Outputs.py b/src/geophires_x/Outputs.py index c816c4a17..785639887 100644 --- a/src/geophires_x/Outputs.py +++ b/src/geophires_x/Outputs.py @@ -250,8 +250,8 @@ def PrintOutputs(self, model: Model): f.write(f' Project lifetime: {model.surfaceplant.plant_lifetime.value:10.0f} ' + model.surfaceplant.plant_lifetime.CurrentUnits.value + NL) f.write(f' Capacity factor: {model.surfaceplant.utilization_factor.value * 100:10.1f} %' + NL) - e_npv = model.economics.ProjectNPV - npv_field_label = Outputs._field_label('Project NPV', 49) + e_npv: OutputParameter = model.economics.ProjectNPV + npv_field_label = Outputs._field_label(e_npv.display_name, 49) # TODO should use CurrentUnits instead of PreferredUnits f.write(f' {npv_field_label}{e_npv.value:10.2f} {e_npv.PreferredUnits.value}\n') diff --git a/src/geophires_x/Parameter.py b/src/geophires_x/Parameter.py index 3e43dbba2..029cb93e8 100644 --- a/src/geophires_x/Parameter.py +++ b/src/geophires_x/Parameter.py @@ -63,6 +63,7 @@ class OutputParameter(HasQuantity): """ Name: str = "" + display_name:str =None value: Any = 0 ToolTipText: str = "" UnitType: IntEnum = Units.NONE @@ -81,6 +82,9 @@ def with_preferred_units(self) -> Any: # Any is a proxy for Self ret.CurrentUnits = ret.PreferredUnits return ret + def __post_init__(self): + if self.display_name is None: + self.display_name: str = self.Name @dataclass diff --git a/src/geophires_x_schema_generator/__init__.py b/src/geophires_x_schema_generator/__init__.py index 4aaea2781..174c3fd75 100644 --- a/src/geophires_x_schema_generator/__init__.py +++ b/src/geophires_x_schema_generator/__init__.py @@ -146,6 +146,15 @@ def get_result_json_schema(self, output_params_json) -> dict: required = [] output_params = json.loads(output_params_json) + display_name_aliases = {} + for param_name in output_params: + if 'display_name' in output_params[param_name]: + display_name = output_params[param_name]['display_name'] + if display_name not in [None, ''] and display_name != param_name: + # output_params[display_name] = output_params[param_name] + display_name_aliases[display_name] = output_params[param_name] + + output_params = {**output_params, **display_name_aliases} # noinspection PyProtectedMember for category in GeophiresXResult._RESULT_FIELDS_BY_CATEGORY: diff --git a/src/geophires_x_schema_generator/geophires-result.json b/src/geophires_x_schema_generator/geophires-result.json index bc2c10cde..83e662804 100644 --- a/src/geophires_x_schema_generator/geophires-result.json +++ b/src/geophires_x_schema_generator/geophires-result.json @@ -6,735 +6,1222 @@ "required": [], "properties": { "End-Use Option": { - "category": "SUMMARY OF RESULTS" + "categories": [ + "SUMMARY OF RESULTS" + ] }, "End-Use": { - "category": "SUMMARY OF RESULTS" + "categories": [ + "SUMMARY OF RESULTS" + ] }, "Surface Application": { - "category": "SUMMARY OF RESULTS" + "categories": [ + "SUMMARY OF RESULTS" + ] }, "Average Net Electricity Production": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "SUMMARY OF RESULTS", + "RESERVOIR SIMULATION RESULTS" + ] }, "Electricity breakeven price": { - "category": "SUMMARY OF RESULTS" + "categories": [ + "SUMMARY OF RESULTS" + ] }, "Average Direct-Use Heat Production": { - "category": "SUMMARY OF RESULTS" + "categories": [ + "SUMMARY OF RESULTS" + ] }, "Direct-Use heat breakeven price": { - "category": "SUMMARY OF RESULTS" + "categories": [ + "SUMMARY OF RESULTS" + ] }, "Direct-Use heat breakeven price (LCOH)": { - "category": "SUMMARY OF RESULTS" + "categories": [ + "SUMMARY OF RESULTS" + ] }, "Direct-Use Cooling Breakeven Price (LCOC)": { - "category": "SUMMARY OF RESULTS" + "categories": [ + "SUMMARY OF RESULTS" + ] }, "Annual District Heating Demand": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SUMMARY OF RESULTS", + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Average Cooling Production": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SUMMARY OF RESULTS", + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Average Annual Geothermal Heat Production": { - "category": "SUMMARY OF RESULTS" + "categories": [ + "SUMMARY OF RESULTS" + ] }, "Average Annual Peaking Fuel Heat Production": { - "category": "SUMMARY OF RESULTS" + "categories": [ + "SUMMARY OF RESULTS" + ] }, "Direct-Use Cooling Breakeven Price": { - "category": "SUMMARY OF RESULTS" + "categories": [ + "SUMMARY OF RESULTS" + ] }, "Number of production wells": { - "category": "SUMMARY OF RESULTS" + "categories": [ + "SUMMARY OF RESULTS" + ] }, "Number of injection wells": { - "category": "SUMMARY OF RESULTS" + "categories": [ + "SUMMARY OF RESULTS" + ] }, "Flowrate per production well": { - "category": "ENGINEERING PARAMETERS" + "categories": [ + "SUMMARY OF RESULTS", + "ENGINEERING PARAMETERS" + ] }, "Well depth": { - "category": "ENGINEERING PARAMETERS" + "categories": [ + "SUMMARY OF RESULTS", + "ENGINEERING PARAMETERS" + ] }, "Well depth (or total length, if not vertical)": { - "category": "ENGINEERING PARAMETERS" + "categories": [ + "SUMMARY OF RESULTS", + "ENGINEERING PARAMETERS" + ] }, "Geothermal gradient": { - "category": "RESOURCE CHARACTERISTICS" + "categories": [ + "SUMMARY OF RESULTS", + "RESOURCE CHARACTERISTICS" + ] }, "Segment 1 Geothermal gradient": { - "category": "RESOURCE CHARACTERISTICS" + "categories": [ + "SUMMARY OF RESULTS", + "RESOURCE CHARACTERISTICS" + ] }, "Segment 1 Thickness": { - "category": "RESOURCE CHARACTERISTICS" + "categories": [ + "SUMMARY OF RESULTS", + "RESOURCE CHARACTERISTICS" + ] }, "Segment 2 Geothermal gradient": { - "category": "RESOURCE CHARACTERISTICS" + "categories": [ + "SUMMARY OF RESULTS", + "RESOURCE CHARACTERISTICS" + ] }, "Segment 2 Thickness": { - "category": "RESOURCE CHARACTERISTICS" + "categories": [ + "SUMMARY OF RESULTS", + "RESOURCE CHARACTERISTICS" + ] }, "Segment 3 Geothermal gradient": { - "category": "RESOURCE CHARACTERISTICS" + "categories": [ + "SUMMARY OF RESULTS", + "RESOURCE CHARACTERISTICS" + ] }, "Segment 3 Thickness": { - "category": "RESOURCE CHARACTERISTICS" + "categories": [ + "SUMMARY OF RESULTS", + "RESOURCE CHARACTERISTICS" + ] }, "Segment 4 Geothermal gradient": { - "category": "RESOURCE CHARACTERISTICS" + "categories": [ + "SUMMARY OF RESULTS", + "RESOURCE CHARACTERISTICS" + ] }, "LCOE": { - "category": "SUMMARY OF RESULTS", + "categories": [ + "SUMMARY OF RESULTS" + ], "description": "", "units": "cents/kWh" }, "LCOH": { - "category": "SUMMARY OF RESULTS", + "categories": [ + "SUMMARY OF RESULTS" + ], "description": "", "units": "USD/MMBTU" }, "Lifetime Average Well Flow Rate": { - "category": "ENGINEERING PARAMETERS" + "categories": [ + "SUMMARY OF RESULTS", + "ENGINEERING PARAMETERS" + ] }, "Total Avoided Carbon Emissions": { - "category": "SUMMARY OF RESULTS" + "categories": [ + "SUMMARY OF RESULTS" + ] }, "Economic Model": { - "category": "ECONOMIC PARAMETERS" + "categories": [ + "ECONOMIC PARAMETERS" + ] }, "Interest Rate": { - "category": "ECONOMIC PARAMETERS", + "categories": [ + "ECONOMIC PARAMETERS" + ], "description": "", "units": "%" }, "Accrued financing during construction": { - "category": "ECONOMIC PARAMETERS" + "categories": [ + "ECONOMIC PARAMETERS" + ] }, "Project lifetime": { - "category": "ECONOMIC PARAMETERS" + "categories": [ + "ECONOMIC PARAMETERS" + ] }, "Capacity factor": { - "category": "ECONOMIC PARAMETERS" + "categories": [ + "ECONOMIC PARAMETERS" + ] }, "Project NPV": { - "category": "ECONOMIC PARAMETERS" + "categories": [ + "ECONOMIC PARAMETERS" + ], + "description": "NPV is calculated with cashflows lumped at the end of periods. See: Short W et al, 1995. \"A Manual for the Economic Evaluation of Energy Efficiency and Renewable Energy Technologies.\", p. 41. https://www.nrel.gov/docs/legosti/old/5173.pdf", + "units": "MUSD" }, "Project IRR": { - "category": "ECONOMIC PARAMETERS" + "categories": [ + "ECONOMIC PARAMETERS" + ] }, "Project VIR=PI=PIR": { - "category": "ECONOMIC PARAMETERS" + "categories": [ + "ECONOMIC PARAMETERS" + ] }, "Project MOIC": { - "category": "ECONOMIC PARAMETERS", + "categories": [ + "ECONOMIC PARAMETERS" + ], "description": "Project Multiple of Invested Capital", "units": "" }, "Fixed Charge Rate (FCR)": { - "category": "ECONOMIC PARAMETERS" + "categories": [ + "ECONOMIC PARAMETERS" + ] }, "Project Payback Period": { - "category": "ECONOMIC PARAMETERS", + "categories": [ + "ECONOMIC PARAMETERS" + ], "description": "", "units": "yr" }, "CHP: Percent cost allocation for electrical plant": { - "category": "ECONOMIC PARAMETERS" + "categories": [ + "ECONOMIC PARAMETERS" + ] }, "Estimated Jobs Created": { - "category": "ECONOMIC PARAMETERS", + "categories": [ + "ECONOMIC PARAMETERS" + ], "description": "", "units": null }, "Adjusted Project LCOE (after incentives, grants, AddOns,etc)": { - "category": "EXTENDED ECONOMICS" + "categories": [ + "EXTENDED ECONOMICS" + ] }, "Adjusted Project LCOH (after incentives, grants, AddOns,etc)": { - "category": "EXTENDED ECONOMICS" + "categories": [ + "EXTENDED ECONOMICS" + ] }, "Adjusted Project CAPEX (after incentives, grants, AddOns, etc)": { - "category": "EXTENDED ECONOMICS" + "categories": [ + "EXTENDED ECONOMICS" + ] }, "Adjusted Project OPEX (after incentives, grants, AddOns, etc)": { - "category": "EXTENDED ECONOMICS" + "categories": [ + "EXTENDED ECONOMICS" + ] }, "Project NPV (including AddOns)": { - "category": "EXTENDED ECONOMICS" + "categories": [ + "EXTENDED ECONOMICS" + ] }, "Project IRR (including AddOns)": { - "category": "EXTENDED ECONOMICS" + "categories": [ + "EXTENDED ECONOMICS" + ] }, "Project VIR=PI=PIR (including AddOns)": { - "category": "EXTENDED ECONOMICS" + "categories": [ + "EXTENDED ECONOMICS" + ] }, "Project MOIC (including AddOns)": { - "category": "EXTENDED ECONOMICS" + "categories": [ + "EXTENDED ECONOMICS" + ] }, "Project Payback Period (including AddOns)": { - "category": "EXTENDED ECONOMICS" + "categories": [ + "EXTENDED ECONOMICS" + ] }, "Total Add-on CAPEX": { - "category": "EXTENDED ECONOMICS" + "categories": [ + "EXTENDED ECONOMICS" + ] }, "Total Add-on OPEX": { - "category": "EXTENDED ECONOMICS" + "categories": [ + "EXTENDED ECONOMICS" + ] }, "Total Add-on Net Elec": { - "category": "EXTENDED ECONOMICS" + "categories": [ + "EXTENDED ECONOMICS" + ] }, "Total Add-on Net Heat": { - "category": "EXTENDED ECONOMICS" + "categories": [ + "EXTENDED ECONOMICS" + ] }, "Total Add-on Profit": { - "category": "EXTENDED ECONOMICS" + "categories": [ + "EXTENDED ECONOMICS" + ] }, "AddOns Payback Period": { - "category": "EXTENDED ECONOMICS" + "categories": [ + "EXTENDED ECONOMICS" + ] }, "Total Avoided Carbon Production": { - "category": "CCUS ECONOMICS" + "categories": [ + "CCUS ECONOMICS" + ] }, "Project NPV (including carbon credit)": { - "category": "CCUS ECONOMICS" + "categories": [ + "CCUS ECONOMICS" + ] }, "Project IRR (including carbon credit)": { - "category": "CCUS ECONOMICS" + "categories": [ + "CCUS ECONOMICS" + ] }, "Project VIR=IR=PIR (including carbon credit)": { - "category": "CCUS ECONOMICS" + "categories": [ + "CCUS ECONOMICS" + ] }, "Project MOIC (including carbon credit)": { - "category": "CCUS ECONOMICS" + "categories": [ + "CCUS ECONOMICS" + ] }, "Project Payback Period (including carbon credit)": { - "category": "CCUS ECONOMICS" + "categories": [ + "CCUS ECONOMICS" + ] }, "LCOD using grid-based electricity only": { - "category": "S-DAC-GT ECONOMICS" + "categories": [ + "S-DAC-GT ECONOMICS" + ] }, "LCOD using natural gas only": { - "category": "S-DAC-GT ECONOMICS" + "categories": [ + "S-DAC-GT ECONOMICS" + ] }, "LCOD using geothermal energy only": { - "category": "S-DAC-GT ECONOMICS" + "categories": [ + "S-DAC-GT ECONOMICS" + ] }, "CO2 Intensity using grid-based electricity only": { - "category": "S-DAC-GT ECONOMICS" + "categories": [ + "S-DAC-GT ECONOMICS" + ] }, "CO2 Intensity using natural gas only": { - "category": "S-DAC-GT ECONOMICS" + "categories": [ + "S-DAC-GT ECONOMICS" + ] }, "CO2 Intensity using geothermal energy only": { - "category": "S-DAC-GT ECONOMICS" + "categories": [ + "S-DAC-GT ECONOMICS" + ] }, "Geothermal LCOH": { - "category": "S-DAC-GT ECONOMICS" + "categories": [ + "S-DAC-GT ECONOMICS" + ] }, "Geothermal Ratio (electricity vs heat)": { - "category": "S-DAC-GT ECONOMICS" + "categories": [ + "S-DAC-GT ECONOMICS" + ] }, "Percent Energy Devoted To Process": { - "category": "S-DAC-GT ECONOMICS" + "categories": [ + "S-DAC-GT ECONOMICS" + ] }, "Total Cost of Capture": { - "category": "S-DAC-GT ECONOMICS" + "categories": [ + "S-DAC-GT ECONOMICS" + ] }, "Number of Production Wells": { - "category": "ENGINEERING PARAMETERS" + "categories": [ + "ENGINEERING PARAMETERS" + ] }, "Number of Injection Wells": { - "category": "ENGINEERING PARAMETERS" + "categories": [ + "ENGINEERING PARAMETERS" + ] }, "Water loss rate": { - "category": "ENGINEERING PARAMETERS" + "categories": [ + "ENGINEERING PARAMETERS" + ] }, "Pump efficiency": { - "category": "ENGINEERING PARAMETERS" + "categories": [ + "ENGINEERING PARAMETERS" + ] }, "Injection temperature": { - "category": "ENGINEERING PARAMETERS" + "categories": [ + "ENGINEERING PARAMETERS" + ] }, "Injection Temperature": { - "category": "ENGINEERING PARAMETERS", + "categories": [ + "ENGINEERING PARAMETERS" + ], "description": "", "units": "degC" }, "Average production well temperature drop": { - "category": "ENGINEERING PARAMETERS" + "categories": [ + "ENGINEERING PARAMETERS" + ] }, "Injection well casing ID": { - "category": "ENGINEERING PARAMETERS" + "categories": [ + "ENGINEERING PARAMETERS", + "ENGINEERING PARAMETERS" + ] }, "Production well casing ID": { - "category": "ENGINEERING PARAMETERS" + "categories": [ + "ENGINEERING PARAMETERS", + "ENGINEERING PARAMETERS" + ] }, "Number of times redrilling": { - "category": "ENGINEERING PARAMETERS" + "categories": [ + "ENGINEERING PARAMETERS" + ] }, "Power plant type": { - "category": "ENGINEERING PARAMETERS" + "categories": [ + "ENGINEERING PARAMETERS" + ] }, "Fluid": { - "category": "ENGINEERING PARAMETERS" + "categories": [ + "ENGINEERING PARAMETERS" + ] }, "Design": { - "category": "ENGINEERING PARAMETERS" + "categories": [ + "ENGINEERING PARAMETERS" + ] }, "Flow rate": { - "category": "ENGINEERING PARAMETERS" + "categories": [ + "ENGINEERING PARAMETERS" + ] }, "Lateral Length": { - "category": "ENGINEERING PARAMETERS" + "categories": [ + "ENGINEERING PARAMETERS" + ] }, "Vertical Depth": { - "category": "ENGINEERING PARAMETERS" + "categories": [ + "ENGINEERING PARAMETERS" + ] }, "Wellbore Diameter": { - "category": "ENGINEERING PARAMETERS" + "categories": [ + "ENGINEERING PARAMETERS" + ] }, "Maximum reservoir temperature": { - "category": "RESOURCE CHARACTERISTICS" + "categories": [ + "RESOURCE CHARACTERISTICS" + ] }, "Number of segments": { - "category": "RESOURCE CHARACTERISTICS" + "categories": [ + "RESOURCE CHARACTERISTICS" + ] }, "Reservoir Model": { - "category": "RESERVOIR PARAMETERS" + "categories": [ + "RESERVOIR PARAMETERS" + ] }, "Fracture model": { - "category": "RESERVOIR PARAMETERS" + "categories": [ + "RESERVOIR PARAMETERS" + ] }, "Bottom-hole temperature": { - "category": "RESERVOIR PARAMETERS", + "categories": [ + "RESERVOIR PARAMETERS" + ], "description": "", "units": "degC" }, "Well separation: fracture diameter": { - "category": "RESERVOIR PARAMETERS" + "categories": [ + "RESERVOIR PARAMETERS" + ] }, "Well separation: fracture height": { - "category": "RESERVOIR PARAMETERS" + "categories": [ + "RESERVOIR PARAMETERS" + ] }, "Fracture width": { - "category": "RESERVOIR PARAMETERS" + "categories": [ + "RESERVOIR PARAMETERS" + ] }, "Fracture area": { - "category": "RESERVOIR PARAMETERS" + "categories": [ + "RESERVOIR PARAMETERS" + ] }, "Number of fractures": { - "category": "RESERVOIR PARAMETERS" + "categories": [ + "RESERVOIR PARAMETERS" + ] }, "Fracture separation": { - "category": "RESERVOIR PARAMETERS" + "categories": [ + "RESERVOIR PARAMETERS" + ] }, "Reservoir volume": { - "category": "RESERVOIR PARAMETERS" + "categories": [ + "RESERVOIR PARAMETERS" + ] }, "Reservoir impedance": { - "category": "RESERVOIR PARAMETERS" + "categories": [ + "RESERVOIR PARAMETERS" + ] }, "Reservoir hydrostatic pressure": { - "category": "RESERVOIR PARAMETERS" + "categories": [ + "RESERVOIR PARAMETERS" + ] }, "Average reservoir pressure": { - "category": "RESERVOIR PARAMETERS" + "categories": [ + "RESERVOIR PARAMETERS" + ] }, "Plant outlet pressure": { - "category": "RESERVOIR PARAMETERS" + "categories": [ + "RESERVOIR PARAMETERS" + ] }, "Production wellhead pressure": { - "category": "RESERVOIR PARAMETERS", + "categories": [ + "RESERVOIR PARAMETERS" + ], "description": "", "units": "kPa" }, "Productivity Index": { - "category": "RESERVOIR PARAMETERS" + "categories": [ + "RESERVOIR PARAMETERS" + ] }, "Injectivity Index": { - "category": "RESERVOIR PARAMETERS" + "categories": [ + "RESERVOIR PARAMETERS" + ] }, "Reservoir density": { - "category": "RESERVOIR PARAMETERS" + "categories": [ + "RESERVOIR PARAMETERS" + ] }, "Reservoir thermal conductivity": { - "category": "RESERVOIR PARAMETERS" + "categories": [ + "RESERVOIR PARAMETERS" + ] }, "Reservoir heat capacity": { - "category": "RESERVOIR PARAMETERS" + "categories": [ + "RESERVOIR PARAMETERS" + ] }, "Reservoir porosity": { - "category": "RESERVOIR PARAMETERS" + "categories": [ + "RESERVOIR PARAMETERS" + ] }, "Thermal Conductivity": { - "category": "RESERVOIR PARAMETERS" + "categories": [ + "RESERVOIR PARAMETERS" + ] }, "Maximum Production Temperature": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Average Production Temperature": { - "category": "RESERVOIR SIMULATION RESULTS", + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ], "description": "", "units": "degC" }, "Minimum Production Temperature": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Initial Production Temperature": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Average Reservoir Heat Extraction": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Production Wellbore Heat Transmission Model": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Wellbore Heat Transmission Model": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Average Production Well Temperature Drop": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Total Average Pressure Drop": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Average Injection Well Pressure Drop": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Average Production Pressure": { - "category": "RESERVOIR SIMULATION RESULTS", + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ], "description": "", "units": "bar" }, "Average Reservoir Pressure Drop": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Average Production Well Pressure Drop": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Average Buoyancy Pressure Drop": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Average Injection Well Pump Pressure Drop": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Average Production Well Pump Pressure Drop": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Average Heat Production": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "First Year Heat Production": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "First Year Electricity Production": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Maximum Storage Well Temperature": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Average Storage Well Temperature": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Minimum Storage Well Temperature": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Maximum Balance Well Temperature": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Average Balance Well Temperature": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Minimum Balance Well Temperature": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Maximum Annual Heat Stored": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Average Annual Heat Stored": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Minimum Annual Heat Stored": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Maximum Annual Heat Supplied": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Average Annual Heat Supplied": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Minimum Annual Heat Supplied": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Average Round-Trip Efficiency": { - "category": "RESERVOIR SIMULATION RESULTS" + "categories": [ + "RESERVOIR SIMULATION RESULTS" + ] }, "Drilling and completion costs": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "Drilling and completion costs per well": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "Drilling and completion costs per production well": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)", + "CAPITAL COSTS (M$)" + ] }, "Drilling and completion costs per injection well": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)", + "CAPITAL COSTS (M$)" + ] }, "Drilling and completion costs per vertical production well": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "Drilling and completion costs per vertical injection well": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "Drilling and completion costs per non-vertical section": { - "category": "CAPITAL COSTS (M$)", + "categories": [ + "CAPITAL COSTS (M$)" + ], "description": "", "units": "MUSD" }, "Drilling and completion costs (for redrilling)": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "Drilling and completion costs per redrilled well": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "Stimulation costs": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "Stimulation costs (for redrilling)": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "Surface power plant costs": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "of which Absorption Chiller Cost": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "of which Heat Pump Cost": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "of which Peaking Boiler Cost": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "Transmission pipeline cost": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "District Heating System Cost": { - "category": "CAPITAL COSTS (M$)", + "categories": [ + "CAPITAL COSTS (M$)" + ], "description": "", "units": "MUSD" }, "Field gathering system costs": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "Total surface equipment costs": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "Exploration costs": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "Investment Tax Credit": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "Total capital costs": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "Annualized capital costs": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "Total CAPEX": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "Drilling Cost": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "Drilling and Completion Costs": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "Drilling and Completion Costs per Well": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "Auxiliary Heater Cost": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "Pump Cost": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "Total Capital Costs": { - "category": "CAPITAL COSTS (M$)" + "categories": [ + "CAPITAL COSTS (M$)" + ] }, "Wellfield maintenance costs": { - "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)" + "categories": [ + "OPERATING AND MAINTENANCE COSTS (M$/yr)" + ] }, "Power plant maintenance costs": { - "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)" + "categories": [ + "OPERATING AND MAINTENANCE COSTS (M$/yr)" + ] }, "Water costs": { - "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)" + "categories": [ + "OPERATING AND MAINTENANCE COSTS (M$/yr)" + ] }, "Average Reservoir Pumping Cost": { - "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)" + "categories": [ + "OPERATING AND MAINTENANCE COSTS (M$/yr)" + ] }, "Absorption Chiller O&M Cost": { - "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)" + "categories": [ + "OPERATING AND MAINTENANCE COSTS (M$/yr)" + ] }, "Average Heat Pump Electricity Cost": { - "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)" + "categories": [ + "OPERATING AND MAINTENANCE COSTS (M$/yr)" + ] }, "Annual District Heating O&M Cost": { - "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)", + "categories": [ + "OPERATING AND MAINTENANCE COSTS (M$/yr)" + ], "description": "", "units": "MUSD/yr" }, "Average Annual Peaking Fuel Cost": { - "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)", + "categories": [ + "OPERATING AND MAINTENANCE COSTS (M$/yr)" + ], "description": "", "units": "MUSD/yr" }, "Average annual pumping costs": { - "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)" + "categories": [ + "OPERATING AND MAINTENANCE COSTS (M$/yr)" + ] }, "Total operating and maintenance costs": { - "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)" + "categories": [ + "OPERATING AND MAINTENANCE COSTS (M$/yr)" + ] }, "OPEX": { - "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)" + "categories": [ + "OPERATING AND MAINTENANCE COSTS (M$/yr)" + ] }, "Average annual auxiliary fuel cost": { - "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)" + "categories": [ + "OPERATING AND MAINTENANCE COSTS (M$/yr)" + ] }, "Average annual pumping cost": { - "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)" + "categories": [ + "OPERATING AND MAINTENANCE COSTS (M$/yr)" + ] }, "Total average annual O&M costs": { - "category": "OPERATING AND MAINTENANCE COSTS (M$/yr)" + "categories": [ + "OPERATING AND MAINTENANCE COSTS (M$/yr)" + ] }, "Initial geofluid availability": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Maximum Total Electricity Generation": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Average Total Electricity Generation": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Minimum Total Electricity Generation": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Initial Total Electricity Generation": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Maximum Net Electricity Generation": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Average Net Electricity Generation": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Minimum Net Electricity Generation": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Initial Net Electricity Generation": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Average Annual Total Electricity Generation": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Average Annual Net Electricity Generation": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Maximum Net Heat Production": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Average Net Heat Production": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Minimum Net Heat Production": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Initial Net Heat Production": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Average Annual Heat Production": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Average Pumping Power": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Average Annual Heat Pump Electricity Use": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Maximum Cooling Production": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Minimum Cooling Production": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Initial Cooling Production": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Average Annual Cooling Production": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Maximum Daily District Heating Demand": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Average Daily District Heating Demand": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Minimum Daily District Heating Demand": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Maximum Geothermal Heating Production": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Average Geothermal Heating Production": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Minimum Geothermal Heating Production": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Maximum Peaking Boiler Heat Production": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Average Peaking Boiler Heat Production": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Minimum Peaking Boiler Heat Production": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Initial pumping power/net installed power": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Heat to Power Conversion Efficiency": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS", + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ], "description": "First law efficiency average over project lifetime", "units": "%" }, "Surface Plant Cost": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Average RTES Heating Production": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Average Auxiliary Heating Production": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Average Annual RTES Heating Production": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Average Annual Auxiliary Heating Production": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Average Annual Total Heating Production": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "Average Annual Electricity Use for Pumping": { - "category": "SURFACE EQUIPMENT SIMULATION RESULTS" + "categories": [ + "SURFACE EQUIPMENT SIMULATION RESULTS" + ] }, "GEOPHIRES Version": { - "category": "Simulation Metadata" + "categories": [ + "Simulation Metadata" + ] } } } From 545d7481f09df38e0bbad6ffcbc9f3f3efbcaec3 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 27 Mar 2025 11:15:47 -0700 Subject: [PATCH 07/24] include 'original' output parameter name in description if parameter name is a display name --- src/geophires_x/Economics.py | 1 + src/geophires_x_schema_generator/__init__.py | 6 +++++- src/geophires_x_schema_generator/geophires-result.json | 6 ++++-- .../test_geophires_x_schema_generator.py | 2 ++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index c1a7a768d..295c69c14 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -1607,6 +1607,7 @@ def __init__(self, model: Model): self.Cwell = self.OutputParameterDict[self.Cwell.Name] = OutputParameter( Name="Wellfield cost", + display_name='Drilling and completion costs', UnitType=Units.CURRENCY, PreferredUnits=CurrencyUnit.MDOLLARS, CurrentUnits=CurrencyUnit.MDOLLARS, diff --git a/src/geophires_x_schema_generator/__init__.py b/src/geophires_x_schema_generator/__init__.py index 174c3fd75..ef30d26c6 100644 --- a/src/geophires_x_schema_generator/__init__.py +++ b/src/geophires_x_schema_generator/__init__.py @@ -153,6 +153,7 @@ def get_result_json_schema(self, output_params_json) -> dict: if display_name not in [None, ''] and display_name != param_name: # output_params[display_name] = output_params[param_name] display_name_aliases[display_name] = output_params[param_name] + display_name_aliases[display_name]['output_parameter_name'] = param_name output_params = {**output_params, **display_name_aliases} @@ -177,7 +178,10 @@ def get_result_json_schema(self, output_params_json) -> dict: if param_name in output_params: output_param = output_params[param_name] - param['description'] = output_param['ToolTipText'] + description = output_param['ToolTipText'] + if 'output_parameter_name' in output_param: + description = f'{output_param["output_parameter_name"]}. {description}' + param['description'] = description param['units'] = ( output_param['CurrentUnits'] if isinstance(output_param['CurrentUnits'], str) else None ) diff --git a/src/geophires_x_schema_generator/geophires-result.json b/src/geophires_x_schema_generator/geophires-result.json index 83e662804..b64137361 100644 --- a/src/geophires_x_schema_generator/geophires-result.json +++ b/src/geophires_x_schema_generator/geophires-result.json @@ -210,7 +210,7 @@ "categories": [ "ECONOMIC PARAMETERS" ], - "description": "NPV is calculated with cashflows lumped at the end of periods. See: Short W et al, 1995. \"A Manual for the Economic Evaluation of Energy Efficiency and Renewable Energy Technologies.\", p. 41. https://www.nrel.gov/docs/legosti/old/5173.pdf", + "description": "Project Net Present Value. NPV is calculated with cashflows lumped at the end of periods. See: Short W et al, 1995. \"A Manual for the Economic Evaluation of Energy Efficiency and Renewable Energy Technologies.\", p. 41. https://www.nrel.gov/docs/legosti/old/5173.pdf", "units": "MUSD" }, "Project IRR": { @@ -789,7 +789,9 @@ "Drilling and completion costs": { "categories": [ "CAPITAL COSTS (M$)" - ] + ], + "description": "Wellfield cost. Includes total drilling and completion cost of all injection and production wells and laterals, plus 5% indirect costs.", + "units": "MUSD" }, "Drilling and completion costs per well": { "categories": [ diff --git a/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py b/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py index b802e2591..d67a04d7e 100644 --- a/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py +++ b/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py @@ -23,6 +23,8 @@ def test_get_json_schema(self): 'multiple of invested capital', result_schema['properties']['Project MOIC']['description'].lower() ) + self.assertIn('Wellfield cost. ', result_schema['properties']['Drilling and completion costs']['description']) + if __name__ == '__main__': unittest.main() From 7fc1fef7a5b12fffc7a9cfdad90104939f90f6a4 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 27 Mar 2025 11:17:48 -0700 Subject: [PATCH 08/24] FIXME TODO re: consolidating output params rst table with (newly added) generated result schema --- src/geophires_x_schema_generator/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/geophires_x_schema_generator/__init__.py b/src/geophires_x_schema_generator/__init__.py index ef30d26c6..2936201b1 100644 --- a/src/geophires_x_schema_generator/__init__.py +++ b/src/geophires_x_schema_generator/__init__.py @@ -271,6 +271,10 @@ def get_input_params_table(_category_params, category_name) -> str: @staticmethod def get_output_params_table_rst(output_params_json) -> str: + """ + FIXME TODO consolidate with generated result schema + """ + output_params = json.loads(output_params_json) output_rst = """ From 4547c3beb06e2c5c9c2d954e1797527ca09efef3 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 4 Apr 2025 10:59:01 -0700 Subject: [PATCH 09/24] Nest result objects instead of assigning multiple categories (aligns better with OpenAPI schema standard) --- src/geophires_x_schema_generator/__init__.py | 10 +- .../geophires-result.json | 1585 ++++------------- .../test_geophires_x_schema_generator.py | 10 +- 3 files changed, 381 insertions(+), 1224 deletions(-) diff --git a/src/geophires_x_schema_generator/__init__.py b/src/geophires_x_schema_generator/__init__.py index 2936201b1..7faa16de7 100644 --- a/src/geophires_x_schema_generator/__init__.py +++ b/src/geophires_x_schema_generator/__init__.py @@ -159,6 +159,7 @@ def get_result_json_schema(self, output_params_json) -> dict: # noinspection PyProtectedMember for category in GeophiresXResult._RESULT_FIELDS_BY_CATEGORY: + cat_properties = {} # noinspection PyProtectedMember for field in GeophiresXResult._RESULT_FIELDS_BY_CATEGORY[category]: param_name = field if isinstance(field, str) else field.field_name @@ -168,13 +169,13 @@ def get_result_json_schema(self, output_params_json) -> dict: param = ( { - 'categories': [], + # 'categories': [], } if param_name not in properties else properties[param_name] ) - param['categories'].append(category) + # param['categories'].append(category) if param_name in output_params: output_param = output_params[param_name] @@ -186,7 +187,10 @@ def get_result_json_schema(self, output_params_json) -> dict: output_param['CurrentUnits'] if isinstance(output_param['CurrentUnits'], str) else None ) - properties[param_name] = param.copy() + # properties[param_name] = param.copy() + cat_properties[param_name] = param.copy() + + properties[category] = {'type': 'object', 'properties': cat_properties} result_schema = { 'definitions': {}, diff --git a/src/geophires_x_schema_generator/geophires-result.json b/src/geophires_x_schema_generator/geophires-result.json index b64137361..38c15f49b 100644 --- a/src/geophires_x_schema_generator/geophires-result.json +++ b/src/geophires_x_schema_generator/geophires-result.json @@ -5,1225 +5,372 @@ "title": "GEOPHIRES-X Result Schema", "required": [], "properties": { - "End-Use Option": { - "categories": [ - "SUMMARY OF RESULTS" - ] - }, - "End-Use": { - "categories": [ - "SUMMARY OF RESULTS" - ] - }, - "Surface Application": { - "categories": [ - "SUMMARY OF RESULTS" - ] - }, - "Average Net Electricity Production": { - "categories": [ - "SUMMARY OF RESULTS", - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Electricity breakeven price": { - "categories": [ - "SUMMARY OF RESULTS" - ] - }, - "Average Direct-Use Heat Production": { - "categories": [ - "SUMMARY OF RESULTS" - ] - }, - "Direct-Use heat breakeven price": { - "categories": [ - "SUMMARY OF RESULTS" - ] - }, - "Direct-Use heat breakeven price (LCOH)": { - "categories": [ - "SUMMARY OF RESULTS" - ] - }, - "Direct-Use Cooling Breakeven Price (LCOC)": { - "categories": [ - "SUMMARY OF RESULTS" - ] - }, - "Annual District Heating Demand": { - "categories": [ - "SUMMARY OF RESULTS", - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Average Cooling Production": { - "categories": [ - "SUMMARY OF RESULTS", - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Average Annual Geothermal Heat Production": { - "categories": [ - "SUMMARY OF RESULTS" - ] - }, - "Average Annual Peaking Fuel Heat Production": { - "categories": [ - "SUMMARY OF RESULTS" - ] - }, - "Direct-Use Cooling Breakeven Price": { - "categories": [ - "SUMMARY OF RESULTS" - ] - }, - "Number of production wells": { - "categories": [ - "SUMMARY OF RESULTS" - ] - }, - "Number of injection wells": { - "categories": [ - "SUMMARY OF RESULTS" - ] - }, - "Flowrate per production well": { - "categories": [ - "SUMMARY OF RESULTS", - "ENGINEERING PARAMETERS" - ] - }, - "Well depth": { - "categories": [ - "SUMMARY OF RESULTS", - "ENGINEERING PARAMETERS" - ] - }, - "Well depth (or total length, if not vertical)": { - "categories": [ - "SUMMARY OF RESULTS", - "ENGINEERING PARAMETERS" - ] - }, - "Geothermal gradient": { - "categories": [ - "SUMMARY OF RESULTS", - "RESOURCE CHARACTERISTICS" - ] - }, - "Segment 1 Geothermal gradient": { - "categories": [ - "SUMMARY OF RESULTS", - "RESOURCE CHARACTERISTICS" - ] - }, - "Segment 1 Thickness": { - "categories": [ - "SUMMARY OF RESULTS", - "RESOURCE CHARACTERISTICS" - ] - }, - "Segment 2 Geothermal gradient": { - "categories": [ - "SUMMARY OF RESULTS", - "RESOURCE CHARACTERISTICS" - ] - }, - "Segment 2 Thickness": { - "categories": [ - "SUMMARY OF RESULTS", - "RESOURCE CHARACTERISTICS" - ] - }, - "Segment 3 Geothermal gradient": { - "categories": [ - "SUMMARY OF RESULTS", - "RESOURCE CHARACTERISTICS" - ] - }, - "Segment 3 Thickness": { - "categories": [ - "SUMMARY OF RESULTS", - "RESOURCE CHARACTERISTICS" - ] - }, - "Segment 4 Geothermal gradient": { - "categories": [ - "SUMMARY OF RESULTS", - "RESOURCE CHARACTERISTICS" - ] - }, - "LCOE": { - "categories": [ - "SUMMARY OF RESULTS" - ], - "description": "", - "units": "cents/kWh" - }, - "LCOH": { - "categories": [ - "SUMMARY OF RESULTS" - ], - "description": "", - "units": "USD/MMBTU" - }, - "Lifetime Average Well Flow Rate": { - "categories": [ - "SUMMARY OF RESULTS", - "ENGINEERING PARAMETERS" - ] - }, - "Total Avoided Carbon Emissions": { - "categories": [ - "SUMMARY OF RESULTS" - ] - }, - "Economic Model": { - "categories": [ - "ECONOMIC PARAMETERS" - ] - }, - "Interest Rate": { - "categories": [ - "ECONOMIC PARAMETERS" - ], - "description": "", - "units": "%" - }, - "Accrued financing during construction": { - "categories": [ - "ECONOMIC PARAMETERS" - ] - }, - "Project lifetime": { - "categories": [ - "ECONOMIC PARAMETERS" - ] - }, - "Capacity factor": { - "categories": [ - "ECONOMIC PARAMETERS" - ] - }, - "Project NPV": { - "categories": [ - "ECONOMIC PARAMETERS" - ], - "description": "Project Net Present Value. NPV is calculated with cashflows lumped at the end of periods. See: Short W et al, 1995. \"A Manual for the Economic Evaluation of Energy Efficiency and Renewable Energy Technologies.\", p. 41. https://www.nrel.gov/docs/legosti/old/5173.pdf", - "units": "MUSD" - }, - "Project IRR": { - "categories": [ - "ECONOMIC PARAMETERS" - ] - }, - "Project VIR=PI=PIR": { - "categories": [ - "ECONOMIC PARAMETERS" - ] - }, - "Project MOIC": { - "categories": [ - "ECONOMIC PARAMETERS" - ], - "description": "Project Multiple of Invested Capital", - "units": "" - }, - "Fixed Charge Rate (FCR)": { - "categories": [ - "ECONOMIC PARAMETERS" - ] - }, - "Project Payback Period": { - "categories": [ - "ECONOMIC PARAMETERS" - ], - "description": "", - "units": "yr" - }, - "CHP: Percent cost allocation for electrical plant": { - "categories": [ - "ECONOMIC PARAMETERS" - ] - }, - "Estimated Jobs Created": { - "categories": [ - "ECONOMIC PARAMETERS" - ], - "description": "", - "units": null - }, - "Adjusted Project LCOE (after incentives, grants, AddOns,etc)": { - "categories": [ - "EXTENDED ECONOMICS" - ] - }, - "Adjusted Project LCOH (after incentives, grants, AddOns,etc)": { - "categories": [ - "EXTENDED ECONOMICS" - ] - }, - "Adjusted Project CAPEX (after incentives, grants, AddOns, etc)": { - "categories": [ - "EXTENDED ECONOMICS" - ] - }, - "Adjusted Project OPEX (after incentives, grants, AddOns, etc)": { - "categories": [ - "EXTENDED ECONOMICS" - ] - }, - "Project NPV (including AddOns)": { - "categories": [ - "EXTENDED ECONOMICS" - ] - }, - "Project IRR (including AddOns)": { - "categories": [ - "EXTENDED ECONOMICS" - ] - }, - "Project VIR=PI=PIR (including AddOns)": { - "categories": [ - "EXTENDED ECONOMICS" - ] - }, - "Project MOIC (including AddOns)": { - "categories": [ - "EXTENDED ECONOMICS" - ] - }, - "Project Payback Period (including AddOns)": { - "categories": [ - "EXTENDED ECONOMICS" - ] - }, - "Total Add-on CAPEX": { - "categories": [ - "EXTENDED ECONOMICS" - ] - }, - "Total Add-on OPEX": { - "categories": [ - "EXTENDED ECONOMICS" - ] - }, - "Total Add-on Net Elec": { - "categories": [ - "EXTENDED ECONOMICS" - ] - }, - "Total Add-on Net Heat": { - "categories": [ - "EXTENDED ECONOMICS" - ] - }, - "Total Add-on Profit": { - "categories": [ - "EXTENDED ECONOMICS" - ] - }, - "AddOns Payback Period": { - "categories": [ - "EXTENDED ECONOMICS" - ] - }, - "Total Avoided Carbon Production": { - "categories": [ - "CCUS ECONOMICS" - ] - }, - "Project NPV (including carbon credit)": { - "categories": [ - "CCUS ECONOMICS" - ] - }, - "Project IRR (including carbon credit)": { - "categories": [ - "CCUS ECONOMICS" - ] - }, - "Project VIR=IR=PIR (including carbon credit)": { - "categories": [ - "CCUS ECONOMICS" - ] - }, - "Project MOIC (including carbon credit)": { - "categories": [ - "CCUS ECONOMICS" - ] - }, - "Project Payback Period (including carbon credit)": { - "categories": [ - "CCUS ECONOMICS" - ] - }, - "LCOD using grid-based electricity only": { - "categories": [ - "S-DAC-GT ECONOMICS" - ] - }, - "LCOD using natural gas only": { - "categories": [ - "S-DAC-GT ECONOMICS" - ] - }, - "LCOD using geothermal energy only": { - "categories": [ - "S-DAC-GT ECONOMICS" - ] - }, - "CO2 Intensity using grid-based electricity only": { - "categories": [ - "S-DAC-GT ECONOMICS" - ] - }, - "CO2 Intensity using natural gas only": { - "categories": [ - "S-DAC-GT ECONOMICS" - ] - }, - "CO2 Intensity using geothermal energy only": { - "categories": [ - "S-DAC-GT ECONOMICS" - ] - }, - "Geothermal LCOH": { - "categories": [ - "S-DAC-GT ECONOMICS" - ] - }, - "Geothermal Ratio (electricity vs heat)": { - "categories": [ - "S-DAC-GT ECONOMICS" - ] - }, - "Percent Energy Devoted To Process": { - "categories": [ - "S-DAC-GT ECONOMICS" - ] - }, - "Total Cost of Capture": { - "categories": [ - "S-DAC-GT ECONOMICS" - ] - }, - "Number of Production Wells": { - "categories": [ - "ENGINEERING PARAMETERS" - ] - }, - "Number of Injection Wells": { - "categories": [ - "ENGINEERING PARAMETERS" - ] - }, - "Water loss rate": { - "categories": [ - "ENGINEERING PARAMETERS" - ] - }, - "Pump efficiency": { - "categories": [ - "ENGINEERING PARAMETERS" - ] - }, - "Injection temperature": { - "categories": [ - "ENGINEERING PARAMETERS" - ] - }, - "Injection Temperature": { - "categories": [ - "ENGINEERING PARAMETERS" - ], - "description": "", - "units": "degC" - }, - "Average production well temperature drop": { - "categories": [ - "ENGINEERING PARAMETERS" - ] - }, - "Injection well casing ID": { - "categories": [ - "ENGINEERING PARAMETERS", - "ENGINEERING PARAMETERS" - ] - }, - "Production well casing ID": { - "categories": [ - "ENGINEERING PARAMETERS", - "ENGINEERING PARAMETERS" - ] - }, - "Number of times redrilling": { - "categories": [ - "ENGINEERING PARAMETERS" - ] - }, - "Power plant type": { - "categories": [ - "ENGINEERING PARAMETERS" - ] - }, - "Fluid": { - "categories": [ - "ENGINEERING PARAMETERS" - ] - }, - "Design": { - "categories": [ - "ENGINEERING PARAMETERS" - ] - }, - "Flow rate": { - "categories": [ - "ENGINEERING PARAMETERS" - ] - }, - "Lateral Length": { - "categories": [ - "ENGINEERING PARAMETERS" - ] - }, - "Vertical Depth": { - "categories": [ - "ENGINEERING PARAMETERS" - ] - }, - "Wellbore Diameter": { - "categories": [ - "ENGINEERING PARAMETERS" - ] - }, - "Maximum reservoir temperature": { - "categories": [ - "RESOURCE CHARACTERISTICS" - ] - }, - "Number of segments": { - "categories": [ - "RESOURCE CHARACTERISTICS" - ] - }, - "Reservoir Model": { - "categories": [ - "RESERVOIR PARAMETERS" - ] - }, - "Fracture model": { - "categories": [ - "RESERVOIR PARAMETERS" - ] - }, - "Bottom-hole temperature": { - "categories": [ - "RESERVOIR PARAMETERS" - ], - "description": "", - "units": "degC" - }, - "Well separation: fracture diameter": { - "categories": [ - "RESERVOIR PARAMETERS" - ] - }, - "Well separation: fracture height": { - "categories": [ - "RESERVOIR PARAMETERS" - ] - }, - "Fracture width": { - "categories": [ - "RESERVOIR PARAMETERS" - ] - }, - "Fracture area": { - "categories": [ - "RESERVOIR PARAMETERS" - ] - }, - "Number of fractures": { - "categories": [ - "RESERVOIR PARAMETERS" - ] - }, - "Fracture separation": { - "categories": [ - "RESERVOIR PARAMETERS" - ] - }, - "Reservoir volume": { - "categories": [ - "RESERVOIR PARAMETERS" - ] - }, - "Reservoir impedance": { - "categories": [ - "RESERVOIR PARAMETERS" - ] - }, - "Reservoir hydrostatic pressure": { - "categories": [ - "RESERVOIR PARAMETERS" - ] - }, - "Average reservoir pressure": { - "categories": [ - "RESERVOIR PARAMETERS" - ] - }, - "Plant outlet pressure": { - "categories": [ - "RESERVOIR PARAMETERS" - ] - }, - "Production wellhead pressure": { - "categories": [ - "RESERVOIR PARAMETERS" - ], - "description": "", - "units": "kPa" - }, - "Productivity Index": { - "categories": [ - "RESERVOIR PARAMETERS" - ] - }, - "Injectivity Index": { - "categories": [ - "RESERVOIR PARAMETERS" - ] - }, - "Reservoir density": { - "categories": [ - "RESERVOIR PARAMETERS" - ] - }, - "Reservoir thermal conductivity": { - "categories": [ - "RESERVOIR PARAMETERS" - ] - }, - "Reservoir heat capacity": { - "categories": [ - "RESERVOIR PARAMETERS" - ] - }, - "Reservoir porosity": { - "categories": [ - "RESERVOIR PARAMETERS" - ] - }, - "Thermal Conductivity": { - "categories": [ - "RESERVOIR PARAMETERS" - ] - }, - "Maximum Production Temperature": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Average Production Temperature": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ], - "description": "", - "units": "degC" - }, - "Minimum Production Temperature": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Initial Production Temperature": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Average Reservoir Heat Extraction": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Production Wellbore Heat Transmission Model": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Wellbore Heat Transmission Model": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Average Production Well Temperature Drop": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Total Average Pressure Drop": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Average Injection Well Pressure Drop": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Average Production Pressure": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ], - "description": "", - "units": "bar" - }, - "Average Reservoir Pressure Drop": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Average Production Well Pressure Drop": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Average Buoyancy Pressure Drop": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Average Injection Well Pump Pressure Drop": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Average Production Well Pump Pressure Drop": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Average Heat Production": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "First Year Heat Production": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "First Year Electricity Production": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Maximum Storage Well Temperature": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Average Storage Well Temperature": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Minimum Storage Well Temperature": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Maximum Balance Well Temperature": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Average Balance Well Temperature": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Minimum Balance Well Temperature": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Maximum Annual Heat Stored": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Average Annual Heat Stored": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Minimum Annual Heat Stored": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Maximum Annual Heat Supplied": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Average Annual Heat Supplied": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Minimum Annual Heat Supplied": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Average Round-Trip Efficiency": { - "categories": [ - "RESERVOIR SIMULATION RESULTS" - ] - }, - "Drilling and completion costs": { - "categories": [ - "CAPITAL COSTS (M$)" - ], - "description": "Wellfield cost. Includes total drilling and completion cost of all injection and production wells and laterals, plus 5% indirect costs.", - "units": "MUSD" - }, - "Drilling and completion costs per well": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "Drilling and completion costs per production well": { - "categories": [ - "CAPITAL COSTS (M$)", - "CAPITAL COSTS (M$)" - ] - }, - "Drilling and completion costs per injection well": { - "categories": [ - "CAPITAL COSTS (M$)", - "CAPITAL COSTS (M$)" - ] - }, - "Drilling and completion costs per vertical production well": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "Drilling and completion costs per vertical injection well": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "Drilling and completion costs per non-vertical section": { - "categories": [ - "CAPITAL COSTS (M$)" - ], - "description": "", - "units": "MUSD" - }, - "Drilling and completion costs (for redrilling)": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "Drilling and completion costs per redrilled well": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "Stimulation costs": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "Stimulation costs (for redrilling)": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "Surface power plant costs": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "of which Absorption Chiller Cost": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "of which Heat Pump Cost": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "of which Peaking Boiler Cost": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "Transmission pipeline cost": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "District Heating System Cost": { - "categories": [ - "CAPITAL COSTS (M$)" - ], - "description": "", - "units": "MUSD" - }, - "Field gathering system costs": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "Total surface equipment costs": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "Exploration costs": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "Investment Tax Credit": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "Total capital costs": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "Annualized capital costs": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "Total CAPEX": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "Drilling Cost": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "Drilling and Completion Costs": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "Drilling and Completion Costs per Well": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "Auxiliary Heater Cost": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "Pump Cost": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "Total Capital Costs": { - "categories": [ - "CAPITAL COSTS (M$)" - ] - }, - "Wellfield maintenance costs": { - "categories": [ - "OPERATING AND MAINTENANCE COSTS (M$/yr)" - ] - }, - "Power plant maintenance costs": { - "categories": [ - "OPERATING AND MAINTENANCE COSTS (M$/yr)" - ] - }, - "Water costs": { - "categories": [ - "OPERATING AND MAINTENANCE COSTS (M$/yr)" - ] - }, - "Average Reservoir Pumping Cost": { - "categories": [ - "OPERATING AND MAINTENANCE COSTS (M$/yr)" - ] - }, - "Absorption Chiller O&M Cost": { - "categories": [ - "OPERATING AND MAINTENANCE COSTS (M$/yr)" - ] - }, - "Average Heat Pump Electricity Cost": { - "categories": [ - "OPERATING AND MAINTENANCE COSTS (M$/yr)" - ] - }, - "Annual District Heating O&M Cost": { - "categories": [ - "OPERATING AND MAINTENANCE COSTS (M$/yr)" - ], - "description": "", - "units": "MUSD/yr" - }, - "Average Annual Peaking Fuel Cost": { - "categories": [ - "OPERATING AND MAINTENANCE COSTS (M$/yr)" - ], - "description": "", - "units": "MUSD/yr" - }, - "Average annual pumping costs": { - "categories": [ - "OPERATING AND MAINTENANCE COSTS (M$/yr)" - ] - }, - "Total operating and maintenance costs": { - "categories": [ - "OPERATING AND MAINTENANCE COSTS (M$/yr)" - ] - }, - "OPEX": { - "categories": [ - "OPERATING AND MAINTENANCE COSTS (M$/yr)" - ] - }, - "Average annual auxiliary fuel cost": { - "categories": [ - "OPERATING AND MAINTENANCE COSTS (M$/yr)" - ] - }, - "Average annual pumping cost": { - "categories": [ - "OPERATING AND MAINTENANCE COSTS (M$/yr)" - ] - }, - "Total average annual O&M costs": { - "categories": [ - "OPERATING AND MAINTENANCE COSTS (M$/yr)" - ] - }, - "Initial geofluid availability": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Maximum Total Electricity Generation": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Average Total Electricity Generation": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Minimum Total Electricity Generation": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Initial Total Electricity Generation": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Maximum Net Electricity Generation": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Average Net Electricity Generation": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Minimum Net Electricity Generation": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Initial Net Electricity Generation": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Average Annual Total Electricity Generation": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Average Annual Net Electricity Generation": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Maximum Net Heat Production": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Average Net Heat Production": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Minimum Net Heat Production": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Initial Net Heat Production": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Average Annual Heat Production": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Average Pumping Power": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Average Annual Heat Pump Electricity Use": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Maximum Cooling Production": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Minimum Cooling Production": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Initial Cooling Production": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Average Annual Cooling Production": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Maximum Daily District Heating Demand": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Average Daily District Heating Demand": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Minimum Daily District Heating Demand": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Maximum Geothermal Heating Production": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Average Geothermal Heating Production": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Minimum Geothermal Heating Production": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Maximum Peaking Boiler Heat Production": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Average Peaking Boiler Heat Production": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Minimum Peaking Boiler Heat Production": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Initial pumping power/net installed power": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Heat to Power Conversion Efficiency": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ], - "description": "First law efficiency average over project lifetime", - "units": "%" - }, - "Surface Plant Cost": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Average RTES Heating Production": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Average Auxiliary Heating Production": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Average Annual RTES Heating Production": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Average Annual Auxiliary Heating Production": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Average Annual Total Heating Production": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "Average Annual Electricity Use for Pumping": { - "categories": [ - "SURFACE EQUIPMENT SIMULATION RESULTS" - ] - }, - "GEOPHIRES Version": { - "categories": [ - "Simulation Metadata" - ] + "SUMMARY OF RESULTS": { + "type": "object", + "properties": { + "End-Use Option": {}, + "End-Use": {}, + "Surface Application": {}, + "Average Net Electricity Production": {}, + "Electricity breakeven price": {}, + "Average Direct-Use Heat Production": {}, + "Direct-Use heat breakeven price": {}, + "Direct-Use heat breakeven price (LCOH)": {}, + "Direct-Use Cooling Breakeven Price (LCOC)": {}, + "Annual District Heating Demand": {}, + "Average Cooling Production": {}, + "Average Annual Geothermal Heat Production": {}, + "Average Annual Peaking Fuel Heat Production": {}, + "Direct-Use Cooling Breakeven Price": {}, + "Number of production wells": {}, + "Number of injection wells": {}, + "Flowrate per production well": {}, + "Well depth": {}, + "Well depth (or total length, if not vertical)": {}, + "Geothermal gradient": {}, + "Segment 1 Geothermal gradient": {}, + "Segment 1 Thickness": {}, + "Segment 2 Geothermal gradient": {}, + "Segment 2 Thickness": {}, + "Segment 3 Geothermal gradient": {}, + "Segment 3 Thickness": {}, + "Segment 4 Geothermal gradient": {}, + "LCOE": { + "description": "", + "units": "cents/kWh" + }, + "LCOH": { + "description": "", + "units": "USD/MMBTU" + }, + "Lifetime Average Well Flow Rate": {}, + "Total Avoided Carbon Emissions": {} + } + }, + "ECONOMIC PARAMETERS": { + "type": "object", + "properties": { + "Economic Model": {}, + "Interest Rate": { + "description": "", + "units": "%" + }, + "Accrued financing during construction": {}, + "Project lifetime": {}, + "Capacity factor": {}, + "Project NPV": { + "description": "Project Net Present Value. NPV is calculated with cashflows lumped at the end of periods. See: Short W et al, 1995. \"A Manual for the Economic Evaluation of Energy Efficiency and Renewable Energy Technologies.\", p. 41. https://www.nrel.gov/docs/legosti/old/5173.pdf", + "units": "MUSD" + }, + "Project IRR": {}, + "Project VIR=PI=PIR": {}, + "Project MOIC": { + "description": "Project Multiple of Invested Capital", + "units": "" + }, + "Fixed Charge Rate (FCR)": {}, + "Project Payback Period": { + "description": "", + "units": "yr" + }, + "CHP: Percent cost allocation for electrical plant": {}, + "Estimated Jobs Created": { + "description": "", + "units": null + } + } + }, + "EXTENDED ECONOMICS": { + "type": "object", + "properties": { + "Adjusted Project LCOE (after incentives, grants, AddOns,etc)": {}, + "Adjusted Project LCOH (after incentives, grants, AddOns,etc)": {}, + "Adjusted Project CAPEX (after incentives, grants, AddOns, etc)": {}, + "Adjusted Project OPEX (after incentives, grants, AddOns, etc)": {}, + "Project NPV (including AddOns)": {}, + "Project IRR (including AddOns)": {}, + "Project VIR=PI=PIR (including AddOns)": {}, + "Project MOIC (including AddOns)": {}, + "Project Payback Period (including AddOns)": {}, + "Total Add-on CAPEX": {}, + "Total Add-on OPEX": {}, + "Total Add-on Net Elec": {}, + "Total Add-on Net Heat": {}, + "Total Add-on Profit": {}, + "AddOns Payback Period": {} + } + }, + "CCUS ECONOMICS": { + "type": "object", + "properties": { + "Total Avoided Carbon Production": {}, + "Project NPV (including carbon credit)": {}, + "Project IRR (including carbon credit)": {}, + "Project VIR=IR=PIR (including carbon credit)": {}, + "Project MOIC (including carbon credit)": {}, + "Project Payback Period (including carbon credit)": {} + } + }, + "S-DAC-GT ECONOMICS": { + "type": "object", + "properties": { + "LCOD using grid-based electricity only": {}, + "LCOD using natural gas only": {}, + "LCOD using geothermal energy only": {}, + "CO2 Intensity using grid-based electricity only": {}, + "CO2 Intensity using natural gas only": {}, + "CO2 Intensity using geothermal energy only": {}, + "Geothermal LCOH": {}, + "Geothermal Ratio (electricity vs heat)": {}, + "Percent Energy Devoted To Process": {}, + "Total Cost of Capture": {} + } + }, + "ENGINEERING PARAMETERS": { + "type": "object", + "properties": { + "Number of Production Wells": {}, + "Number of Injection Wells": {}, + "Well depth": {}, + "Well depth (or total length, if not vertical)": {}, + "Water loss rate": {}, + "Pump efficiency": {}, + "Injection temperature": {}, + "Injection Temperature": { + "description": "", + "units": "degC" + }, + "Average production well temperature drop": {}, + "Flowrate per production well": {}, + "Injection well casing ID": {}, + "Production well casing ID": {}, + "Number of times redrilling": {}, + "Power plant type": {}, + "Fluid": {}, + "Design": {}, + "Flow rate": {}, + "Lateral Length": {}, + "Vertical Depth": {}, + "Wellbore Diameter": {}, + "Lifetime Average Well Flow Rate": {} + } + }, + "RESOURCE CHARACTERISTICS": { + "type": "object", + "properties": { + "Maximum reservoir temperature": {}, + "Number of segments": {}, + "Geothermal gradient": {}, + "Segment 1 Geothermal gradient": {}, + "Segment 1 Thickness": {}, + "Segment 2 Geothermal gradient": {}, + "Segment 2 Thickness": {}, + "Segment 3 Geothermal gradient": {}, + "Segment 3 Thickness": {}, + "Segment 4 Geothermal gradient": {} + } + }, + "RESERVOIR PARAMETERS": { + "type": "object", + "properties": { + "Reservoir Model": {}, + "Fracture model": {}, + "Bottom-hole temperature": { + "description": "", + "units": "degC" + }, + "Well separation: fracture diameter": {}, + "Well separation: fracture height": {}, + "Fracture width": {}, + "Fracture area": {}, + "Number of fractures": {}, + "Fracture separation": {}, + "Reservoir volume": {}, + "Reservoir impedance": {}, + "Reservoir hydrostatic pressure": {}, + "Average reservoir pressure": {}, + "Plant outlet pressure": {}, + "Production wellhead pressure": { + "description": "", + "units": "kPa" + }, + "Productivity Index": {}, + "Injectivity Index": {}, + "Reservoir density": {}, + "Reservoir thermal conductivity": {}, + "Reservoir heat capacity": {}, + "Reservoir porosity": {}, + "Thermal Conductivity": {} + } + }, + "RESERVOIR SIMULATION RESULTS": { + "type": "object", + "properties": { + "Maximum Production Temperature": {}, + "Average Production Temperature": { + "description": "", + "units": "degC" + }, + "Minimum Production Temperature": {}, + "Initial Production Temperature": {}, + "Average Reservoir Heat Extraction": {}, + "Production Wellbore Heat Transmission Model": {}, + "Wellbore Heat Transmission Model": {}, + "Average Production Well Temperature Drop": {}, + "Total Average Pressure Drop": {}, + "Average Injection Well Pressure Drop": {}, + "Average Production Pressure": { + "description": "", + "units": "bar" + }, + "Average Reservoir Pressure Drop": {}, + "Average Production Well Pressure Drop": {}, + "Average Buoyancy Pressure Drop": {}, + "Average Injection Well Pump Pressure Drop": {}, + "Average Production Well Pump Pressure Drop": {}, + "Average Heat Production": {}, + "First Year Heat Production": {}, + "Average Net Electricity Production": {}, + "First Year Electricity Production": {}, + "Maximum Storage Well Temperature": {}, + "Average Storage Well Temperature": {}, + "Minimum Storage Well Temperature": {}, + "Maximum Balance Well Temperature": {}, + "Average Balance Well Temperature": {}, + "Minimum Balance Well Temperature": {}, + "Maximum Annual Heat Stored": {}, + "Average Annual Heat Stored": {}, + "Minimum Annual Heat Stored": {}, + "Maximum Annual Heat Supplied": {}, + "Average Annual Heat Supplied": {}, + "Minimum Annual Heat Supplied": {}, + "Average Round-Trip Efficiency": {} + } + }, + "CAPITAL COSTS (M$)": { + "type": "object", + "properties": { + "Drilling and completion costs": { + "description": "Wellfield cost. Includes total drilling and completion cost of all injection and production wells and laterals, plus 5% indirect costs.", + "units": "MUSD" + }, + "Drilling and completion costs per well": {}, + "Drilling and completion costs per production well": {}, + "Drilling and completion costs per injection well": {}, + "Drilling and completion costs per vertical production well": {}, + "Drilling and completion costs per vertical injection well": {}, + "Drilling and completion costs per non-vertical section": { + "description": "", + "units": "MUSD" + }, + "Drilling and completion costs (for redrilling)": {}, + "Drilling and completion costs per redrilled well": {}, + "Stimulation costs": {}, + "Stimulation costs (for redrilling)": {}, + "Surface power plant costs": {}, + "of which Absorption Chiller Cost": {}, + "of which Heat Pump Cost": {}, + "of which Peaking Boiler Cost": {}, + "Transmission pipeline cost": {}, + "District Heating System Cost": { + "description": "", + "units": "MUSD" + }, + "Field gathering system costs": {}, + "Total surface equipment costs": {}, + "Exploration costs": {}, + "Investment Tax Credit": {}, + "Total capital costs": {}, + "Annualized capital costs": {}, + "Total CAPEX": {}, + "Drilling Cost": {}, + "Drilling and Completion Costs": {}, + "Drilling and Completion Costs per Well": {}, + "Auxiliary Heater Cost": {}, + "Pump Cost": {}, + "Total Capital Costs": {} + } + }, + "OPERATING AND MAINTENANCE COSTS (M$/yr)": { + "type": "object", + "properties": { + "Wellfield maintenance costs": {}, + "Power plant maintenance costs": {}, + "Water costs": {}, + "Average Reservoir Pumping Cost": {}, + "Absorption Chiller O&M Cost": {}, + "Average Heat Pump Electricity Cost": {}, + "Annual District Heating O&M Cost": { + "description": "", + "units": "MUSD/yr" + }, + "Average Annual Peaking Fuel Cost": { + "description": "", + "units": "MUSD/yr" + }, + "Average annual pumping costs": {}, + "Total operating and maintenance costs": {}, + "OPEX": {}, + "Average annual auxiliary fuel cost": {}, + "Average annual pumping cost": {}, + "Total average annual O&M costs": {} + } + }, + "SURFACE EQUIPMENT SIMULATION RESULTS": { + "type": "object", + "properties": { + "Initial geofluid availability": {}, + "Maximum Total Electricity Generation": {}, + "Average Total Electricity Generation": {}, + "Minimum Total Electricity Generation": {}, + "Initial Total Electricity Generation": {}, + "Maximum Net Electricity Generation": {}, + "Average Net Electricity Generation": {}, + "Minimum Net Electricity Generation": {}, + "Initial Net Electricity Generation": {}, + "Average Annual Total Electricity Generation": {}, + "Average Annual Net Electricity Generation": {}, + "Maximum Net Heat Production": {}, + "Average Net Heat Production": {}, + "Minimum Net Heat Production": {}, + "Initial Net Heat Production": {}, + "Average Annual Heat Production": {}, + "Average Pumping Power": {}, + "Average Annual Heat Pump Electricity Use": {}, + "Maximum Cooling Production": {}, + "Average Cooling Production": {}, + "Minimum Cooling Production": {}, + "Initial Cooling Production": {}, + "Average Annual Cooling Production": {}, + "Annual District Heating Demand": {}, + "Maximum Daily District Heating Demand": {}, + "Average Daily District Heating Demand": {}, + "Minimum Daily District Heating Demand": {}, + "Maximum Geothermal Heating Production": {}, + "Average Geothermal Heating Production": {}, + "Minimum Geothermal Heating Production": {}, + "Maximum Peaking Boiler Heat Production": {}, + "Average Peaking Boiler Heat Production": {}, + "Minimum Peaking Boiler Heat Production": {}, + "Initial pumping power/net installed power": {}, + "Heat to Power Conversion Efficiency": { + "description": "First law efficiency average over project lifetime", + "units": "%" + }, + "Surface Plant Cost": {}, + "Average RTES Heating Production": {}, + "Average Auxiliary Heating Production": {}, + "Average Annual RTES Heating Production": {}, + "Average Annual Auxiliary Heating Production": {}, + "Average Annual Total Heating Production": {}, + "Average Annual Electricity Use for Pumping": {} + } + }, + "Simulation Metadata": { + "type": "object", + "properties": { + "GEOPHIRES Version": {} + } } } } diff --git a/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py b/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py index d67a04d7e..8957a7b23 100644 --- a/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py +++ b/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py @@ -1,5 +1,6 @@ import json import unittest +from typing import Any from geophires_x_schema_generator import GeophiresXSchemaGenerator from tests.base_test_case import BaseTestCase @@ -19,11 +20,16 @@ def test_get_json_schema(self): print(f'Generated result schema: {json.dumps(result_schema, indent=2)}') + def get_prop(cat: str, name: str) -> dict[str, Any]: + return result_schema['properties'][cat]['properties'][name] + self.assertIn( - 'multiple of invested capital', result_schema['properties']['Project MOIC']['description'].lower() + 'multiple of invested capital', get_prop('ECONOMIC PARAMETERS', 'Project MOIC')['description'].lower() ) - self.assertIn('Wellfield cost. ', result_schema['properties']['Drilling and completion costs']['description']) + self.assertIn( + 'Wellfield cost. ', get_prop('CAPITAL COSTS (M$)', 'Drilling and completion costs')['description'] + ) if __name__ == '__main__': From 93cd08bdf7777dd2d846129b4542a3f51590ac64 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 4 Apr 2025 11:24:15 -0700 Subject: [PATCH 10/24] OutputParameter.json_parameter_type --- src/geophires_x/Parameter.py | 42 +++++++++++++++++++++++++++++------- tests/test_parameter.py | 16 ++++++++++++++ 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src/geophires_x/Parameter.py b/src/geophires_x/Parameter.py index 029cb93e8..3298c5da2 100644 --- a/src/geophires_x/Parameter.py +++ b/src/geophires_x/Parameter.py @@ -6,6 +6,7 @@ import sys from array import array +from collections.abc import Iterable from typing import List, Optional, Any from dataclasses import dataclass, field from enum import IntEnum @@ -21,6 +22,13 @@ _ureg = get_unit_registry() _DISABLE_FOREX_API = True # See https://github.com/NREL/GEOPHIRES-X/issues/236#issuecomment-2414681434 +_JSON_PARAMETER_TYPE_STRING = 'string' +_JSON_PARAMETER_TYPE_INTEGER = 'integer' +_JSON_PARAMETER_TYPE_NUMBER = 'number' +_JSON_PARAMETER_TYPE_ARRAY = 'array' +_JSON_PARAMETER_TYPE_BOOLEAN = 'boolean' +_JSON_PARAMETER_TYPE_OBJECT = 'object' + class HasQuantity(ABC): def quantity(self) -> PlainQuantity: @@ -63,7 +71,7 @@ class OutputParameter(HasQuantity): """ Name: str = "" - display_name:str =None + display_name: str = None value: Any = 0 ToolTipText: str = "" UnitType: IntEnum = Units.NONE @@ -71,6 +79,7 @@ class OutputParameter(HasQuantity): # set to PreferredUnits by default assuming that the current units are the preferred units - # they will only change if the read function reads a different unit associated with a parameter CurrentUnits: Enum = PreferredUnits + json_parameter_type: str = None @property def UnitsMatch(self) -> str: @@ -86,6 +95,23 @@ def __post_init__(self): if self.display_name is None: self.display_name: str = self.Name + if self.json_parameter_type is None: + # Note that this is sensitive to order of comparison; unit test ensures correct behavior: + # test_parameter.ParameterTestCase.test_output_parameter_json_types + if isinstance(self.value, str): + self.json_parameter_type = _JSON_PARAMETER_TYPE_STRING + elif isinstance(self.value, bool): + self.json_parameter_type = _JSON_PARAMETER_TYPE_BOOLEAN + elif isinstance(self.value, int): + self.json_parameter_type = _JSON_PARAMETER_TYPE_INTEGER + elif isinstance(self.value, float): + self.json_parameter_type = _JSON_PARAMETER_TYPE_NUMBER + elif isinstance(self.value, dict): + self.json_parameter_type = _JSON_PARAMETER_TYPE_OBJECT + elif isinstance(self.value, Iterable): + self.json_parameter_type = _JSON_PARAMETER_TYPE_ARRAY + else: + self.json_parameter_type = _JSON_PARAMETER_TYPE_OBJECT @dataclass class Parameter(HasQuantity): @@ -152,7 +178,7 @@ def __post_init__(self): value: bool = None DefaultValue: bool = value - json_parameter_type: str = 'boolean' + json_parameter_type: str = _JSON_PARAMETER_TYPE_BOOLEAN @dataclass @@ -174,7 +200,7 @@ def __post_init__(self): value: int = None DefaultValue: int = value AllowableRange: List[int] = field(default_factory=list) - json_parameter_type: str = 'integer' + json_parameter_type: str = _JSON_PARAMETER_TYPE_INTEGER def coerce_value_to_enum(self): if self.ValuesEnum is not None: @@ -207,7 +233,7 @@ def __post_init__(self): DefaultValue: float = 0.0 Min: float = -1.8e30 Max: float = 1.8e30 - json_parameter_type: str = 'number' + json_parameter_type: str = _JSON_PARAMETER_TYPE_NUMBER @dataclass @@ -222,11 +248,11 @@ class strParameter(Parameter): """ def __post_init__(self): if self.value is None: - self.value:str = self.DefaultValue + self.value: str = self.DefaultValue value: str = None DefaultValue: str = value - json_parameter_type: str = 'string' + json_parameter_type: str = _JSON_PARAMETER_TYPE_STRING @dataclass @@ -246,13 +272,13 @@ class listParameter(Parameter): def __post_init__(self): if self.value is None: - self.value:str = self.DefaultValue + self.value: str = self.DefaultValue value: List[float] = None DefaultValue: List[float] = field(default_factory=list) Min: float = -1.8e308 Max: float = 1.8e308 - json_parameter_type: str = 'array' + json_parameter_type: str = _JSON_PARAMETER_TYPE_ARRAY def ReadParameter(ParameterReadIn: ParameterEntry, ParamToModify, model): diff --git a/tests/test_parameter.py b/tests/test_parameter.py index 592622265..7c911b416 100644 --- a/tests/test_parameter.py +++ b/tests/test_parameter.py @@ -151,6 +151,22 @@ def test_output_parameter_with_preferred_units(self): self.assertEqual(5.5, result.value[0]) self.assertEqual(5.5, result.value[-1]) + def test_output_parameter_json_types(self): + cases = [ + ('foo', 'string'), + (1, 'integer'), + (True, 'boolean'), + ([1, 2, 3], 'array'), + ({4, 5, 6}, 'array'), + (None, 'object'), + ({'foo': 'bar'}, 'object'), + ] + + for case in cases: + with self.subTest(case=case): + jpt = OutputParameter(value=case[0]).json_parameter_type + self.assertEqual(case[1], jpt) + def test_convert_units_back_currency(self): model = self._new_model() From 5f62dab7956b3020bd4a7ec1d79a292296b5231d Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 4 Apr 2025 11:31:30 -0700 Subject: [PATCH 11/24] treat output vals of both int and float as number --- src/geophires_x/Parameter.py | 6 +++--- tests/test_parameter.py | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/geophires_x/Parameter.py b/src/geophires_x/Parameter.py index 3298c5da2..e3e51a219 100644 --- a/src/geophires_x/Parameter.py +++ b/src/geophires_x/Parameter.py @@ -102,9 +102,9 @@ def __post_init__(self): self.json_parameter_type = _JSON_PARAMETER_TYPE_STRING elif isinstance(self.value, bool): self.json_parameter_type = _JSON_PARAMETER_TYPE_BOOLEAN - elif isinstance(self.value, int): - self.json_parameter_type = _JSON_PARAMETER_TYPE_INTEGER - elif isinstance(self.value, float): + elif isinstance(self.value, float) or isinstance(self.value, int): + # Default number values may not be representative of whether calculated values are integer-only, + # so we specify number type even if value is int. self.json_parameter_type = _JSON_PARAMETER_TYPE_NUMBER elif isinstance(self.value, dict): self.json_parameter_type = _JSON_PARAMETER_TYPE_OBJECT diff --git a/tests/test_parameter.py b/tests/test_parameter.py index 7c911b416..4972a11f9 100644 --- a/tests/test_parameter.py +++ b/tests/test_parameter.py @@ -154,7 +154,8 @@ def test_output_parameter_with_preferred_units(self): def test_output_parameter_json_types(self): cases = [ ('foo', 'string'), - (1, 'integer'), + (1, 'number'), + (44.4, 'number'), (True, 'boolean'), ([1, 2, 3], 'array'), ({4, 5, 6}, 'array'), From 330c7f535326e12f6d20dc9da8f98f20b8d2c1bc Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 4 Apr 2025 11:33:03 -0700 Subject: [PATCH 12/24] add types to result schema --- src/geophires_x_schema_generator/__init__.py | 2 ++ .../geophires-result.json | 18 ++++++++++++++++++ .../test_geophires_x_schema_generator.py | 7 ++++--- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/geophires_x_schema_generator/__init__.py b/src/geophires_x_schema_generator/__init__.py index 7faa16de7..b9aeaaad5 100644 --- a/src/geophires_x_schema_generator/__init__.py +++ b/src/geophires_x_schema_generator/__init__.py @@ -114,6 +114,7 @@ def generate_json_schema(self) -> Tuple[dict, dict]: units_val = param['CurrentUnits'] if isinstance(param['CurrentUnits'], str) else None min_val, max_val = _get_min_and_max(param, default_val=None) + properties[param_name] = { 'description': param['ToolTipText'], 'type': param['json_parameter_type'], @@ -179,6 +180,7 @@ def get_result_json_schema(self, output_params_json) -> dict: if param_name in output_params: output_param = output_params[param_name] + param['type'] = output_param['json_parameter_type'] description = output_param['ToolTipText'] if 'output_parameter_name' in output_param: description = f'{output_param["output_parameter_name"]}. {description}' diff --git a/src/geophires_x_schema_generator/geophires-result.json b/src/geophires_x_schema_generator/geophires-result.json index 38c15f49b..3115bc68b 100644 --- a/src/geophires_x_schema_generator/geophires-result.json +++ b/src/geophires_x_schema_generator/geophires-result.json @@ -36,10 +36,12 @@ "Segment 3 Thickness": {}, "Segment 4 Geothermal gradient": {}, "LCOE": { + "type": "number", "description": "", "units": "cents/kWh" }, "LCOH": { + "type": "number", "description": "", "units": "USD/MMBTU" }, @@ -52,6 +54,7 @@ "properties": { "Economic Model": {}, "Interest Rate": { + "type": "number", "description": "", "units": "%" }, @@ -59,22 +62,26 @@ "Project lifetime": {}, "Capacity factor": {}, "Project NPV": { + "type": "number", "description": "Project Net Present Value. NPV is calculated with cashflows lumped at the end of periods. See: Short W et al, 1995. \"A Manual for the Economic Evaluation of Energy Efficiency and Renewable Energy Technologies.\", p. 41. https://www.nrel.gov/docs/legosti/old/5173.pdf", "units": "MUSD" }, "Project IRR": {}, "Project VIR=PI=PIR": {}, "Project MOIC": { + "type": "number", "description": "Project Multiple of Invested Capital", "units": "" }, "Fixed Charge Rate (FCR)": {}, "Project Payback Period": { + "type": "number", "description": "", "units": "yr" }, "CHP: Percent cost allocation for electrical plant": {}, "Estimated Jobs Created": { + "type": "number", "description": "", "units": null } @@ -137,6 +144,7 @@ "Pump efficiency": {}, "Injection temperature": {}, "Injection Temperature": { + "type": "array", "description": "", "units": "degC" }, @@ -176,6 +184,7 @@ "Reservoir Model": {}, "Fracture model": {}, "Bottom-hole temperature": { + "type": "number", "description": "", "units": "degC" }, @@ -191,6 +200,7 @@ "Average reservoir pressure": {}, "Plant outlet pressure": {}, "Production wellhead pressure": { + "type": "number", "description": "", "units": "kPa" }, @@ -208,6 +218,7 @@ "properties": { "Maximum Production Temperature": {}, "Average Production Temperature": { + "type": "number", "description": "", "units": "degC" }, @@ -220,6 +231,7 @@ "Total Average Pressure Drop": {}, "Average Injection Well Pressure Drop": {}, "Average Production Pressure": { + "type": "number", "description": "", "units": "bar" }, @@ -251,6 +263,7 @@ "type": "object", "properties": { "Drilling and completion costs": { + "type": "number", "description": "Wellfield cost. Includes total drilling and completion cost of all injection and production wells and laterals, plus 5% indirect costs.", "units": "MUSD" }, @@ -260,6 +273,7 @@ "Drilling and completion costs per vertical production well": {}, "Drilling and completion costs per vertical injection well": {}, "Drilling and completion costs per non-vertical section": { + "type": "number", "description": "", "units": "MUSD" }, @@ -273,6 +287,7 @@ "of which Peaking Boiler Cost": {}, "Transmission pipeline cost": {}, "District Heating System Cost": { + "type": "number", "description": "", "units": "MUSD" }, @@ -301,10 +316,12 @@ "Absorption Chiller O&M Cost": {}, "Average Heat Pump Electricity Cost": {}, "Annual District Heating O&M Cost": { + "type": "number", "description": "", "units": "MUSD/yr" }, "Average Annual Peaking Fuel Cost": { + "type": "number", "description": "", "units": "MUSD/yr" }, @@ -354,6 +371,7 @@ "Minimum Peaking Boiler Heat Production": {}, "Initial pumping power/net installed power": {}, "Heat to Power Conversion Efficiency": { + "type": "object", "description": "First law efficiency average over project lifetime", "units": "%" }, diff --git a/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py b/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py index 8957a7b23..a0be2fb76 100644 --- a/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py +++ b/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py @@ -20,15 +20,16 @@ def test_get_json_schema(self): print(f'Generated result schema: {json.dumps(result_schema, indent=2)}') - def get_prop(cat: str, name: str) -> dict[str, Any]: + def get_result_prop(cat: str, name: str) -> dict[str, Any]: return result_schema['properties'][cat]['properties'][name] self.assertIn( - 'multiple of invested capital', get_prop('ECONOMIC PARAMETERS', 'Project MOIC')['description'].lower() + 'multiple of invested capital', + get_result_prop('ECONOMIC PARAMETERS', 'Project MOIC')['description'].lower(), ) self.assertIn( - 'Wellfield cost. ', get_prop('CAPITAL COSTS (M$)', 'Drilling and completion costs')['description'] + 'Wellfield cost. ', get_result_prop('CAPITAL COSTS (M$)', 'Drilling and completion costs')['description'] ) From f752c3f1e5b27e75520918b757648cfb9c35ed52 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 4 Apr 2025 11:41:03 -0700 Subject: [PATCH 13/24] Electricity breakeven price/LCOE display name --- src/geophires_x/Economics.py | 1 + src/geophires_x/Outputs.py | 4 ++-- src/geophires_x_schema_generator/__init__.py | 16 +++++----------- .../geophires-result.json | 8 ++++++-- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index 295c69c14..0168d19c0 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -1582,6 +1582,7 @@ def __init__(self, model: Model): self.LCOE = self.OutputParameterDict[self.LCOE.Name] = OutputParameter( Name="LCOE", + display_name='Electricity breakeven price', UnitType=Units.ENERGYCOST, PreferredUnits=EnergyCostUnit.CENTSSPERKWH, CurrentUnits=EnergyCostUnit.CENTSSPERKWH diff --git a/src/geophires_x/Outputs.py b/src/geophires_x/Outputs.py index 785639887..b2b1773db 100644 --- a/src/geophires_x/Outputs.py +++ b/src/geophires_x/Outputs.py @@ -202,7 +202,7 @@ def PrintOutputs(self, model: Model): f.write(f' Average Cooling Production: {np.average(model.surfaceplant.cooling_produced.value):10.2f} ' + model.surfaceplant.cooling_produced.CurrentUnits.value + NL) if model.surfaceplant.enduse_option.value in [EndUseOptions.ELECTRICITY]: - f.write(f' Electricity breakeven price: {model.economics.LCOE.value:10.2f} ' + model.economics.LCOE.CurrentUnits.value + NL) + f.write(f' {model.economics.LCOE.display_name}: {model.economics.LCOE.value:10.2f} {model.economics.LCOE.CurrentUnits.value}\n') elif model.surfaceplant.enduse_option.value in [EndUseOptions.HEAT] and \ model.surfaceplant.plant_type.value not in [PlantType.ABSORPTION_CHILLER]: f.write(f' Direct-Use heat breakeven price (LCOH): {model.economics.LCOH.value:10.2f} ' + model.economics.LCOH.CurrentUnits.value + NL) @@ -214,7 +214,7 @@ def PrintOutputs(self, model: Model): EndUseOptions.COGENERATION_TOPPING_EXTRA_ELECTRICITY, EndUseOptions.COGENERATION_BOTTOMING_EXTRA_ELECTRICITY, EndUseOptions.COGENERATION_PARALLEL_EXTRA_ELECTRICITY]: - f.write(f' Electricity breakeven price: {model.economics.LCOE.value:10.2f} ' + model.economics.LCOE.CurrentUnits.value + NL) + f.write(f' {model.economics.LCOE.display_name}: {model.economics.LCOE.value:10.2f} {model.economics.LCOE.CurrentUnits.value}\n') f.write(f' Direct-Use heat breakeven price (LCOH): {model.economics.LCOH.value:10.2f} ' + model.economics.LCOH.CurrentUnits.value + NL) f.write(f' Number of production wells: {model.wellbores.nprod.value:10.0f}'+NL) diff --git a/src/geophires_x_schema_generator/__init__.py b/src/geophires_x_schema_generator/__init__.py index b9aeaaad5..8e0301d11 100644 --- a/src/geophires_x_schema_generator/__init__.py +++ b/src/geophires_x_schema_generator/__init__.py @@ -168,28 +168,22 @@ def get_result_json_schema(self, output_params_json) -> dict: if param_name in properties: _log.warning(f'Param {param_name} is already in properties: {properties[param_name]}') - param = ( - { - # 'categories': [], - } - if param_name not in properties - else properties[param_name] - ) - - # param['categories'].append(category) + param = {} if param_name not in properties else properties[param_name] if param_name in output_params: output_param = output_params[param_name] param['type'] = output_param['json_parameter_type'] description = output_param['ToolTipText'] if 'output_parameter_name' in output_param: - description = f'{output_param["output_parameter_name"]}. {description}' + if description is not None and description != '': + description = f'{output_param["output_parameter_name"]}. {description}' + else: + description = output_param['output_parameter_name'] param['description'] = description param['units'] = ( output_param['CurrentUnits'] if isinstance(output_param['CurrentUnits'], str) else None ) - # properties[param_name] = param.copy() cat_properties[param_name] = param.copy() properties[category] = {'type': 'object', 'properties': cat_properties} diff --git a/src/geophires_x_schema_generator/geophires-result.json b/src/geophires_x_schema_generator/geophires-result.json index 3115bc68b..c3f39b7ce 100644 --- a/src/geophires_x_schema_generator/geophires-result.json +++ b/src/geophires_x_schema_generator/geophires-result.json @@ -12,7 +12,11 @@ "End-Use": {}, "Surface Application": {}, "Average Net Electricity Production": {}, - "Electricity breakeven price": {}, + "Electricity breakeven price": { + "type": "number", + "description": "LCOE", + "units": "cents/kWh" + }, "Average Direct-Use Heat Production": {}, "Direct-Use heat breakeven price": {}, "Direct-Use heat breakeven price (LCOH)": {}, @@ -37,7 +41,7 @@ "Segment 4 Geothermal gradient": {}, "LCOE": { "type": "number", - "description": "", + "description": "LCOE", "units": "cents/kWh" }, "LCOH": { From fc1f0f489edcdd07c1d0681adb0ff57464d8641c Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 4 Apr 2025 11:59:30 -0700 Subject: [PATCH 14/24] IRR + VIR=PI=PIR output param display names --- src/geophires_x/Economics.py | 2 ++ src/geophires_x/Outputs.py | 20 +++++++++---------- src/geophires_x/Reservoir.py | 2 +- .../geophires-request.json | 2 +- .../geophires-result.json | 12 +++++++++-- 5 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index 0168d19c0..01c7ad911 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -1820,12 +1820,14 @@ def __init__(self, model: Model): ) self.ProjectIRR = self.OutputParameterDict[self.ProjectIRR.Name] = OutputParameter( "Project Internal Rate of Return", + display_name='Project IRR', UnitType=Units.PERCENT, CurrentUnits=PercentUnit.PERCENT, PreferredUnits=PercentUnit.PERCENT, ) self.ProjectVIR = self.OutputParameterDict[self.ProjectVIR.Name] = OutputParameter( "Project Value Investment Ratio", + display_name='Project VIR=PI=PIR', UnitType=Units.PERCENT, PreferredUnits=PercentUnit.TENTH, CurrentUnits=PercentUnit.TENTH diff --git a/src/geophires_x/Outputs.py b/src/geophires_x/Outputs.py index b2b1773db..8f6ce7ad2 100644 --- a/src/geophires_x/Outputs.py +++ b/src/geophires_x/Outputs.py @@ -238,26 +238,26 @@ def PrintOutputs(self, model: Model): f.write(' ***ECONOMIC PARAMETERS***\n') f.write(NL) if model.economics.econmodel.value == EconomicModel.FCR: - f.write(' Economic Model = ' + model.economics.econmodel.value.value + NL) - f.write(f' Fixed Charge Rate (FCR): {model.economics.FCR.value*100.0:10.2f} ' + model.economics.FCR.CurrentUnits.value + NL) + f.write(f' Economic Model = {model.economics.econmodel.value.value}\n') + f.write(f' Fixed Charge Rate (FCR): {model.economics.FCR.value*100.0:10.2f} {model.economics.FCR.CurrentUnits.value}\n') elif model.economics.econmodel.value == EconomicModel.STANDARDIZED_LEVELIZED_COST: - f.write(' Economic Model = ' + model.economics.econmodel.value.value + NL) + f.write(f' Economic Model = {model.economics.econmodel.value.value}\n') f.write(f' {model.economics.interest_rate.Name}: {model.economics.interest_rate.value:10.2f} {model.economics.interest_rate.CurrentUnits.value}\n') elif model.economics.econmodel.value == EconomicModel.BICYCLE: - f.write(' Economic Model = ' + model.economics.econmodel.value.value + NL) - f.write(f' Accrued financing during construction: {model.economics.inflrateconstruction.value*100:10.2f} ' + model.economics.inflrateconstruction.CurrentUnits.value + NL) - f.write(f' Project lifetime: {model.surfaceplant.plant_lifetime.value:10.0f} ' + model.surfaceplant.plant_lifetime.CurrentUnits.value + NL) - f.write(f' Capacity factor: {model.surfaceplant.utilization_factor.value * 100:10.1f} %' + NL) + f.write(f' Economic Model = {model.economics.econmodel.value.value}\n') + f.write(f' Accrued financing during construction: {model.economics.inflrateconstruction.value*100:10.2f} {model.economics.inflrateconstruction.CurrentUnits.value}\n') + f.write(f' Project lifetime: {model.surfaceplant.plant_lifetime.value:10.0f} {model.surfaceplant.plant_lifetime.CurrentUnits.value}\n') + f.write(f' Capacity factor: {model.surfaceplant.utilization_factor.value * 100:10.1f} %\n') e_npv: OutputParameter = model.economics.ProjectNPV npv_field_label = Outputs._field_label(e_npv.display_name, 49) # TODO should use CurrentUnits instead of PreferredUnits f.write(f' {npv_field_label}{e_npv.value:10.2f} {e_npv.PreferredUnits.value}\n') - f.write(f' Project IRR: {model.economics.ProjectIRR.value:10.2f} ' + model.economics.ProjectIRR.PreferredUnits.value + NL) - f.write(f' Project VIR=PI=PIR: {model.economics.ProjectVIR.value:10.2f}' + NL) - f.write(f' {model.economics.ProjectMOIC.Name}: {model.economics.ProjectMOIC.value:10.2f}' + NL) + f.write(f' {model.economics.ProjectIRR.display_name}: {model.economics.ProjectIRR.value:10.2f} {model.economics.ProjectIRR.PreferredUnits.value}\n') + f.write(f' {model.economics.ProjectVIR.display_name}: {model.economics.ProjectVIR.value:10.2f}\n') + f.write(f' {model.economics.ProjectMOIC.Name}: {model.economics.ProjectMOIC.value:10.2f}\n') payback_period_val = model.economics.ProjectPaybackPeriod.value project_payback_period_display = f'{payback_period_val:10.2f} {model.economics.ProjectPaybackPeriod.PreferredUnits.value}' \ diff --git a/src/geophires_x/Reservoir.py b/src/geophires_x/Reservoir.py index c52fbea72..19e9140a4 100644 --- a/src/geophires_x/Reservoir.py +++ b/src/geophires_x/Reservoir.py @@ -104,7 +104,7 @@ def __init__(self, model: Model): CurrentUnits=TemperatureGradientUnit.DEGREESCPERM, Required=True, ErrMessage="assume default geothermal gradients 1 (50, 0, 0, 0 deg.C/km)", - ToolTipText="Geothermal gradients" + ToolTipText="Geothermal gradient(s)" ) self.gradient1 = self.ParameterDict[self.gradient1.Name] = floatParameter( diff --git a/src/geophires_x_schema_generator/geophires-request.json b/src/geophires_x_schema_generator/geophires-request.json index 8496c46d5..9d91f4e9a 100644 --- a/src/geophires_x_schema_generator/geophires-request.json +++ b/src/geophires_x_schema_generator/geophires-request.json @@ -125,7 +125,7 @@ "maximum": 4 }, "Gradients": { - "description": "Geothermal gradients", + "description": "Geothermal gradient(s)", "type": "array", "units": "degC/m", "category": "Reservoir", diff --git a/src/geophires_x_schema_generator/geophires-result.json b/src/geophires_x_schema_generator/geophires-result.json index c3f39b7ce..cc592da66 100644 --- a/src/geophires_x_schema_generator/geophires-result.json +++ b/src/geophires_x_schema_generator/geophires-result.json @@ -70,8 +70,16 @@ "description": "Project Net Present Value. NPV is calculated with cashflows lumped at the end of periods. See: Short W et al, 1995. \"A Manual for the Economic Evaluation of Energy Efficiency and Renewable Energy Technologies.\", p. 41. https://www.nrel.gov/docs/legosti/old/5173.pdf", "units": "MUSD" }, - "Project IRR": {}, - "Project VIR=PI=PIR": {}, + "Project IRR": { + "type": "number", + "description": "Project Internal Rate of Return", + "units": "%" + }, + "Project VIR=PI=PIR": { + "type": "number", + "description": "Project Value Investment Ratio", + "units": "" + }, "Project MOIC": { "type": "number", "description": "Project Multiple of Invested Capital", From 72e35ba38a1662de87a7f1b2a44f488735c99055 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 4 Apr 2025 12:15:23 -0700 Subject: [PATCH 15/24] fix py38-incompatible type annotation --- .../test_geophires_x_schema_generator.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py b/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py index a0be2fb76..90627c391 100644 --- a/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py +++ b/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py @@ -1,6 +1,5 @@ import json import unittest -from typing import Any from geophires_x_schema_generator import GeophiresXSchemaGenerator from tests.base_test_case import BaseTestCase @@ -20,7 +19,7 @@ def test_get_json_schema(self): print(f'Generated result schema: {json.dumps(result_schema, indent=2)}') - def get_result_prop(cat: str, name: str) -> dict[str, Any]: + def get_result_prop(cat: str, name: str) -> dict: return result_schema['properties'][cat]['properties'][name] self.assertIn( From 7e7517cbbd69c76ccb8bcda57a9759a0ac98f506 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 4 Apr 2025 12:15:58 -0700 Subject: [PATCH 16/24] configure 1:1 output display names thru total capex --- src/geophires_x/Economics.py | 4 +++ src/geophires_x/Outputs.py | 26 ++++++++-------- src/geophires_x/Reservoir.py | 1 + src/geophires_x/WellBores.py | 1 + .../geophires-result.json | 30 +++++++++++++++---- 5 files changed, 44 insertions(+), 18 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index 01c7ad911..42645af82 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -1601,6 +1601,7 @@ def __init__(self, model: Model): ) self.Cexpl = self.OutputParameterDict[self.Cexpl.Name] = OutputParameter( Name="Exploration cost", + display_name='Exploration costs', UnitType=Units.CURRENCY, PreferredUnits=CurrencyUnit.MDOLLARS, CurrentUnits=CurrencyUnit.MDOLLARS @@ -1643,6 +1644,7 @@ def __init__(self, model: Model): ) self.Cpiping = self.OutputParameterDict[self.Cpiping.Name] = OutputParameter( Name="Transmission pipeline costs", + display_name='Transmission pipeline cost', UnitType=Units.CURRENCY, PreferredUnits=CurrencyUnit.MDOLLARS, CurrentUnits=CurrencyUnit.MDOLLARS @@ -1655,6 +1657,7 @@ def __init__(self, model: Model): ) self.CCap = self.OutputParameterDict[self.CCap.Name] = OutputParameter( Name="Total Capital Cost", + display_name='Total capital costs', UnitType=Units.CURRENCY, PreferredUnits=CurrencyUnit.MDOLLARS, CurrentUnits=CurrencyUnit.MDOLLARS @@ -1847,6 +1850,7 @@ def __init__(self, model: Model): ) self.RITCValue = self.OutputParameterDict[self.RITCValue.Name] = OutputParameter( Name="Investment Tax Credit Value", + display_name='Investment Tax Credit', UnitType=Units.CURRENCY, PreferredUnits=CurrencyUnit.MDOLLARS, CurrentUnits=CurrencyUnit.MDOLLARS diff --git a/src/geophires_x/Outputs.py b/src/geophires_x/Outputs.py index 8f6ce7ad2..8e9a8812c 100644 --- a/src/geophires_x/Outputs.py +++ b/src/geophires_x/Outputs.py @@ -349,10 +349,10 @@ def PrintOutputs(self, model: Model): f.write(' Fracture separation calculated with reservoir volume and number of fractures as input\n') elif model.reserv.resvoloption.value == ReservoirVolume.RES_VOL_ONLY: f.write(' Reservoir volume provided as input\n') - if model.reserv.resvoloption.value in [ReservoirVolume.FRAC_NUM_SEP, ReservoirVolume.RES_VOL_FRAC_SEP,ReservoirVolume.FRAC_NUM_SEP]: + if model.reserv.resvoloption.value in [ReservoirVolume.FRAC_NUM_SEP, ReservoirVolume.RES_VOL_FRAC_SEP, ReservoirVolume.FRAC_NUM_SEP]: f.write(f' Number of fractures: {model.reserv.fracnumbcalc.value:10.2f}' + NL) f.write(f' Fracture separation: {model.reserv.fracsepcalc.value:10.2f} ' + model.reserv.fracsep.CurrentUnits.value + NL) - f.write(f' Reservoir volume: {model.reserv.resvolcalc.value:10.0f} ' + model.reserv.resvol.CurrentUnits.value + NL) + f.write(f' Reservoir volume: {model.reserv.resvolcalc.value:10.0f} {model.reserv.resvol.CurrentUnits.value}\n') if model.wellbores.impedancemodelused.value: # See note re: unit conversion: @@ -361,7 +361,7 @@ def PrintOutputs(self, model: Model): else: if model.wellbores.overpressure_percentage.Provided: # write the reservoir pressure as an average in the overpressure case - f.write(f' Average reservoir pressure: {model.wellbores.average_production_reservoir_pressure.value:10.2f} ' + model.wellbores.average_production_reservoir_pressure.CurrentUnits.value + NL) + f.write(f' {model.wellbores.average_production_reservoir_pressure.display_name}: {model.wellbores.average_production_reservoir_pressure.value:10.2f} {model.wellbores.average_production_reservoir_pressure.CurrentUnits.value}\n') else: # write the reservoir pressure as a single value f.write(f' Reservoir hydrostatic pressure: {model.wellbores.production_reservoir_pressure.value[0]:10.2f} ' + model.wellbores.production_reservoir_pressure.CurrentUnits.value + NL) @@ -392,11 +392,11 @@ def PrintOutputs(self, model: Model): f.write(f' Minimum Production Temperature: {np.min(model.wellbores.ProducedTemperature.value):10.1f} ' + model.wellbores.ProducedTemperature.PreferredUnits.value + NL) f.write(f' Initial Production Temperature: {model.wellbores.ProducedTemperature.value[0]:10.1f} ' + model.wellbores.ProducedTemperature.PreferredUnits.value + NL) if model.wellbores.IsAGS.value: - f.write('The AGS models contain an intrinsic reservoir model that doesn\'t expose values that can be used in extensive reporting.' + NL) + f.write('The AGS models contain an intrinsic reservoir model that doesn\'t expose values that can be used in extensive reporting.\n') else: f.write(f' Average Reservoir Heat Extraction: {np.average(model.surfaceplant.HeatExtracted.value):10.2f} ' + model.surfaceplant.HeatExtracted.PreferredUnits.value + NL) if model.wellbores.rameyoptionprod.value: - f.write(' Production Wellbore Heat Transmission Model = Ramey Model' + NL) + f.write(' Production Wellbore Heat Transmission Model = Ramey Model\n') f.write(f' Average Production Well Temperature Drop: {np.average(model.wellbores.ProdTempDrop.value):10.1f} ' + model.wellbores.ProdTempDrop.PreferredUnits.value + NL) else: f.write(f' Wellbore Heat Transmission Model = Constant Temperature Drop:{model.wellbores.tempdropprod.value:10.1f} ' + model.wellbores.tempdropprod.PreferredUnits.value + NL) @@ -416,13 +416,13 @@ def PrintOutputs(self, model: Model): f.write(' ***CAPITAL COSTS (M$)***\n') f.write(NL) if not model.economics.totalcapcost.Valid: - f.write(f' Drilling and completion costs: {model.economics.Cwell.value:10.2f} ' + model.economics.Cwell.CurrentUnits.value + NL) + f.write(f' {model.economics.Cwell.display_name}: {model.economics.Cwell.value:10.2f} {model.economics.Cwell.CurrentUnits.value}\n') if econ.cost_lateral_section.value > 0.0: f.write(f' Drilling and completion costs per vertical production well: {econ.cost_one_production_well.value:10.2f} ' + econ.cost_one_production_well.CurrentUnits.value + NL) f.write(f' Drilling and completion costs per vertical injection well: {econ.cost_one_injection_well.value:10.2f} ' + econ.cost_one_injection_well.CurrentUnits.value + NL) f.write(f' {econ.cost_per_lateral_section.Name}: {econ.cost_per_lateral_section.value:10.2f} {econ.cost_lateral_section.CurrentUnits.value}\n') - elif round(econ.cost_one_production_well.value, 4) != round(econ.cost_one_injection_well.value, 4) and \ - model.economics.cost_one_injection_well.value != -1: + elif round(econ.cost_one_production_well.value, 4) != round(econ.cost_one_injection_well.value, 4) \ + and model.economics.cost_one_injection_well.value != -1: f.write(f' Drilling and completion costs per production well: {econ.cost_one_production_well.value:10.2f} ' + econ.cost_one_production_well.CurrentUnits.value + NL) f.write(f' Drilling and completion costs per injection well: {econ.cost_one_injection_well.value:10.2f} ' + econ.cost_one_injection_well.CurrentUnits.value + NL) else: @@ -437,18 +437,18 @@ def PrintOutputs(self, model: Model): f.write(f' of which Peaking Boiler Cost: {model.economics.peakingboilercost.value:10.2f} ' + model.economics.peakingboilercost.CurrentUnits.value + NL) f.write(f' Field gathering system costs: {model.economics.Cgath.value:10.2f} ' + model.economics.Cgath.CurrentUnits.value + NL) if model.surfaceplant.piping_length.value > 0: - f.write(f' Transmission pipeline cost: {model.economics.Cpiping.value:10.2f} ' + model.economics.Cpiping.CurrentUnits.value + NL) + f.write(f' {model.economics.Cpiping.display_name}: {model.economics.Cpiping.value:10.2f} {model.economics.Cpiping.CurrentUnits.value}\n') if model.surfaceplant.plant_type.value == PlantType.DISTRICT_HEATING: - f.write(f' District Heating System Cost: {model.economics.dhdistrictcost.value:10.2f} ' + model.economics.dhdistrictcost.CurrentUnits.value + NL) + f.write(f' District Heating System Cost: {model.economics.dhdistrictcost.value:10.2f} {model.economics.dhdistrictcost.CurrentUnits.value}\n') f.write(f' Total surface equipment costs: {(model.economics.Cplant.value+model.economics.Cgath.value):10.2f} ' + model.economics.Cplant.CurrentUnits.value + NL) - f.write(f' Exploration costs: {model.economics.Cexpl.value:10.2f} ' + model.economics.Cexpl.CurrentUnits.value + NL) + f.write(f' {model.economics.Cexpl.display_name}: {model.economics.Cexpl.value:10.2f} {model.economics.Cexpl.CurrentUnits.value}\n') if model.economics.totalcapcost.Valid and model.wellbores.redrill.value > 0: f.write(f' Drilling and completion costs (for redrilling):{model.economics.Cwell.value:10.2f} ' + model.economics.Cwell.CurrentUnits.value + NL) f.write(f' Drilling and completion costs per redrilled well: {(model.economics.Cwell.value/(model.wellbores.nprod.value+model.wellbores.ninj.value)):10.2f} ' + model.economics.Cwell.CurrentUnits.value + NL) f.write(f' Stimulation costs (for redrilling): {model.economics.Cstim.value:10.2f} ' + model.economics.Cstim.CurrentUnits.value + NL) if model.economics.RITCValue.value: - f.write(f' Investment Tax Credit: {-1*model.economics.RITCValue.value:10.2f} ' + model.economics.RITCValue.CurrentUnits.value + NL) - f.write(f' Total capital costs: {model.economics.CCap.value:10.2f} ' + model.economics.CCap.CurrentUnits.value + NL) + f.write(f' {model.economics.RITCValue.display_name}: {-1*model.economics.RITCValue.value:10.2f} {model.economics.RITCValue.CurrentUnits.value}\n') + f.write(f' {model.economics.CCap.display_name}: {model.economics.CCap.value:10.2f} {model.economics.CCap.CurrentUnits.value}\n') if model.economics.econmodel.value == EconomicModel.FCR: f.write(f' Annualized capital costs: {(model.economics.CCap.value*(1+model.economics.inflrateconstruction.value)*model.economics.FCR.value):10.2f} ' + model.economics.CCap.CurrentUnits.value + NL) diff --git a/src/geophires_x/Reservoir.py b/src/geophires_x/Reservoir.py index 19e9140a4..7575e95a8 100644 --- a/src/geophires_x/Reservoir.py +++ b/src/geophires_x/Reservoir.py @@ -458,6 +458,7 @@ def __init__(self, model: Model): self.resvolcalc = self.OutputParameterDict[self.resvolcalc.Name] = floatParameter( "Calculated Reservoir Volume", + # display_name='Reservoir volume', value=self.resvol.value, UnitType=Units.VOLUME, PreferredUnits=VolumeUnit.METERS3, diff --git a/src/geophires_x/WellBores.py b/src/geophires_x/WellBores.py index 7317ecf4f..9f250d49b 100644 --- a/src/geophires_x/WellBores.py +++ b/src/geophires_x/WellBores.py @@ -1072,6 +1072,7 @@ def __init__(self, model: Model): ) self.average_production_reservoir_pressure = self.OutputParameterDict[self.average_production_reservoir_pressure.Name] = OutputParameter( Name="Average Reservoir Pressure", + display_name='Average reservoir pressure', UnitType=Units.PRESSURE, PreferredUnits=PressureUnit.KPASCAL, CurrentUnits=PressureUnit.KPASCAL diff --git a/src/geophires_x_schema_generator/geophires-result.json b/src/geophires_x_schema_generator/geophires-result.json index cc592da66..342b93428 100644 --- a/src/geophires_x_schema_generator/geophires-result.json +++ b/src/geophires_x_schema_generator/geophires-result.json @@ -209,7 +209,11 @@ "Reservoir volume": {}, "Reservoir impedance": {}, "Reservoir hydrostatic pressure": {}, - "Average reservoir pressure": {}, + "Average reservoir pressure": { + "type": "number", + "description": "Average Reservoir Pressure", + "units": "kPa" + }, "Plant outlet pressure": {}, "Production wellhead pressure": { "type": "number", @@ -297,7 +301,11 @@ "of which Absorption Chiller Cost": {}, "of which Heat Pump Cost": {}, "of which Peaking Boiler Cost": {}, - "Transmission pipeline cost": {}, + "Transmission pipeline cost": { + "type": "number", + "description": "Transmission pipeline costs", + "units": "MUSD" + }, "District Heating System Cost": { "type": "number", "description": "", @@ -305,9 +313,21 @@ }, "Field gathering system costs": {}, "Total surface equipment costs": {}, - "Exploration costs": {}, - "Investment Tax Credit": {}, - "Total capital costs": {}, + "Exploration costs": { + "type": "number", + "description": "Exploration cost", + "units": "MUSD" + }, + "Investment Tax Credit": { + "type": "number", + "description": "Investment Tax Credit Value", + "units": "MUSD" + }, + "Total capital costs": { + "type": "number", + "description": "Total Capital Cost", + "units": "MUSD" + }, "Annualized capital costs": {}, "Total CAPEX": {}, "Drilling Cost": {}, From fd7f1896649fc3b410ceb2490ef830949e4d1a70 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 4 Apr 2025 12:42:19 -0700 Subject: [PATCH 17/24] more 1:1 output display names including LCO* --- src/geophires_x/Economics.py | 9 ++-- src/geophires_x/EconomicsS_DAC_GT.py | 1 + src/geophires_x/Outputs.py | 38 ++++++++-------- src/geophires_x/Reservoir.py | 4 ++ src/geophires_x/SUTRAEconomics.py | 1 + .../geophires-result.json | 44 +++++++++++++++---- 6 files changed, 68 insertions(+), 29 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index 42645af82..a65dff2fc 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -1575,6 +1575,7 @@ def __init__(self, model: Model): self.LCOC = self.OutputParameterDict[self.LCOC.Name] = OutputParameter( Name="LCOC", + display_name='Direct-Use Cooling Breakeven Price (LCOC)', UnitType=Units.ENERGYCOST, PreferredUnits=EnergyCostUnit.DOLLARSPERMMBTU, CurrentUnits=EnergyCostUnit.DOLLARSPERMMBTU @@ -1589,12 +1590,13 @@ def __init__(self, model: Model): ) self.LCOH = self.OutputParameterDict[self.LCOH.Name] = OutputParameter( Name="LCOH", + display_name='Direct-Use heat breakeven price (LCOH)', UnitType=Units.ENERGYCOST, - PreferredUnits=EnergyCostUnit.DOLLARSPERMMBTU, + PreferredUnits=EnergyCostUnit.DOLLARSPERMMBTU, # $/MMBTU CurrentUnits=EnergyCostUnit.DOLLARSPERMMBTU - ) # $/MMBTU + ) self.Cstim = self.OutputParameterDict[self.Cstim.Name] = OutputParameter( - Name="O&M Surface Plant costs", # FIXME wrong name - should be Stimulation Costs + Name="O&M Surface Plant costs", # FIXME wrong name - should be Stimulation Costs UnitType=Units.CURRENCY, PreferredUnits=CurrencyUnit.MDOLLARS, CurrentUnits=CurrencyUnit.MDOLLARS @@ -1780,6 +1782,7 @@ def __init__(self, model: Model): self.CarbonThatWouldHaveBeenProducedTotal = self.OutputParameterDict[ self.CarbonThatWouldHaveBeenProducedTotal.Name] = OutputParameter( "Total Saved Carbon Production", + display_name='Total Avoided Carbon Emissions', UnitType=Units.MASS, PreferredUnits=MassUnit.LB, CurrentUnits=MassUnit.LB diff --git a/src/geophires_x/EconomicsS_DAC_GT.py b/src/geophires_x/EconomicsS_DAC_GT.py index 26e6f1275..78cf9a5c1 100644 --- a/src/geophires_x/EconomicsS_DAC_GT.py +++ b/src/geophires_x/EconomicsS_DAC_GT.py @@ -267,6 +267,7 @@ def __init__(self, model: Model): ) self.LCOH = self.OutputParameterDict[self.LCOH.Name] = OutputParameter( Name="LCOH", + display_name='Direct-Use heat breakeven price (LCOH)', UnitType=Units.ENERGYCOST, PreferredUnits=EnergyCostUnit.DOLLARSPERKWH, CurrentUnits=EnergyCostUnit.DOLLARSPERKWH diff --git a/src/geophires_x/Outputs.py b/src/geophires_x/Outputs.py index 8e9a8812c..a1f40f377 100644 --- a/src/geophires_x/Outputs.py +++ b/src/geophires_x/Outputs.py @@ -187,12 +187,13 @@ def PrintOutputs(self, model: Model): f.write(NL) f.write(' ***SUMMARY OF RESULTS***\n') f.write(NL) - f.write(' End-Use Option: ' + str(model.surfaceplant.enduse_option.value.value) + NL) + f.write(f' End-Use Option: {str(model.surfaceplant.enduse_option.value.value)}\n') if model.surfaceplant.plant_type.value in [PlantType.ABSORPTION_CHILLER, PlantType.HEAT_PUMP, PlantType.DISTRICT_HEATING]: f.write(' Surface Application: ' + str(model.surfaceplant.plant_type.value.value) + NL) if model.surfaceplant.enduse_option.value in [EndUseOptions.ELECTRICITY, EndUseOptions.COGENERATION_TOPPING_EXTRA_HEAT, EndUseOptions.COGENERATION_TOPPING_EXTRA_ELECTRICITY, EndUseOptions.COGENERATION_BOTTOMING_EXTRA_ELECTRICITY, EndUseOptions.COGENERATION_BOTTOMING_EXTRA_HEAT, EndUseOptions.COGENERATION_PARALLEL_EXTRA_HEAT, EndUseOptions.COGENERATION_PARALLEL_EXTRA_ELECTRICITY]: # there is an electricity component f.write(f' Average Net Electricity Production: {np.average(model.surfaceplant.NetElectricityProduced.value):10.2f} ' + model.surfaceplant.NetElectricityProduced.CurrentUnits.value + NL) - if model.surfaceplant.enduse_option.value is not EndUseOptions.ELECTRICITY: # there is a direct-use component + if model.surfaceplant.enduse_option.value is not EndUseOptions.ELECTRICITY: + # there is a direct-use component f.write(f' Average Direct-Use Heat Production: {np.average(model.surfaceplant.HeatProduced.value):10.2f} '+ model.surfaceplant.HeatProduced.CurrentUnits.value + NL) if model.surfaceplant.plant_type.value == PlantType.DISTRICT_HEATING: f.write(f' Annual District Heating Demand: {np.average(model.surfaceplant.annual_heating_demand.value):10.2f} ' + model.surfaceplant.annual_heating_demand.CurrentUnits.value + NL) @@ -204,10 +205,10 @@ def PrintOutputs(self, model: Model): if model.surfaceplant.enduse_option.value in [EndUseOptions.ELECTRICITY]: f.write(f' {model.economics.LCOE.display_name}: {model.economics.LCOE.value:10.2f} {model.economics.LCOE.CurrentUnits.value}\n') elif model.surfaceplant.enduse_option.value in [EndUseOptions.HEAT] and \ - model.surfaceplant.plant_type.value not in [PlantType.ABSORPTION_CHILLER]: - f.write(f' Direct-Use heat breakeven price (LCOH): {model.economics.LCOH.value:10.2f} ' + model.economics.LCOH.CurrentUnits.value + NL) + model.surfaceplant.plant_type.value not in [PlantType.ABSORPTION_CHILLER]: + f.write(f' {model.economics.LCOH.display_name}: {model.economics.LCOH.value:10.2f} {model.economics.LCOH.CurrentUnits.value}\n') elif model.surfaceplant.enduse_option.value in [EndUseOptions.HEAT] and model.surfaceplant.plant_type.value == PlantType.ABSORPTION_CHILLER: - f.write(f' Direct-Use Cooling Breakeven Price (LCOC): {model.economics.LCOC.value:10.2f} ' + model.economics.LCOC.CurrentUnits.value + NL) + f.write(f' {model.economics.LCOC.display_name}: {model.economics.LCOC.value:10.2f} {model.economics.LCOC.CurrentUnits.value}\n') elif model.surfaceplant.enduse_option.value in [EndUseOptions.COGENERATION_TOPPING_EXTRA_HEAT, EndUseOptions.COGENERATION_BOTTOMING_EXTRA_HEAT, EndUseOptions.COGENERATION_PARALLEL_EXTRA_HEAT, @@ -215,7 +216,7 @@ def PrintOutputs(self, model: Model): EndUseOptions.COGENERATION_BOTTOMING_EXTRA_ELECTRICITY, EndUseOptions.COGENERATION_PARALLEL_EXTRA_ELECTRICITY]: f.write(f' {model.economics.LCOE.display_name}: {model.economics.LCOE.value:10.2f} {model.economics.LCOE.CurrentUnits.value}\n') - f.write(f' Direct-Use heat breakeven price (LCOH): {model.economics.LCOH.value:10.2f} ' + model.economics.LCOH.CurrentUnits.value + NL) + f.write(f' {model.economics.LCOH.display_name}: {model.economics.LCOH.value:10.2f} {model.economics.LCOH.CurrentUnits.value}\n') f.write(f' Number of production wells: {model.wellbores.nprod.value:10.0f}'+NL) f.write(f' Number of injection wells: {model.wellbores.ninj.value:10.0f}'+NL) @@ -230,8 +231,9 @@ def PrintOutputs(self, model: Model): f.write(f' Segment {str(i):s} Thickness: {round(model.reserv.layerthickness.value[i-1], 10)} {model.reserv.layerthickness.CurrentUnits.value}\n') f.write(f' Segment {str(i+1):s} Geothermal gradient: {model.reserv.gradient.value[i]:10.4g} ' + model.reserv.gradient.CurrentUnits.value + NL) if model.economics.DoCarbonCalculations.value: - f.write(f' Total Avoided Carbon Emissions: {model.economics.CarbonThatWouldHaveBeenProducedTotal.value:10.2f} ' - f'{model.economics.CarbonThatWouldHaveBeenProducedTotal.CurrentUnits.value}\n') + f.write(f' {model.economics.CarbonThatWouldHaveBeenProducedTotal.display_name}:' + f' {model.economics.CarbonThatWouldHaveBeenProducedTotal.value:10.2f}' + f' {model.economics.CarbonThatWouldHaveBeenProducedTotal.CurrentUnits.value}\n') f.write(NL) f.write(NL) @@ -316,21 +318,21 @@ def PrintOutputs(self, model: Model): f.write(' ***RESERVOIR PARAMETERS***\n') f.write(NL) if model.wellbores.IsAGS.value: - f.write('The AGS models contain an intrinsic reservoir model that doesn\'t expose values that can be used in extensive reporting.' + NL) + f.write('The AGS models contain an intrinsic reservoir model that doesn\'t expose values that can be used in extensive reporting.\n') else: - f.write(' Reservoir Model = ' + str(model.reserv.resoption.value.value) + ' Model\n') + f.write(f' Reservoir Model = {str(model.reserv.resoption.value.value)} Model\n') if model.reserv.resoption.value is ReservoirModel.SINGLE_FRACTURE: f.write(f' m/A Drawdown Parameter: {model.reserv.drawdp.value:.5f} ' + model.reserv.drawdp.CurrentUnits.value + NL) elif model.reserv.resoption.value is ReservoirModel.ANNUAL_PERCENTAGE: f.write(f' Annual Thermal Drawdown: {model.reserv.drawdp.value*100:.3f} ' + model.reserv.drawdp.CurrentUnits.value + NL) - f.write(f' Bottom-hole temperature: {model.reserv.Trock.value:10.2f} ' + model.reserv.Trock.CurrentUnits.value + NL) + f.write(f' Bottom-hole temperature: {model.reserv.Trock.value:10.2f} {model.reserv.Trock.CurrentUnits.value}\n') if model.reserv.resoption.value in [ReservoirModel.ANNUAL_PERCENTAGE, ReservoirModel.USER_PROVIDED_PROFILE, ReservoirModel.TOUGH2_SIMULATOR]: f.write(' Warning: the reservoir dimensions and thermo-physical properties \n') f.write(' listed below are default values if not provided by the user. \n') f.write(' They are only used for calculating remaining heat content. \n') if model.reserv.resoption.value in [ReservoirModel.MULTIPLE_PARALLEL_FRACTURES, ReservoirModel.LINEAR_HEAT_SWEEP]: - f.write(' Fracture model = ' + model.reserv.fracshape.value.value + NL) + f.write(f' Fracture model = {model.reserv.fracshape.value.value}\n') if model.reserv.fracshape.value == FractureShape.CIRCULAR_AREA: f.write(f' Well separation: fracture diameter: {model.reserv.fracheightcalc.value:10.2f} ' + model.reserv.fracheight.CurrentUnits.value + NL) elif model.reserv.fracshape.value == FractureShape.CIRCULAR_DIAMETER: @@ -339,8 +341,8 @@ def PrintOutputs(self, model: Model): f.write(f' Well separation: fracture height: {model.reserv.fracheightcalc.value:10.2f} ' + model.reserv.fracheight.CurrentUnits.value + NL) elif model.reserv.fracshape.value == FractureShape.RECTANGULAR: f.write(f' Well separation: fracture height: {model.reserv.fracheightcalc.value:10.2f} ' + model.reserv.fracheight.CurrentUnits.value + NL) - f.write(f' Fracture width: {model.reserv.fracwidthcalc.value:10.2f} ' + model.reserv.fracwidth.CurrentUnits.value + NL) - f.write(f' Fracture area: {model.reserv.fracareacalc.value:10.2f} ' + model.reserv.fracarea.CurrentUnits.value + NL) + f.write(f' {model.reserv.fracwidthcalc.display_name}: {model.reserv.fracwidthcalc.value:10.2f} {model.reserv.fracwidth.CurrentUnits.value }\n') + f.write(f' {model.reserv.fracareacalc.display_name}: {model.reserv.fracareacalc.value:10.2f} {model.reserv.fracarea.CurrentUnits.value}\n') if model.reserv.resvoloption.value == ReservoirVolume.FRAC_NUM_SEP: f.write(' Reservoir volume calculated with fracture separation and number of fractures as input\n') elif model.reserv.resvoloption.value == ReservoirVolume.RES_VOL_FRAC_SEP: @@ -350,8 +352,8 @@ def PrintOutputs(self, model: Model): elif model.reserv.resvoloption.value == ReservoirVolume.RES_VOL_ONLY: f.write(' Reservoir volume provided as input\n') if model.reserv.resvoloption.value in [ReservoirVolume.FRAC_NUM_SEP, ReservoirVolume.RES_VOL_FRAC_SEP, ReservoirVolume.FRAC_NUM_SEP]: - f.write(f' Number of fractures: {model.reserv.fracnumbcalc.value:10.2f}' + NL) - f.write(f' Fracture separation: {model.reserv.fracsepcalc.value:10.2f} ' + model.reserv.fracsep.CurrentUnits.value + NL) + f.write(f' {model.reserv.fracnumbcalc.display_name}: {model.reserv.fracnumbcalc.value:10.2f}\n') + f.write(f' {model.reserv.fracsepcalc.display_name}: {model.reserv.fracsepcalc.value:10.2f} {model.reserv.fracsep.CurrentUnits.value}\n') f.write(f' Reservoir volume: {model.reserv.resvolcalc.value:10.0f} {model.reserv.resvol.CurrentUnits.value}\n') if model.wellbores.impedancemodelused.value: @@ -385,7 +387,7 @@ def PrintOutputs(self, model: Model): f.write(NL) f.write(NL) - f.write(' ***RESERVOIR SIMULATION RESULTS***' + NL) + f.write(' ***RESERVOIR SIMULATION RESULTS***\n') f.write(NL) f.write(f' Maximum Production Temperature: {np.max(model.wellbores.ProducedTemperature.value):10.1f} ' + model.wellbores.ProducedTemperature.PreferredUnits.value + NL) f.write(f' Average Production Temperature: {np.average(model.wellbores.ProducedTemperature.value):10.1f} ' + model.wellbores.ProducedTemperature.PreferredUnits.value + NL) @@ -751,7 +753,7 @@ def o(output_param: OutputParameter): @staticmethod - def _field_label(field_name:str, print_width_before_value: int) -> str: + def _field_label(field_name: str, print_width_before_value: int) -> str: return f'{field_name}:{" " * (print_width_before_value - len(field_name) - 1)}' diff --git a/src/geophires_x/Reservoir.py b/src/geophires_x/Reservoir.py index 7575e95a8..e4422b299 100644 --- a/src/geophires_x/Reservoir.py +++ b/src/geophires_x/Reservoir.py @@ -420,6 +420,7 @@ def __init__(self, model: Model): # starts as a copy of the input value and only changes if needed. self.fracsepcalc = self.OutputParameterDict[self.fracsepcalc.Name] = OutputParameter( "Calculated Fracture Separation", + display_name='Fracture separation', value=self.fracsep.value, UnitType=Units.LENGTH, PreferredUnits=LengthUnit.METERS, @@ -428,12 +429,14 @@ def __init__(self, model: Model): self.fracnumbcalc = self.OutputParameterDict[self.fracnumbcalc.Name] = OutputParameter( "Calculated Number of Fractures", + display_name='Number of fractures', value=self.fracnumb.value, UnitType=Units.NONE ) self.fracwidthcalc = self.OutputParameterDict[self.fracwidthcalc.Name] = OutputParameter( "Calculated Fracture Width", + display_name='Fracture width', value=self.fracwidth.value, UnitType=Units.LENGTH, PreferredUnits=LengthUnit.METERS, @@ -450,6 +453,7 @@ def __init__(self, model: Model): self.fracareacalc = self.OutputParameterDict[self.fracareacalc.Name] = OutputParameter( "Calculated Fracture Area", + display_name='Fracture area', value=self.fracarea.value, UnitType=Units.AREA, PreferredUnits=AreaUnit.METERS2, diff --git a/src/geophires_x/SUTRAEconomics.py b/src/geophires_x/SUTRAEconomics.py index b866f8fc3..dbe8f9f53 100644 --- a/src/geophires_x/SUTRAEconomics.py +++ b/src/geophires_x/SUTRAEconomics.py @@ -204,6 +204,7 @@ def __init__(self, model: Model): self.LCOH = self.OutputParameterDict[self.LCOH.Name] = OutputParameter( "Heat Sale Price Model", + display_name='Direct-Use heat breakeven price (LCOH)', value=[0.025], UnitType=Units.ENERGYCOST, PreferredUnits=EnergyCostUnit.CENTSSPERKWH, diff --git a/src/geophires_x_schema_generator/geophires-result.json b/src/geophires_x_schema_generator/geophires-result.json index 342b93428..0b4d17e28 100644 --- a/src/geophires_x_schema_generator/geophires-result.json +++ b/src/geophires_x_schema_generator/geophires-result.json @@ -19,8 +19,16 @@ }, "Average Direct-Use Heat Production": {}, "Direct-Use heat breakeven price": {}, - "Direct-Use heat breakeven price (LCOH)": {}, - "Direct-Use Cooling Breakeven Price (LCOC)": {}, + "Direct-Use heat breakeven price (LCOH)": { + "type": "number", + "description": "LCOH", + "units": "USD/MMBTU" + }, + "Direct-Use Cooling Breakeven Price (LCOC)": { + "type": "number", + "description": "LCOC", + "units": "USD/MMBTU" + }, "Annual District Heating Demand": {}, "Average Cooling Production": {}, "Average Annual Geothermal Heat Production": {}, @@ -46,11 +54,15 @@ }, "LCOH": { "type": "number", - "description": "", + "description": "LCOH", "units": "USD/MMBTU" }, "Lifetime Average Well Flow Rate": {}, - "Total Avoided Carbon Emissions": {} + "Total Avoided Carbon Emissions": { + "type": "number", + "description": "Total Saved Carbon Production", + "units": "pound" + } } }, "ECONOMIC PARAMETERS": { @@ -202,10 +214,26 @@ }, "Well separation: fracture diameter": {}, "Well separation: fracture height": {}, - "Fracture width": {}, - "Fracture area": {}, - "Number of fractures": {}, - "Fracture separation": {}, + "Fracture width": { + "type": "number", + "description": "Calculated Fracture Width", + "units": "meter" + }, + "Fracture area": { + "type": "number", + "description": "Calculated Fracture Area", + "units": "m**2" + }, + "Number of fractures": { + "type": "number", + "description": "Calculated Number of Fractures", + "units": null + }, + "Fracture separation": { + "type": "number", + "description": "Calculated Fracture Separation", + "units": "meter" + }, "Reservoir volume": {}, "Reservoir impedance": {}, "Reservoir hydrostatic pressure": {}, From 7d1419b07c84e1929d053efd27a4622a5c19c0ab Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 4 Apr 2025 12:50:43 -0700 Subject: [PATCH 18/24] more 1:1 output display names including o&m-related --- src/geophires_x/Economics.py | 6 +++-- src/geophires_x/Outputs.py | 22 ++++++++--------- .../geophires-result.json | 24 +++++++++++++++---- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index a65dff2fc..8cb6233df 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -1622,6 +1622,7 @@ def __init__(self, model: Model): ) self.Coamwell = self.OutputParameterDict[self.Coamwell.Name] = OutputParameter( Name="O&M Wellfield cost", + display_name='Wellfield maintenance costs', UnitType=Units.CURRENCYFREQUENCY, PreferredUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR, CurrentUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR @@ -1634,12 +1635,14 @@ def __init__(self, model: Model): ) self.Coamplant = self.OutputParameterDict[self.Coamplant.Name] = OutputParameter( Name="O&M Surface Plant costs", + display_name='Power plant maintenance costs', UnitType=Units.CURRENCYFREQUENCY, PreferredUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR, CurrentUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR ) self.Cgath = self.OutputParameterDict[self.Cgath.Name] = OutputParameter( Name="Field gathering system cost", + display_name='Field gathering system costs', UnitType=Units.CURRENCY, PreferredUnits=CurrencyUnit.MDOLLARS, CurrentUnits=CurrencyUnit.MDOLLARS @@ -1653,6 +1656,7 @@ def __init__(self, model: Model): ) self.Coamwater = self.OutputParameterDict[self.Coamwater.Name] = OutputParameter( Name="O&M Make-up Water costs", + display_name='Water costs', UnitType=Units.CURRENCYFREQUENCY, PreferredUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR, CurrentUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR @@ -1670,8 +1674,6 @@ def __init__(self, model: Model): PreferredUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR, CurrentUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR ) -# self.averageannualpumpingcosts = self.OutputParameterDict[ -# self.averageannualpumpingcosts.Name] = OutputParameter( #typo here!??! self.averageannualpumpingcosts = OutputParameter( Name="Average Annual Pumping Costs", UnitType=Units.CURRENCYFREQUENCY, diff --git a/src/geophires_x/Outputs.py b/src/geophires_x/Outputs.py index a1f40f377..0be94710f 100644 --- a/src/geophires_x/Outputs.py +++ b/src/geophires_x/Outputs.py @@ -437,7 +437,7 @@ def PrintOutputs(self, model: Model): f.write(f' of which Heat Pump Cost: {model.economics.heatpumpcapex.value:10.2f} ' + model.economics.Cplant.CurrentUnits.value + NL) if model.surfaceplant.plant_type.value == PlantType.DISTRICT_HEATING: f.write(f' of which Peaking Boiler Cost: {model.economics.peakingboilercost.value:10.2f} ' + model.economics.peakingboilercost.CurrentUnits.value + NL) - f.write(f' Field gathering system costs: {model.economics.Cgath.value:10.2f} ' + model.economics.Cgath.CurrentUnits.value + NL) + f.write(f' {model.economics.Cgath.display_name}: {model.economics.Cgath.value:10.2f} {model.economics.Cgath.CurrentUnits.value}\n') if model.surfaceplant.piping_length.value > 0: f.write(f' {model.economics.Cpiping.display_name}: {model.economics.Cpiping.value:10.2f} {model.economics.Cpiping.CurrentUnits.value}\n') if model.surfaceplant.plant_type.value == PlantType.DISTRICT_HEATING: @@ -459,18 +459,18 @@ def PrintOutputs(self, model: Model): f.write(' ***OPERATING AND MAINTENANCE COSTS (M$/yr)***\n') f.write(NL) if not model.economics.oamtotalfixed.Valid: - f.write(f' Wellfield maintenance costs: {model.economics.Coamwell.value:10.2f} ' + model.economics.Coamwell.CurrentUnits.value + NL) - f.write(f' Power plant maintenance costs: {model.economics.Coamplant.value:10.2f} ' + model.economics.Coamplant.CurrentUnits.value + NL) - f.write(f' Water costs: {model.economics.Coamwater.value:10.2f} ' + model.economics.Coamwater.CurrentUnits.value + NL) + f.write(f' {model.economics.Coamwell.display_name}: {model.economics.Coamwell.value:10.2f} {model.economics.Coamwell.CurrentUnits.value}\n') + f.write(f' {model.economics.Coamplant.display_name}: {model.economics.Coamplant.value:10.2f} {model.economics.Coamplant.CurrentUnits.value}\n') + f.write(f' {model.economics.Coamwater.display_name}: {model.economics.Coamwater.value:10.2f} {model.economics.Coamwater.CurrentUnits.value}\n') if model.surfaceplant.plant_type.value in [PlantType.INDUSTRIAL, PlantType.ABSORPTION_CHILLER, PlantType.HEAT_PUMP, PlantType.DISTRICT_HEATING]: - f.write(f' Average Reservoir Pumping Cost: {model.economics.averageannualpumpingcosts.value:10.2f} ' + model.economics.averageannualpumpingcosts.CurrentUnits.value + NL) - if model.surfaceplant.plant_type.value == PlantType.ABSORPTION_CHILLER: - f.write(f' Absorption Chiller O&M Cost: {model.economics.chilleropex.value:10.2f} ' + model.economics.chilleropex.CurrentUnits.value + NL) - if model.surfaceplant.plant_type.value == PlantType.HEAT_PUMP: - f.write(f' Average Heat Pump Electricity Cost: {model.economics.averageannualheatpumpelectricitycost.value:10.2f} ' + model.economics.averageannualheatpumpelectricitycost.CurrentUnits.value + NL) + f.write(f' Average Reservoir Pumping Cost: {model.economics.averageannualpumpingcosts.value:10.2f} {model.economics.averageannualpumpingcosts.CurrentUnits.value}\n7') + if model.surfaceplant.plant_type.value == PlantType.ABSORPTION_CHILLER: + f.write(f' Absorption Chiller O&M Cost: {model.economics.chilleropex.value:10.2f} {model.economics.chilleropex.CurrentUnits.value}\n') + if model.surfaceplant.plant_type.value == PlantType.HEAT_PUMP: + f.write(f' Average Heat Pump Electricity Cost: {model.economics.averageannualheatpumpelectricitycost.value:10.2f} {model.economics.averageannualheatpumpelectricitycost.CurrentUnits.value}\n') if model.surfaceplant.plant_type.value == PlantType.DISTRICT_HEATING: - f.write(f' Annual District Heating O&M Cost: {model.economics.dhdistrictoandmcost.value:10.2f} ' + model.economics.dhdistrictoandmcost.CurrentUnits.value + NL) - f.write(f' Average Annual Peaking Fuel Cost: {model.economics.averageannualngcost.value:10.2f} ' + model.economics.averageannualngcost.CurrentUnits.value + NL) + f.write(f' Annual District Heating O&M Cost: {model.economics.dhdistrictoandmcost.value:10.2f} {model.economics.dhdistrictoandmcost.CurrentUnits.value}\n') + f.write(f' Average Annual Peaking Fuel Cost: {model.economics.averageannualngcost.value:10.2f} {model.economics.averageannualngcost.CurrentUnits.value}\n') f.write(f' Total operating and maintenance costs: {(model.economics.Coam.value + model.economics.averageannualpumpingcosts.value+model.economics.averageannualheatpumpelectricitycost.value):10.2f} ' + model.economics.Coam.CurrentUnits.value + NL) else: diff --git a/src/geophires_x_schema_generator/geophires-result.json b/src/geophires_x_schema_generator/geophires-result.json index 0b4d17e28..04fdcbdab 100644 --- a/src/geophires_x_schema_generator/geophires-result.json +++ b/src/geophires_x_schema_generator/geophires-result.json @@ -339,7 +339,11 @@ "description": "", "units": "MUSD" }, - "Field gathering system costs": {}, + "Field gathering system costs": { + "type": "number", + "description": "Field gathering system cost", + "units": "MUSD" + }, "Total surface equipment costs": {}, "Exploration costs": { "type": "number", @@ -369,9 +373,21 @@ "OPERATING AND MAINTENANCE COSTS (M$/yr)": { "type": "object", "properties": { - "Wellfield maintenance costs": {}, - "Power plant maintenance costs": {}, - "Water costs": {}, + "Wellfield maintenance costs": { + "type": "number", + "description": "O&M Wellfield cost", + "units": "MUSD/yr" + }, + "Power plant maintenance costs": { + "type": "number", + "description": "O&M Surface Plant costs", + "units": "MUSD/yr" + }, + "Water costs": { + "type": "number", + "description": "O&M Make-up Water costs", + "units": "MUSD/yr" + }, "Average Reservoir Pumping Cost": {}, "Absorption Chiller O&M Cost": {}, "Average Heat Pump Electricity Cost": {}, From d3e71b83e38bd8c12b609516e443778eb257c8ec Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 4 Apr 2025 12:56:44 -0700 Subject: [PATCH 19/24] water heat capacity/density tooltip texts --- src/geophires_x/Reservoir.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/geophires_x/Reservoir.py b/src/geophires_x/Reservoir.py index e4422b299..f6b67cff4 100644 --- a/src/geophires_x/Reservoir.py +++ b/src/geophires_x/Reservoir.py @@ -472,13 +472,15 @@ def __init__(self, model: Model): self.cpwater = self.OutputParameterDict[self.cpwater.Name] = floatParameter( "cpwater", value=0.0, - UnitType=Units.NONE + UnitType=Units.NONE, + ToolTipText='water heat capacity' ) self.rhowater = self.OutputParameterDict[self.rhowater.Name] = floatParameter( "rhowater", value=0.0, - UnitType=Units.NONE + UnitType=Units.NONE, + ToolTipText='water density' ) self.averagegradient = self.OutputParameterDict[self.averagegradient.Name] = floatParameter( From f64e0466c5340165d7b5cb72c3a87409b05033ec Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 4 Apr 2025 13:29:17 -0700 Subject: [PATCH 20/24] generate categorized output parameters rst table for geophires --- src/geophires_x_schema_generator/__init__.py | 76 +++++++++++++++----- 1 file changed, 57 insertions(+), 19 deletions(-) diff --git a/src/geophires_x_schema_generator/__init__.py b/src/geophires_x_schema_generator/__init__.py index 8e0301d11..00f785606 100644 --- a/src/geophires_x_schema_generator/__init__.py +++ b/src/geophires_x_schema_generator/__init__.py @@ -269,16 +269,24 @@ def get_input_params_table(_category_params, category_name) -> str: return rst - @staticmethod - def get_output_params_table_rst(output_params_json) -> str: - """ - FIXME TODO consolidate with generated result schema - """ + def get_output_params_table_rst(self, output_params_json) -> str: + output_schema = self.get_result_json_schema(output_params_json) - output_params = json.loads(output_params_json) + output_params_by_category: dict = {} - output_rst = """ - .. list-table:: Output Parameters + for category, category_params in output_schema['properties'].items(): + if category not in output_params_by_category: + output_params_by_category[category] = {} # [] + + for param_name, param in category_params['properties'].items(): + output_params_by_category[category][param_name] = param + + def get_output_params_table(_category_params, category_name) -> str: + category_display = category_name if category_name is not None else '' + _output_rst = f""" +{category_display} +{'-' * len(category_display)} + .. list-table:: {category_display}{' ' if len(category_display) > 0 else ''}Parameters :header-rows: 1 * - Name @@ -286,19 +294,17 @@ def get_output_params_table_rst(output_params_json) -> str: - Preferred Units - Default Value Type""" - for param_name in output_params: - param = output_params[param_name] + for _param_name, _param in _category_params.items(): + _output_rst += f"""\n * - {_param_name} + - {_get_key(_param, 'description')} + - {_get_key(_param, 'units')} + - {_get_key(_param, 'type')}""" - def get_key(k): - if k in param and str(param[k]) != '': # noqa - return param[k] # noqa - else: - return '' + return _output_rst - output_rst += f"""\n * - {param['Name']} - - {get_key('ToolTipText')} - - {get_key('PreferredUnits')} - - {get_key('json_parameter_type')}""" + output_rst = '' + for category, category_params in output_params_by_category.items(): + output_rst += get_output_params_table(category_params, category) return output_rst @@ -343,6 +349,38 @@ def get_schema_title(self) -> str: def get_result_json_schema(self, output_params_json) -> dict: return None # FIXME TODO + def get_output_params_table_rst(self, output_params_json) -> str: + """ + FIXME TODO consolidate with generated result schema + """ + + output_params = json.loads(output_params_json) + + output_rst = """ + .. list-table:: Output Parameters + :header-rows: 1 + + * - Name + - Description + - Preferred Units + - Default Value Type""" + + for param_name in output_params: + param = output_params[param_name] + + def get_key(k): + if k in param and str(param[k]) != '': # noqa + return param[k] # noqa + else: + return '' + + output_rst += f"""\n * - {param['Name']} + - {get_key('ToolTipText')} + - {get_key('PreferredUnits')} + - {get_key('json_parameter_type')}""" + + return output_rst + def _get_logger(logger_name=None): sh = logging.StreamHandler(sys.stdout) From ae4e7721e3be7c9fb24a16fb5c9de336e46be0e6 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 4 Apr 2025 13:33:40 -0700 Subject: [PATCH 21/24] strip inaccurate/misleading result category unit suffixes in output rst --- src/geophires_x_schema_generator/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/geophires_x_schema_generator/__init__.py b/src/geophires_x_schema_generator/__init__.py index 00f785606..bbb4454b6 100644 --- a/src/geophires_x_schema_generator/__init__.py +++ b/src/geophires_x_schema_generator/__init__.py @@ -283,6 +283,7 @@ def get_output_params_table_rst(self, output_params_json) -> str: def get_output_params_table(_category_params, category_name) -> str: category_display = category_name if category_name is not None else '' + category_display = category_display.replace(' (M$)', '').replace(' (M$/yr)', '') _output_rst = f""" {category_display} {'-' * len(category_display)} From a41ab0bf52d42b4ebb534e5695d0db34b87caa78 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 4 Apr 2025 13:49:00 -0700 Subject: [PATCH 22/24] fix typo --- src/geophires_x/Outputs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/geophires_x/Outputs.py b/src/geophires_x/Outputs.py index 0be94710f..934c5b69c 100644 --- a/src/geophires_x/Outputs.py +++ b/src/geophires_x/Outputs.py @@ -463,7 +463,7 @@ def PrintOutputs(self, model: Model): f.write(f' {model.economics.Coamplant.display_name}: {model.economics.Coamplant.value:10.2f} {model.economics.Coamplant.CurrentUnits.value}\n') f.write(f' {model.economics.Coamwater.display_name}: {model.economics.Coamwater.value:10.2f} {model.economics.Coamwater.CurrentUnits.value}\n') if model.surfaceplant.plant_type.value in [PlantType.INDUSTRIAL, PlantType.ABSORPTION_CHILLER, PlantType.HEAT_PUMP, PlantType.DISTRICT_HEATING]: - f.write(f' Average Reservoir Pumping Cost: {model.economics.averageannualpumpingcosts.value:10.2f} {model.economics.averageannualpumpingcosts.CurrentUnits.value}\n7') + f.write(f' Average Reservoir Pumping Cost: {model.economics.averageannualpumpingcosts.value:10.2f} {model.economics.averageannualpumpingcosts.CurrentUnits.value}\n') if model.surfaceplant.plant_type.value == PlantType.ABSORPTION_CHILLER: f.write(f' Absorption Chiller O&M Cost: {model.economics.chilleropex.value:10.2f} {model.economics.chilleropex.CurrentUnits.value}\n') if model.surfaceplant.plant_type.value == PlantType.HEAT_PUMP: From 72fd0dcfaad8427e052ef2fe7621925b22983486 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 4 Apr 2025 14:05:21 -0700 Subject: [PATCH 23/24] add links to schemas in parameters reference --- src/geophires_x_schema_generator/__init__.py | 39 ++++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/src/geophires_x_schema_generator/__init__.py b/src/geophires_x_schema_generator/__init__.py index bbb4454b6..fa775f8fd 100644 --- a/src/geophires_x_schema_generator/__init__.py +++ b/src/geophires_x_schema_generator/__init__.py @@ -253,6 +253,25 @@ def get_input_params_table(_category_params, category_name) -> str: output_rst = self.get_output_params_table_rst(output_params_json) + schema_ref_base_url = ( + 'https://github.com/softwareengineerprogrammer/GEOPHIRES/blob/main/src/geophires_x_schema_generator/' + ) + input_schema_ref_rst = '' + if self.get_input_schema_reference() is not None: + input_schema_ref_rst = ( + f'Schema: ' + f'`{self.get_input_schema_reference()} ' + f'<{schema_ref_base_url}{self.get_input_schema_reference()}>`__' + ) + + output_schema_ref_rst = '' + if self.get_output_schema_reference() is not None: + output_schema_ref_rst = ( + f'Schema: ' + f'`{self.get_output_schema_reference()} ' + f'<{schema_ref_base_url}{self.get_output_schema_reference()}>`__' + ) + rst = f"""{self.get_schema_title()} Parameters ========== @@ -260,10 +279,12 @@ def get_input_params_table(_category_params, category_name) -> str: Input Parameters ################ +{input_schema_ref_rst} {input_rst} -Output Parameters +Outputs ################# +{output_schema_ref_rst} {output_rst} """ @@ -287,7 +308,7 @@ def get_output_params_table(_category_params, category_name) -> str: _output_rst = f""" {category_display} {'-' * len(category_display)} - .. list-table:: {category_display}{' ' if len(category_display) > 0 else ''}Parameters + .. list-table:: {category_display}{' ' if len(category_display) > 0 else ''}Outputs :header-rows: 1 * - Name @@ -309,6 +330,12 @@ def get_output_params_table(_category_params, category_name) -> str: return output_rst + def get_input_schema_reference(self) -> str: + return 'geophires-request.json' + + def get_output_schema_reference(self) -> str: + return 'geophires-result.json' + def _get_key(param: dict, k: str, default_val='') -> Any: if k in param and str(param[k]) != '': @@ -358,7 +385,7 @@ def get_output_params_table_rst(self, output_params_json) -> str: output_params = json.loads(output_params_json) output_rst = """ - .. list-table:: Output Parameters + .. list-table:: Outputs :header-rows: 1 * - Name @@ -382,6 +409,12 @@ def get_key(k): return output_rst + def get_input_schema_reference(self) -> str: + return 'hip-ra-x-request.json' + + def get_output_schema_reference(self) -> str: + return None + def _get_logger(logger_name=None): sh = logging.StreamHandler(sys.stdout) From 10a1af486e185817884123d6f7836344a4e45521 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 4 Apr 2025 14:55:16 -0700 Subject: [PATCH 24/24] sanit test of outputs rst --- .../test_geophires_x_schema_generator.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py b/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py index 90627c391..40ecab098 100644 --- a/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py +++ b/tests/geophires_x_schema_generator_tests/test_geophires_x_schema_generator.py @@ -11,6 +11,35 @@ def test_parameters_rst(self): rst = g.generate_parameters_reference_rst() self.assertIsNotNone(rst) # TODO sanity checks on content + def test_outputs_rst(self): + g = GeophiresXSchemaGenerator() + _, output_params_json = g.get_parameters_json() + rst = g.get_output_params_table_rst(output_params_json) + + self.assertIn( + """ECONOMIC PARAMETERS +------------------- + .. list-table:: ECONOMIC PARAMETERS Outputs + :header-rows: 1 + + * - Name + - Description + - Preferred Units + - Default Value Type + * - Economic Model +""", + rst, + ) + + self.assertIn( + """ * - Project IRR + - Project Internal Rate of Return + - % + - number +""", + rst, + ) + def test_get_json_schema(self): g = GeophiresXSchemaGenerator() req_schema, result_schema = g.generate_json_schema()