# -*- coding: utf-8 -*-
"""
Created on Tue Sep  3 14:19:35 2024

@author: chris
"""

# import libraries
import pandas as pd
from scipy.interpolate import interp1d
from math import ceil
import json

# PySAM unit modules
import PySAM.TroughPhysicalIph as tr # system model
import PySAM.LcoefcrDesign as lh     # LCOH model

# user inputs =================================================================

weather_files = ['denver_co_39.74_-104.992_psm3-2-2-tmy_60_tmy', 'boston_ma_42.3587_-71.0567_psm3-2-2-tmy_60_tmy']
location_names = ['Denver, CO', 'Boston, MA'] # for output
process_temps = [100, 300, 500] # industrial process temperatures (C)
solar_multiples = [1, 3, 5]
thermal_storage_capacities = [24, 72, 120] # hours
demand = 100 # industrial process heat demand size (MWth)

# create default SAM model ====================================================

# create an instance of the TcstroughEmpirical module with defaults from the Parabolic Trough - LCOH Calculator configuration
system_model = tr.default('PhysicalTroughIPHLCOHCalculator')
lcoh_model = lh.from_existing(system_model, 'PhysicalTroughIPHLCOHCalculator')

# attempted fix to utf-8 encoding error (temporary) ===========================

# # Create a new instance of the TroughPhysicalIph and LcoefcrDesign module
# system_model = tr.new()
# lcoh_model = lh.new()

# # Get the system model inputs from the JSON file
# with open('PhysicalTroughIPHLCOHCalculator_default_trough_physical_iph.json', 'r') as f:
#     tr_inputs = json.load(f)

# # Iterate through the input key-value pairs and set the module inputs
# for k, v in tr_inputs.items():
#     # temporary fix to 'adjust' variable mal-naming
#     if 'adjust' in k:
#         k = k.split('adjust_')[1]
#     if k != 'number_inputs':
#         system_model.value(k, v)

# # Get the LCOH model inputs from the JSON file
# with open('PhysicalTroughIPHLCOHCalculator_default_lcoefcr_design.json', 'r') as f:
#     lh_inputs = json.load(f)

# # Iterate through the input key-value pairs and set the module inputs
# for k, v in lh_inputs.items():
#     # temporary fix to 'adjust' variable mal-naming
#     if 'adjust' in k:
#         k = k.split('adjust_')[1]
#     if k != 'number_inputs':
#         lcoh_model.value(k, v)

# non-iterative inputs ========================================================
# (changed default SAM values, but will not change / perturb further)

# tax assumptions
lcoh_model.value('sales_tax_rate', 0)
lcoh_model.value('c_tax_rate', 0)

# fixed operating cost
lcoh_model.value('fixed_operating_cost', 80 * demand * 1000) # 2023 USD / yr.

# iterative inputs ============================================================
# (calculated or obtained automatically by the code for each case)

i, loc = 0, 0
for weather_file in weather_files: # by location

    # Location name (for output)
    loc += 1
    location_name = location_names[loc]
    
    # Look up design point DNI (W/m2) from weather file
    weather_file_path = 'C:\\Users\\chris\\SAM Downloaded Weather Files\\' + weather_file + '.csv'
    df = pd.read_csv(weather_file_path)
    I_bn_des_value = int(df.iloc[4118,5]) # DNI at noon on the summer solstice (June 21 in northern hemisphere) (W/m2)
    print(I_bn_des_value)
    system_model.value('I_bn_des_value', I_bn_des_value)
    
    for process_temp in process_temps: # by process temperature
        
        # Set loop outlet temperature
        T_loop_out = process_temp # C
        system_model.value('T_loop_out', T_loop_out) # C
        
        # Select heat transfer fluid
        if T_loop_out <= 200:
            system_model.value('combo_htf_type', 'Pressurized Water') # field HTF
            system_model.value('combo_tes_htf_type', 'Pressurized Water') # tank HTF
            min_operating_temp = 10 # C
            max_operating_temp = 220 # C
            rho = interp1d([10, 20, 30, 40, 50, 60, 70, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220] + 273.15, # K
                           [999.7, 998.2, 995.6, 992.2, 988, 983.2, 977.7, 971.8, 965.3, 958.4, 951, 943.2, 934.9, 926.2, 917.1, 907.5, 897.5, 887.1, 876.1, 864.7, 852.8, 840.3]) # kg/m3
            cp = interp1d([10, 20, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220] + 273.15, # K
                           [4.23, 4.19, 4.18, 4.19, 4.20, 4.22, 4.23, 4.25, 4.27, 4.29, 4.31, 4.34, 4.37, 4.40, 4.44, 4.49, 4.54, 4.60]*1000) # J/kgK
        elif T_loop_out <= 250:
            system_model.value('combo_htf_type', 'Dowtherm Q') # field HTF
            system_model.value('combo_tes_htf_type', 'Dowtherm Q') # tank HTF
            min_operating_temp = -35 # C
            max_operating_temp = 330 # C
            # rho = interp1d(...)
            # cp = iterp1d(...)
        else:
            system_model.value('combo_htf_type', 'Hitec Solar Salt') # field HTF
            system_model.value('combo_tes_htf_type', 'Hitec Solar Salt') # tank HTF
            min_operating_temp = 238 # C
            max_operating_temp = 593 # C
            rho = interp1d([0, 1E9], [1750, 1750]) # placeholder (kg/m3)
            cp = interp1d([0, 1E9], [1550, 1550]) # placeholder (J/kgK)
        
        # Set loop inlet temperature
        T_loop_in_des = max(40, min_operating_temp + 20) # C
        system_model.value('T_loop_in_des', T_loop_in_des) # C
        
        # Freeze protection temperature
        system_model.value('T_fp', min_operating_temp) # C
        
        # Tank heater setpoints
        system_model.value('cold_tank_Thtr', T_loop_in_des - 30) # C
        system_model.value('hot_tank_Thtr', T_loop_out - 40) # C
        
        for thermal_storage_capacity in thermal_storage_capacities: # by thermal storage capacity

            # Calculate number of storage tanks required
            Q_tes_des = demand * thermal_storage_capacity # MWh(th)
            T_tes_cold = T_loop_in_des + 273.15 # K
            T_tes_hot = T_loop_out + 273.15 # K
            T_tes_ave = 0.5*(T_tes_hot + T_tes_cold) # K
            rho_ave, cp_ave = rho(T_tes_ave), cp(T_tes_ave) # kg/m3, J/kgK
            available_HTF_volume = Q_tes_des*3600 / (rho_ave * cp_ave / 1000 * (T_tes_hot - T_tes_cold)) # volume required to supply design hours of thermal energy storage (m3)
            h_min, h_tank = 0.5, 15 # m
            available_HTF_volume /= 1 - h_min / h_tank # additional volume necessary due to minimum tank limits
            system_model.value('tank_pairs', ceil(available_HTF_volume / 18850))
            
            for solar_multiple in solar_multiples: # by solar multiple
            
                # execute models
                system_model.execute()
                lcoh_model.execute()
                
                print('Case ' + str(i) + ':')
                print('   Location = ' + location_name)
                print('   Demand Size = ' + str(demand) + ' MWth')
                print('   Process Temperature = ' + str(process_temp) + ' C')
                print('   Solar Multiple = ' + str(solar_multiple))
                print('   Thermal Storage Capacity = ' + str(thermal_storage_capacity) + ' hours')
                
                print('Results:')
                print('   Capacity Factor = ' + str(system_model.Outputs.capacity_factor))
                print('   LCOH = ' + str(lcoh_model.Outputs.lcoe_fcr) + ' $/kWh(th)')