diff --git a/shared/lib_geothermal.cpp b/shared/lib_geothermal.cpp index def77e0a5..e1dc99538 100644 --- a/shared/lib_geothermal.cpp +++ b/shared/lib_geothermal.cpp @@ -1158,11 +1158,11 @@ double CGeothermalAnalyzer::Gringarten() double CGeothermalAnalyzer::RameyWellbore() { - double alpharock = mo_geo_in.md_EGSThermalConductivity / (mo_geo_in.md_EGSRockDensity * mo_geo_in.md_EGSSpecificHeatConstant); + double alpharock = (mo_geo_in.md_EGSThermalConductivity) / (mo_geo_in.md_EGSRockDensity * mo_geo_in.md_EGSSpecificHeatConstant); double time = 0; double working_temp = md_WorkingTemperatureC; if (mp_geo_out->ElapsedHours < 0.1) { - time = 744 * 3600; + time = 8760 * 3600; working_temp = GetResourceTemperatureC(); } else { @@ -1170,12 +1170,12 @@ double CGeothermalAnalyzer::RameyWellbore() working_temp = md_WorkingTemperatureC; } double utilfactor = 1.0; //capacity factor? - double avg_gradient = 2 / GetResourceDepthM(); //local average geothermal gradient - double framey = -1.0 * log(1.1 * (0.3048 * (mo_geo_in.md_DiameterProductionWellInches / (2 * 12)) / sqrt(4.0 * alpharock * time * utilfactor))) - 0.29; + double avg_gradient = (GetResourceTemperatureC() - 11.6) / GetResourceDepthM(); //local average geothermal gradient + double framey = -1.0 * log(1.1 * (0.3048 * (mo_geo_in.md_DiameterPumpCasingInches / (2 * 12)) / sqrt(4.0 * alpharock * time * utilfactor))) - 0.29; double c_w = geothermal::EGSSpecificHeat(working_temp); double rameyA = mo_geo_in.md_ProductionFlowRateKgPerS * c_w * framey / (2 * physics::PI * mo_geo_in.md_EGSThermalConductivity); double ProdTempDrop = -1.0 * ((GetResourceTemperatureC() - working_temp) - avg_gradient * (GetResourceDepthM() - rameyA) + (working_temp - avg_gradient * rameyA - GetResourceTemperatureC()) * exp(-GetResourceDepthM() / rameyA)); - return ProdTempDrop; + return (isfinite(ProdTempDrop)) ? ProdTempDrop : 0.0; } double CGeothermalAnalyzer::DT_prod_well(double prod_well_choice) @@ -1513,8 +1513,71 @@ double CGeothermalAnalyzer::flowRateTotal(void) { } } // lbs per hour, all wells +void CGeothermalAnalyzer::WellCountDecisionTable(void) +{ + int well_stim = mo_geo_in.md_WellsStimulated; //0 - injection only, 1 - production only, 2 - both + double production_stim_well = 0; //Column A + double injection_stim_well = 0; //Column B + double stim_well = 0; //Column C + double SWDDE = mo_geo_in.md_ExplorationWellsProd; + switch (well_stim) { + case 0: + if (mo_geo_in.me_rt == HYDROTHERMAL) { + production_stim_well = SWDDE; + injection_stim_well = 0; + } + else if (SWDDE >= 1.0) { + production_stim_well = SWDDE - 1; + injection_stim_well = 1; + } + else if (SWDDE > 0.0) { + production_stim_well = 0; + injection_stim_well = SWDDE; + } + else { + production_stim_well = 0; + injection_stim_well = 0; + } + case 1: + if (mo_geo_in.me_rt == HYDROTHERMAL) { + production_stim_well = SWDDE; + injection_stim_well = 0; + } + else if (SWDDE >= 1.0) { + production_stim_well = 1; + injection_stim_well = SWDDE - 1; + } + else if (SWDDE > 0.0) { + production_stim_well = 0; + injection_stim_well = SWDDE; + } + else { + production_stim_well = 0; + injection_stim_well = 0; + } + case 2: + if (mo_geo_in.me_rt == HYDROTHERMAL) { + production_stim_well = SWDDE; + injection_stim_well = 0; + } + else if (SWDDE > 0.0) { + production_stim_well = SWDDE/2; + injection_stim_well = SWDDE/2; + } + else { + production_stim_well = 0; + injection_stim_well = 0; + } + } + mp_geo_out->ProdWellsExploration = production_stim_well; + mp_geo_out->InjWellsExploration = injection_stim_well; +} + double CGeothermalAnalyzer::GetNumberOfWells(void) { + WellCountDecisionTable(); + bool inj_wells_stimulated = (mo_geo_in.md_WellsStimulated == 0 || mo_geo_in.md_WellsStimulated == 2); + bool prod_wells_stimulated = (mo_geo_in.md_WellsStimulated == 1 || mo_geo_in.md_WellsStimulated == 2); if (mo_geo_in.me_cb == NUMBER_OF_WELLS) { mp_geo_out->md_NumberOfWells = mo_geo_in.md_NumberOfWells; @@ -1534,10 +1597,12 @@ double CGeothermalAnalyzer::GetNumberOfWells(void) mp_geo_out->md_PumpWorkWattHrPerLb = GetPumpWorkWattHrPerLb(); mp_geo_out->md_NumberOfWells = mo_geo_in.md_DesiredSalesCapacityKW / netCapacityPerWell; if (mp_geo_out->md_NumberOfWells < 0) mp_geo_out->md_NumberOfWells = 0; - mp_geo_out->md_NumberOfWellsProdExp = mp_geo_out->md_NumberOfWells - mo_geo_in.md_ExplorationWellsProd - mp_geo_out->md_FailedWells; + mp_geo_out->md_NumberOfWellsProdExp = (mp_geo_out->md_NumberOfWells > mp_geo_out->ProdWellsExploration) ? mp_geo_out->md_NumberOfWells - mo_geo_in.md_ExplorationWellsProd : 0.0; mp_geo_out->md_NumberOfWellsProdDrilled = mp_geo_out->md_NumberOfWellsProdExp / (1 - (1 - mo_geo_in.md_StimSuccessRate) * (1 - mo_geo_in.md_DrillSuccessRate)); double num_prod_wells_successful = mp_geo_out->md_NumberOfWellsProdDrilled * mo_geo_in.md_DrillSuccessRate; double num_prod_wells_failed = mp_geo_out->md_NumberOfWellsProdDrilled * (1 - mo_geo_in.md_DrillSuccessRate); + //2. # of Injection Wells Required = # successful injection wells required in drilling phase + # successful injection wells drilled in exploration phase + double inj_flow = flowRatePerWell() * mp_geo_out->md_NumberOfWells; if (mo_geo_in.me_ct == FLASH) inj_flow -= inj_flow * (waterLoss() / 1000.0); double failed_prod_wells_inj = mp_geo_out->md_NumberOfWellsProdDrilled - num_prod_wells_successful; @@ -1549,20 +1614,50 @@ double CGeothermalAnalyzer::GetNumberOfWells(void) double prod_failed_inj_rate = (mo_geo_in.md_FailedProdFlowRatio * mo_geo_in.md_ReservoirDeltaPressure) * (mo_geo_in.md_InjWellPressurePSI + geothermal::MetersToFeet(GetResourceDepthM()) * InjectionDensity() / 144.0 + (pressure_well_head) - mo_geo_in.md_ProdWellFriction * pow(mo_geo_in.md_FailedProdFlowRatio, 2) - pressureHydrostaticPSI()); - double inj_failed_inj_rate = (mo_geo_in.md_FailedProdFlowRatio * mo_geo_in.md_ReservoirDeltaPressure) * + double inj_failed_inj_rate = (mo_geo_in.md_FailedProdFlowRatio > 0) ? (mo_geo_in.md_FailedProdFlowRatio * mo_geo_in.md_ReservoirDeltaPressure) * (mo_geo_in.md_InjWellPressurePSI + geothermal::MetersToFeet(GetResourceDepthM()) * InjectionDensity() / 144.0 + (pressure_well_head) - - mo_geo_in.md_InjWellFriction * pow(mo_geo_in.md_FailedProdFlowRatio, 2) - pressureHydrostaticPSI()); + mo_geo_in.md_InjWellFriction * pow(mo_geo_in.md_FailedProdFlowRatio, 2) - pressureHydrostaticPSI()) : 0.0; double inj_rate_failed_prod_wells = MIN(prod_failed_inj_rate, flowRatePerWell()); //Injectivity of failed production well? double inj_rate_failed_inj_wells = MIN(inj_failed_inj_rate, flowRatePerWell()); + double inj_rate_successful_inj_wells = flowRatePerWell() / mo_geo_in.md_RatioInjectionToProduction; + double num_failed_prod_wells_inj = (mo_geo_in.md_FailedProdFlowRatio > 0) ? mp_geo_out->md_NumberOfWellsProdDrilled - num_prod_wells_successful : 0.0; + double successful_inj_wells_required_drilling = (inj_flow - (num_failed_prod_wells_inj * prod_failed_inj_rate)) / (inj_rate_successful_inj_wells + (inj_rate_failed_inj_wells * (1 / (mo_geo_in.md_DrillSuccessRate) - 1))); + double successful_inj_wells_exploration = mp_geo_out->InjWellsExploration; + double num_injection_wells_required = successful_inj_wells_required_drilling + successful_inj_wells_exploration; mp_geo_out->md_NumberOfWellsInj = (inj_flow - (failed_prod_wells_inj * inj_rate_failed_prod_wells)) / (flowPerWellInj + inj_rate_failed_inj_wells * (1 / (mo_geo_in.md_DrillSuccessRate) - 1)); - mp_geo_out->md_NumberOfWellsInjDrilled = (1 / mo_geo_in.md_DrillSuccessRate) * mp_geo_out->md_NumberOfWellsInj; + mp_geo_out->md_NumberOfWellsInjDrilled = (1 / mo_geo_in.md_DrillSuccessRate) * successful_inj_wells_required_drilling + successful_inj_wells_exploration; double num_inj_wells_successful = mp_geo_out->md_NumberOfWellsInjDrilled * mo_geo_in.md_DrillSuccessRate; double num_inj_wells_failed = mp_geo_out->md_NumberOfWellsInjDrilled * (1 - mo_geo_in.md_DrillSuccessRate); //mp_geo_out->md_NumberOfWellsInj = (mo_geo_in.md_DesiredSalesCapacityKW / (netBrineEffectiveness / 1000)) * (mp_geo_out->md_FractionGFInjected) / flowPerWellInj; mp_geo_out->md_InjPump_hp = ( (mp_geo_out->md_NumberOfWellsInj * flowPerWellInj * GetInjectionPumpWorkft()) / (60 * 33000) ) / mo_geo_in.md_GFPumpEfficiency; if (mo_geo_in.me_rt == EGS) { - mp_geo_out->md_NumberOfWellsProdExp = mp_geo_out->md_NumberOfWells - 8; - mp_geo_out->md_NumberOfWellsProdDrilled = mp_geo_out->md_NumberOfWellsProdExp / mo_geo_in.md_DrillSuccessRate; + //mp_geo_out->md_NumberOfWellsProdExp = mp_geo_out->md_NumberOfWells - 8; + if (mo_geo_in.md_WellsStimulated != 1) mp_geo_out->md_NumberOfWellsProdDrilled = mp_geo_out->md_NumberOfWellsProdExp / (mo_geo_in.md_DrillSuccessRate); + else { + double failed_prod_well_stimulations = 0; + double successful_prod_well_stimulations = 0; + if (mo_geo_in.md_WellsStimulated == 3) { + failed_prod_well_stimulations = 0; + successful_prod_well_stimulations = 0; + } + else { + failed_prod_well_stimulations = mp_geo_out->md_NumberOfWellsProdExp * (1 / mo_geo_in.md_StimSuccessRate - 1); + successful_prod_well_stimulations = mp_geo_out->md_NumberOfWellsProdExp; + } + mp_geo_out->md_NumberOfWellsProdDrilled = (failed_prod_well_stimulations + successful_prod_well_stimulations) / mo_geo_in.md_DrillSuccessRate; + + } + double inj_wells_drilled = 0; + if (inj_wells_stimulated) { + inj_wells_drilled = mp_geo_out->md_NumberOfWellsInj / (mo_geo_in.md_DrillSuccessRate * mo_geo_in.md_StimSuccessRate); + } + else { + inj_wells_drilled = mp_geo_out->md_NumberOfWellsInj / (mo_geo_in.md_DrillSuccessRate); + } + mp_geo_out->md_NumberOfWellsInj = inj_flow / inj_rate_successful_inj_wells - successful_inj_wells_exploration; + mp_geo_out->md_NumberOfWellsInjDrilled = inj_wells_drilled; + //mp_geo_out->md_NumberOfWellsProdDrilled = mp_geo_out->md_NumberOfWellsProdExp / mo_geo_in.md_DrillSuccessRate; + /* num_prod_wells_failed = mp_geo_out->md_NumberOfWellsProdExp / mo_geo_in.md_DrillSuccessRate * (1 - mo_geo_in.md_DrillSuccessRate); num_prod_wells_successful = mp_geo_out->md_NumberOfWellsProdExp; inj_flow = flowRatePerWell() * mp_geo_out->md_NumberOfWells * (1 + (1 / (1 - 0.05) - 1)); @@ -1572,6 +1667,7 @@ double CGeothermalAnalyzer::GetNumberOfWells(void) num_inj_wells_failed = mp_geo_out->md_NumberOfWellsInjDrilled * mo_geo_in.md_DrillSuccessRate; double inj_wells_stim = successful_inj_wells_expl / mo_geo_in.md_StimSuccessRate; double failed_stim_wells = inj_wells_stim - successful_inj_wells_expl; + */ } if (mp_geo_out->md_NumberOfWellsInj < 0) mp_geo_out->md_NumberOfWellsInj = 0; diff --git a/shared/lib_geothermal.h b/shared/lib_geothermal.h index 9dcdff2cc..8e6d4759f 100644 --- a/shared/lib_geothermal.h +++ b/shared/lib_geothermal.h @@ -107,6 +107,7 @@ struct SGeothermal_Inputs double md_ProdWellFriction; double md_NumberOfWells; // entered or calculated, depending on 'cb' double md_NumberofWellsInj; + int md_WellsStimulated; // 0 - Injection only, 1 - Production only, 2 - both, 3 - neither double md_PlantEfficiency; // not in GETEM - essentially the ratio of plant brine effectiveness to max possible brine effectiveness double md_TemperatureDeclineRate; // '% per year, 3% is default double md_MaxTempDeclineC; // degrees C, default = 30 @@ -181,6 +182,8 @@ struct SGeothermal_Outputs //Following list of variables used as inputs in cmod_geothermal_costs.cpp for calculating direct geothermal plant cost: double md_NumberOfWells; double md_NumberOfWellsProdExp; + double ProdWellsExploration; + double InjWellsExploration; double md_NumberOfWellsProdDrilled; double md_NumberOfWellsInjDrilled; double md_FailedWells; @@ -368,6 +371,7 @@ class CGeothermalAnalyzer double flowRatePerWell(void); // take Kg/second input and translate to lbs/hour double flowRateTotal(void); // flow rate per well * number of wells double GetNumberOfWells(void); + void WellCountDecisionTable(void); double GetPlantBrineEffectiveness(void); // turbine output diff --git a/ssc/cmod_geothermal.cpp b/ssc/cmod_geothermal.cpp index 30ee25538..2375400d2 100644 --- a/ssc/cmod_geothermal.cpp +++ b/ssc/cmod_geothermal.cpp @@ -400,7 +400,7 @@ class cm_geothermal : public compute_module // assign values for UI results assign("num_wells_getem_output", var_data((ssc_number_t)geo_outputs.md_NumberOfWells)); - assign("num_wells_getem_inj", var_data((ssc_number_t)geo_outputs.md_NumberOfWellsInj)); + assign("num_wells_getem_inj", var_data((ssc_number_t)geo_outputs.md_NumberOfWellsInjDrilled)); assign("plant_brine_eff", var_data((ssc_number_t)geo_outputs.md_PlantBrineEffectiveness)); assign("pump_watthr_per_lb", var_data((ssc_number_t)geo_outputs.md_PumpWorkWattHrPerLb)); assign("pumpwork_prod", var_data((ssc_number_t)geo_outputs.md_pumpwork_prod)); diff --git a/ssc/cmod_geothermal_costs.cpp b/ssc/cmod_geothermal_costs.cpp index 0a83495b5..05621cb87 100644 --- a/ssc/cmod_geothermal_costs.cpp +++ b/ssc/cmod_geothermal_costs.cpp @@ -93,10 +93,15 @@ static var_info _cm_vtab_geothermal_costs[] = { { SSC_INPUT, SSC_NUMBER, "geotherm.cost.conf_multiplier", "Confirmation cost multiplier", "", "?=1.2", "GeoHourly", "calc_drill_costs=1", "", "" }, { SSC_INPUT, SSC_NUMBER, "geotherm.cost.expl_num_wells", "Number of exploration wells", "", "?=2", "GeoHourly", "calc_drill_costs=1", "", "" }, { SSC_INPUT, SSC_NUMBER, "geotherm.cost.conf_num_wells", "Number of confirmation wells", "", "?=2", "GeoHourly", "calc_drill_costs=1", "", "" }, + { SSC_INPUT, SSC_NUMBER, "geotherm.cost.pump_fixed", "Fixed pump workover and casing cost", "$", "", "GeoHourly", "", "", "" }, + { SSC_INPUT, SSC_NUMBER, "geotherm.cost.pump_per_foot", "Pump cost per foot", "$/ft", "", "GeoHourly", "", "", "" }, + { SSC_INPUT, SSC_NUMBER, "geotherm.cost.pump_geotherm.cost.pump_depth", "Pump depth", "ft", "", "GeoHourly", "", "", "" }, + { SSC_INPUT, SSC_NUMBER, "geotherm.cost.prod_req", "Number of production wells required", "", "", "GeoHourly", "", "", "" }, + { SSC_INPUT, SSC_NUMBER, "pump_size_hp", "Production pump power", "hp", "", "GeoHourly", "", "", "" }, + { SSC_INPUT, SSC_NUMBER, "inj_pump_hp", "Injection pump power", "hp", "", "GeoHourly", "", "", "" }, // Outputs - { SSC_OUTPUT, SSC_NUMBER, "baseline_cost", "Baseline cost", "$/kW", "", "GeoHourly", "?", "", "" }, { SSC_OUTPUT, SSC_NUMBER, "inj_total_cost", "Total injection well cost", "$", "", "GeoHourly", "calc_drill_costs=1", "", "" }, { SSC_OUTPUT, SSC_NUMBER, "prod_total_cost", "Total production well cost", "$", "", "GeoHourly", "calc_drill_costs=1", "", "" }, @@ -104,6 +109,8 @@ static var_info _cm_vtab_geothermal_costs[] = { { SSC_OUTPUT, SSC_NUMBER, "expl_total_cost", "Total exploration well cost", "$", "", "GeoHourly", "calc_drill_costs=1", "", "" }, { SSC_OUTPUT, SSC_NUMBER, "conf_total_cost", "Total confirmation well cost", "$", "", "GeoHourly", "calc_drill_costs=1", "", "" }, { SSC_OUTPUT, SSC_NUMBER, "drilling_total_cost", "Total drilling cost", "$", "", "GeoHourly", "calc_drill_costs=1", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "total_pump_cost", "Total pumping cost", "$/kW", "", "GeoHourly", "?", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "total_gathering_cost", "Total gathering well cost", "$/kW", "", "GeoHourly", "?", "", "" }, var_info_invalid }; @@ -826,11 +833,45 @@ class cm_geothermal_costs : public compute_module } - - - - - + //Pump costs + double workover_casing_cost = as_double("geotherm.cost.pump_fixed"); + //double casing_cost = as_double("casing_cost"); + double installation_cost_per_foot = as_double("geotherm.cost.pump_per_foot"); + double pump_set_depth = as_double("geotherm.cost.pump_depth"); + double num_prod_wells = as_double("geotherm.cost.prod_req"); + double num_inj_wells = as_double("inj_num_pumps"); + double prod_pump_power = as_double("pump_size_hp"); + double other_pump_install_cost = 5750 * pow(prod_pump_power, 0.2) * pump_ppi[ppi_base_year]; + double prod_pump_cost_per_well = workover_casing_cost + installation_cost_per_foot * pump_set_depth * pump_ppi[ppi_base_year] + other_pump_install_cost; + double production_pump_cost = prod_pump_cost_per_well * num_prod_wells; + if (conversion_type == FLASH) production_pump_cost = 0; + //Calculated injection pump cost + double inj_pump_power = as_double("inj_pump_hp"); + double num_injection_pumps = std::ceil(inj_pump_power / 2000.0); + double inj_pump_cost_per_pump = 1750 * pow(inj_pump_power, 0.7) * 3.0 * pow(inj_pump_power, -0.11); + double injection_pump_cost = num_injection_pumps * inj_pump_cost_per_pump * pump_ppi[ppi_base_year]; + + double indirect_pump_cost = (production_pump_cost + injection_pump_cost) * (1.0 / (1.0 - 0.12) - 1.0); + + double total_pump_cost = production_pump_cost + injection_pump_cost + indirect_pump_cost; + assign("total_pump_cost", var_data(static_cast(total_pump_cost))); + + //Field Gathering cost + double pipe_diam = as_double("geotherm.cost.prod_cost_curve_welldiam"); //inches + if (pipe_diam == 0) pipe_diam = 12.5; + else pipe_diam = 8.75; + double pipe_outer_diam = pipe_diam + 2 * 0.375; //inches + double pipe_cost_per_foot = 0.4249 * pow(pipe_outer_diam, 2) - 0.0472 * pipe_outer_diam + 40.683; + double pipe_cost_per_foot_adj = pipe_cost_per_foot * steel_ppi[ppi_base_year]; + double distance_plant_to_well = 1640.4; + int resource_type = as_integer("resource_type"); + if (resource_type == 0) distance_plant_to_well = 2460.63; + double piping_cost_per_well = pipe_cost_per_foot_adj * distance_plant_to_well; //average distance from well to plant (ft)? + double gathering_cost_total = piping_cost_per_well * (num_prod_wells + num_inj_wells); + assign("total_gathering_cost", var_data(static_cast(gathering_cost_total))); + + double indirect_pump_gathering_cost = (total_pump_cost + gathering_cost_total) * (1.0 / (1 - 0.12) - 1); + assign("indirect_pump_gathering_cost", var_data(static_cast(indirect_pump_gathering_cost))); //OM Cost calculations /*