diff --git a/cases.csv b/cases.csv index 8c1309ba..c9f9e5ac 100644 --- a/cases.csv +++ b/cases.csv @@ -272,6 +272,9 @@ GSw_PRM_StressThresholdMetrics,/-delimited list of metrics for identifying stres GSw_PRM_StressThresholdLOLE,LOLE threshold above which to re-solve the latest model year with new stress periods; formulated as HierarchyLevel_nLOLE_StressMetric_PeriodAggMethod where HierarchyLevel is a column in hierarchy.csv; nLOLE is number of loss of load events; StressMetric is LOLE; PeriodAggMethod is 'sum' or 'max' over the hours in each period (only used in period selection) (see README.md for detailed notes),N/A,transgrp_10_LOLE_sum, GSw_PRM_StressThresholdEUE,/Annual EUE level [MWh] threshold above which to re-solve the latest model year with new stress periods; formulated as HierarchyLevel_EUE_StressMetric_PeriodAggMethod where HierarchyLevel is a column in hierarchy.csv; EUE is expected unserved energy; StressMetric EUE (only used in period selection); PeriodAggMethod is 'sum' or 'max' over the hours in each period (only used in period selection) (see README.md for detailed notes),N/A,transgrp_1000_EUE_sum, GSw_PRM_StressThresholdNEUE,/Annual NEUE level [ppm] threshold above which to re-solve the latest model year with new stress periods; formulated as HierarchyLevel_NEUEppm_StressMetric_PeriodAggMethod where HierarchyLevel is a column in hierarchy.csv; NEUEppm is normalized expected unserved energy in parts per million; StressMetric is EUE or NEUE (only used in period selection); PeriodAggMethod is 'sum' or 'max' over the hours in each period (only used in period selection) (see README.md for detailed notes),N/A,transgrp_1_NEUE_sum, +GSw_PRM_CVARAlpha,Alpha value used for CVAR/NCVAR calculations,float,0.95, +GSw_PRM_StressThresholdNCVAR,/Annual NCVAR level [ppm] threshold used only for CVAR/NCVAR target checks; formulated as HierarchyLevel_NCVARppm_NCVAR_cvar,N/A,transgrp_10_NCVAR_cvar, +GSw_PRM_StressThresholdCVAR,/Annual CVAR level [MWh] threshold used only for CVAR target checks; formulated as HierarchyLevel_CVAR_CVAR_cvar,N/A,country_50000_CVAR_cvar, GSw_PRM_UpdateFraction,Fraction to add to the PRM if a region fails RA threshold (only used if GSw_PRM_UpdateMethod = 1),float,0.02, GSw_PRM_UpdateMethod,Option to update PRM: (0) no update; (1) static update set by GSw_PRM_UpdateFraction; (2) dynamic update informed by PRAS; (3) dynamic update but only after all new stress periods have been added,0; 1; 2; 3,0, GSw_PRMTRADE_level,hierarchy level within which to allow PRM trading,r; nercr; transreg; transgrp; cendiv; st; interconnect; country; usda_region,country, diff --git a/cases_test.csv b/cases_test.csv index e97c4207..90a6773a 100644 --- a/cases_test.csv +++ b/cases_test.csv @@ -60,3 +60,4 @@ pras_scheduled_outage,,,,,,,,,,,,,,,,,,,,,,,,,,,,,0 pras_unitsize_source,,,,,,,,,,,,,,,,,,,,,,,,,,,,,r2x pras_vre_combine,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1 pras_samples,,,,,,10,10,10,,,,,,,,,,,,,,,10,10,,,,, +keep_resource_adequacy_files,1,,,,,,,,,,,,,,,,,,,,,,,,,,,, diff --git a/load_atm.sh b/load_atm.sh new file mode 100755 index 00000000..c3bb649c --- /dev/null +++ b/load_atm.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +cd /kfs2/projects/atm/Bcakire/ReEDS || exit 1 + +module load anaconda3 +conda activate /kfs2/projects/atm/Bcakire/conda_envs/reeds2_atm + +module load julia/1.12.1 +export JULIA_DEPOT_PATH=/kfs2/projects/atm/Bcakire/julia_depot + +echo "CONDA_PREFIX=$CONDA_PREFIX" +echo "Python:" +which python +python --version + +echo "Julia:" +which julia +julia --version + +echo "JULIA_DEPOT_PATH=$JULIA_DEPOT_PATH" +module load gams/51.3.0 diff --git a/reeds/hpc/srun_template.sh b/reeds/hpc/srun_template.sh index 4bebd071..cd4957cf 100644 --- a/reeds/hpc/srun_template.sh +++ b/reeds/hpc/srun_template.sh @@ -1,9 +1,9 @@ #!/bin/bash -#SBATCH --account=[your HPC allocation] +#SBATCH --account=atm #SBATCH --time=2-00:00:00 #SBATCH --nodes=1 #SBATCH --ntasks-per-node=1 -#SBATCH --mail-user=[your email address] +#SBATCH --mail-user=Burcin.CakirErdener@nlr.gov #SBATCH --mail-type=BEGIN,END,FAIL #SBATCH --mem=246000 # RAM in MB; up to 246000 for normal or 2000000 for bigmem on kestrel # add >>> #SBATCH --qos=high <<< above for quicker launch at double AU cost \ No newline at end of file diff --git a/reeds/resource_adequacy/run_pras.jl b/reeds/resource_adequacy/run_pras.jl index a1d8b598..ac694d55 100644 --- a/reeds/resource_adequacy/run_pras.jl +++ b/reeds/resource_adequacy/run_pras.jl @@ -1,5 +1,6 @@ #%% Imports import ArgParse +import CSV import DataFrames import Logging import LoggingExtras @@ -70,10 +71,20 @@ function parse_commandline() default = 0 required = false "--write_shortfall_samples" - help = "Write the sample-level shortfall" + help = "Write the sample-level shortfall (hourly, large)" arg_type = Int default = 0 required = false + "--write_shortfall_samples_totals" + help = "Write per-sample total shortfall by region (small)" + arg_type = Int + default = 0 + required = false + "--cvar_alpha" + help = "Alpha for CVaR (e.g., 0.95)" + arg_type = Float64 + default = 0.95 + required = false "--write_availability_samples" help = "Write the sample-level generator and storage availability" arg_type = Int @@ -182,7 +193,7 @@ function run_pras(pras_system_path::String, args::Dict) if args["write_energy"] == 1 resultspec["energy"] = PRAS.StorageEnergy() end - if args["write_shortfall_samples"] == 1 + if args["write_shortfall_samples"] == 1 || args["write_shortfall_samples_totals"] == 1 resultspec["short_samples"] = PRAS.ShortfallSamples() end if args["write_availability_samples"] == 1 @@ -204,9 +215,37 @@ function run_pras(pras_system_path::String, args::Dict) results = Dict{String,Any}(zip(keys(resultspec), results_tuple)) #%% Print some results for the entire modeled region to show it worked - @info "$(PRAS.LOLE(results["short"]))" - @info "$( PRAS.EUE(results["short"]))" - @info "$(PRAS.NEUE(results["short"]))" + @info "$(PRAS.LOLE(results["short"])) event-h" + @info "$(PRAS.EUE(results["short"])) MWh" + @info "NEUE = $(1e6 * PRAS.EUE(results["short"]).eue.estimate / sum(sys.regions.load)) ppm" + + # CVAR results + if haskey(results, "short_samples") + _,_,_,_,energyunit = PRAS.get_params(sys) + alpha = Float64(args["cvar_alpha"]) + cvar_obj = PRAS.CVAR(energyunit, results["short_samples"], alpha) + @info(cvar_obj) + + total_load = sum(sys.regions.load) + ncvar_value = PRAS.val(cvar_obj.cvar) / total_load * 1e6 + ncvar_stderr = PRAS.stderror(cvar_obj.cvar) / total_load * 1e6 + ncvar_var = cvar_obj.var / total_load * 1e6 + + ### Write risk metrics to CSV + base = replace(pras_system_path, ".pras"=>"") + riskfile = base * "-risk_metrics.csv" + dfrisk = DF.DataFrame( + metric = ["CVAR", "NCVAR"], + alpha = [alpha, alpha], + region = ["USA", "USA"], + unit = ["MWh", "ppm"], + value = [PRAS.val(cvar_obj.cvar), ncvar_value], + stderr = [PRAS.stderror(cvar_obj.cvar), ncvar_stderr], + var = [cvar_obj.var, ncvar_var], + ) + CSV.write(riskfile, dfrisk) + @info("Wrote risk metrics to $(riskfile)") + end ## Filter out DC regions used for VSC HVDC transmission regions = [r for r in sys.regions.names if !(occursin("|", r))] @@ -285,6 +324,7 @@ function run_pras(pras_system_path::String, args::Dict) end @info("Wrote PRAS surplus to $(surplusfile)") end + ### Storage energy if args["write_energy"] == 1 dfenergy = DF.DataFrame() @@ -302,31 +342,39 @@ function run_pras(pras_system_path::String, args::Dict) @info("Wrote PRAS storage energy to $(energyfile)") end - ### Sample-level shortfall + ### Sample-level shortfall (hourly, large) if args["write_shortfall_samples"] == 1 - dictshort = Dict(s => DF.DataFrame() for s = 1:args["samples"]) - for s in range(1, args["samples"]) - dictshort[s] = DF.DataFrame( - transpose(getindex.(results["short_samples"][:, :], s)), - sys.regions.names - ) - # subset to regions (filter out DC regions) - dictshort[s] = dictshort[s][:,findall(regions .∈ Ref(sys.regions.names))] - end - ## Write it + sf = results["short_samples"] + region_names = sf.regions.names + idx = [findfirst(==(r), region_names) for r in regions] + shortfile = replace(outfile, ".h5"=>"-shortfall_samples.h5") HDF5.h5open(shortfile, "w") do f - ## Create a group for each sample. Within each group, write an array for each region. - for s in range(1, args["samples"]) + for s in 1:args["samples"] HDF5.create_group(f, "$s") - for column in DF._names(dictshort[s]) - f["$s"]["$column", compress=4] = convert(Array, dictshort[s][!, column]) + for (r, i_r) in zip(regions, idx) + arr = Float64.(sf.shortfall[i_r, :, s]) + f["$s"]["$r", compress=4] = arr end end end @info("Wrote PRAS shortfall by sample to $(shortfile)") end + ### Per-sample total shortfall by region (small, needed for CVaR) + if args["write_shortfall_samples_totals"] == 1 + sf = results["short_samples"] + totalsfile = replace(outfile, ".h5"=>"-shortfall_totals_by_sample.h5") + HDF5.h5open(totalsfile, "w") do f + f["sample", compress=4] = collect(1:args["samples"]) + f["USA", compress=4] = Float64.(sf[]) + for r in regions + f["$r", compress=4] = Float64.(sf[r]) + end + end + @info("Wrote PRAS shortfall totals by sample to $(totalsfile)") + end + ### Sample-level generator and storage availability if args["write_availability_samples"] == 1 dictavail = Dict(s => DF.DataFrame() for s = 1:args["samples"]) @@ -457,6 +505,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ # "write_surplus" => 0, # "write_energy" => 0, # "write_shortfall_samples" => 1, + # "write_shortfall_samples_totals" => 1, # "write_availability_samples" => 0, # "overwrite" => 1, # "debug" => 0, @@ -466,6 +515,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ # "pras_existing_unit_size" => 1, # "pras_max_unitsize_prm" => 1, # "pras_seed" => 1, + # "cvar_alpha" => 0.95, # ) # reedscase = args["reedscase"] # solve_year = args["solve_year"] @@ -478,11 +528,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ #%% Include ReEDS2PRAS include(joinpath( - args["reedscase"], "reeds", "resource_adequacy", "reeds2pras", "src", "ReEDS2PRAS.jl" + args["reeds_path"], "reeds", "resource_adequacy", "reeds2pras", "src", "ReEDS2PRAS.jl" )) #%% Run it main(args) #%% -end +end \ No newline at end of file diff --git a/reeds/resource_adequacy/stress_periods.py b/reeds/resource_adequacy/stress_periods.py index e51c5f7f..538dacf3 100644 --- a/reeds/resource_adequacy/stress_periods.py +++ b/reeds/resource_adequacy/stress_periods.py @@ -16,6 +16,8 @@ # import importlib # importlib.reload(functions) +CVAR_METRICS = {'CVAR', 'NCVAR'} + #%%### Functions def get_pras_stress_metric(case, t, iteration=0, stress_metric='EUE'): @@ -204,6 +206,115 @@ def get_and_write_neue(sw, write=True): eue.to_csv(os.path.join(sw['casedir'],'outputs','eue.csv')) return neue +def get_cvar_alpha(sw): + val = sw.get('GSw_PRM_CVARAlpha', 0.95) + if pd.isna(val) or str(val).strip().lower() in ['', 'none', 'nan']: + return 0.95 + return float(val) + + +def get_shortfall_totals_by_sample(case, t, iteration=0): + filepath = os.path.join(case, 'handoff', 'PRAS', f'PRAS_{t}i{iteration}-shortfall_totals_by_sample.h5') + if not os.path.isfile(filepath): + raise FileNotFoundError(f"{filepath} not found. Re-run PRAS with --write_shortfall_samples_totals 1.") + df = reeds.io.read_pras_results(filepath) + df.columns = df.columns.astype(str) + if 'sample' in df.columns: + df = df.set_index('sample') + elif df.index.name != 'sample': + df.index = pd.RangeIndex(1, len(df) + 1, name='sample') + return df.apply(pd.to_numeric, errors='coerce').clip(lower=0) + + +def _sample_cvar(samples, alpha=0.95): + x = pd.Series(samples).dropna().astype(float) + if x.empty: + return np.nan + if not (0 <= alpha < 1): + raise ValueError(f"CVaR alpha must be in [0, 1). Got alpha={alpha}") + n_tail = max(1, int(np.ceil((1 - alpha) * len(x)))) + return x.sort_values(ascending=False).iloc[:n_tail].mean() + + +def get_annual_cvar_stress_metric(case, t, stress_metric='NCVAR', iteration=0, alpha=0.95): + stress_metric = stress_metric.upper() + if stress_metric not in CVAR_METRICS: + raise NotImplementedError(f"get_annual_cvar_stress_metric only supports {CVAR_METRICS}. Got {stress_metric}.") + + shortfall_samples = get_shortfall_totals_by_sample(case=case, t=t, iteration=iteration).drop(columns=['USA'], errors='ignore') + dfload = reeds.io.read_h5py_file(os.path.join(case, 'handoff', 'reeds_data', f'pras_load_{t}.h5')) + + levels = ['country','interconnect','nercr','transreg','transgrp','st','r'] + _metric = {} + for hierarchy_level in levels: + rmap = reeds.io.get_rmap(case=case, hierarchy_level=hierarchy_level) + regions = [c for c in shortfall_samples.columns if c in rmap.index] + if not regions: + continue + + shortfall_agg = shortfall_samples[regions].rename(columns=rmap).groupby(axis=1, level=0).sum() + cvar = shortfall_agg.apply(lambda s: _sample_cvar(s, alpha=alpha), axis=0) + + if stress_metric == 'NCVAR': + load_regions = [c for c in dfload.columns if c in rmap.index] + load_agg = dfload[load_regions].rename(columns=rmap).groupby(axis=1, level=0).sum().sum() + cvar = cvar / load_agg.reindex(cvar.index) * 1e6 + + _metric[hierarchy_level,'cvar'] = cvar + + return pd.concat(_metric, names=['level','metric','region']).rename(stress_metric) + + +def evaluate_cvar_target_check(sw, t, iteration=0, stress_metrics=None): + if stress_metrics is None: + stress_metrics = sw.GSw_PRM_StressThresholdMetrics.split('/') + + stress_metrics = [m.upper() for m in stress_metrics if str(m).strip()] + cvar_metrics = [m for m in stress_metrics if m in CVAR_METRICS] + if not cvar_metrics: + return pd.DataFrame() + + records = [] + for metric in cvar_metrics: + switch_name = f'GSw_PRM_StressThreshold{metric}' + if switch_name not in sw.index: + print(f"Warning: {metric} is listed in GSw_PRM_StressThresholdMetrics, but {switch_name} is not defined. Skipping {metric} target check.") + continue + + for criterion in str(sw[switch_name]).split('/'): + criterion = criterion.strip() + if not criterion: + continue + + hierarchy_level, stress_level, stress_metric, metric_name = criterion.split('_') + stress_metric = stress_metric.upper() + metric_name = metric_name.lower() + + if stress_metric not in CVAR_METRICS: + raise ValueError(f"Invalid CVAR criterion: {criterion}. Metric must be one of {CVAR_METRICS}.") + if metric_name != 'cvar': + raise ValueError(f"Invalid CVAR criterion: {criterion}. The fourth field must be 'cvar'.") + + stress_vals = pd.read_csv(os.path.join(sw.casedir, 'outputs', f'{stress_metric}_{t}i{iteration}.csv'), index_col=['level','metric','region']).squeeze(1) + this_test = stress_vals.xs((hierarchy_level, 'cvar'), level=['level','metric']) + threshold = float(stress_level) + + for region, value in this_test.items(): + records.append({'criterion':criterion, 'level':hierarchy_level, 'metric':stress_metric, 'region':region, 'alpha':get_cvar_alpha(sw), 'threshold':threshold, 'value':value, 'passed':value <= threshold}) + + failed = this_test.loc[this_test > threshold] + if len(failed): + print(f"GSw_PRM_StressThreshold = {criterion} failed for:") + print(failed) + print(f"{stress_metric} is check-only: no stress periods and no PRM increment will be added.") + else: + print(f"GSw_PRM_StressThreshold = {criterion} passed") + + dfcheck = pd.DataFrame(records) + if not dfcheck.empty: + dfcheck.to_csv(os.path.join(sw.casedir, 'outputs', f'cvar_target_check_{t}i{iteration}.csv'), index=False) + return dfcheck + def get_annual_stress_metric(case, t, stress_metric, iteration=0): """ """ @@ -393,6 +504,10 @@ def _evaluate_stress_threshold_criterion(stress_criteria, criterion, sw, t, iter return stress_criteria def get_stress_metrics_sorted_periods(sw, t, iteration): + stress_metric_switches = [m.upper() for m in sw.GSw_PRM_StressThresholdMetrics.split('/') if str(m).strip() and m.upper() not in CVAR_METRICS] + if not stress_metric_switches: + return {}, None, None + ### Get storage state of charge (SOC) to use in selection of "shoulder" stress periods dfenergy = reeds.io.read_pras_results( os.path.join(sw['casedir'], 'handoff', 'PRAS', f"PRAS_{t}i{iteration}-energy.h5") @@ -414,27 +529,18 @@ def get_stress_metrics_sorted_periods(sw, t, iteration): ### Check all stress criteria; for regions that fail, add new stress periods stress_criteria = {'stress_sorted_periods': {}, 'failed': {}, 'high_stress_periods': {}, 'shoulder_periods': {}} - - # Validation check: Display any GSw_PRM_StressThreshold{metric} - # that is not specified in GSw_PRM_StressThresholdMetrics - stress_metric_switches = sw.GSw_PRM_StressThresholdMetrics.split('/') - + # stress periods column names for writing outputs stress_metrics_units = {'EUE':'MWh', 'NEUE':'ppm', 'LOLE':'days'} - stress_metrics_col_names = {m:f'{m}_{stress_metrics_units[m]}' for m in - stress_metric_switches} + stress_metrics_col_names = {m:f'{m}_{stress_metrics_units[m]}' for m in stress_metric_switches} for metric in stress_metric_switches: - for criterion in sw[f'GSw_PRM_StressThreshold{metric.upper()}'].split('/'): - print(f"Evaluating GSw_PRM_StressThreshold {metric.upper()} with criterion: {criterion}") - stress_criteria = _evaluate_stress_threshold_criterion(stress_criteria, - criterion, - sw, - t, - iteration, - dfenergy_r, - stressperiods_this_iteration, - ) + for criterion in sw[f'GSw_PRM_StressThreshold{metric}'].split('/'): + print(f"Evaluating GSw_PRM_StressThreshold {metric} with criterion: {criterion}") + stress_criteria = _evaluate_stress_threshold_criterion(stress_criteria, criterion, sw, t, iteration, dfenergy_r, stressperiods_this_iteration, + ) + + stress_sorted_periods = stress_criteria['stress_sorted_periods'] stress_sorted_periods = stress_criteria['stress_sorted_periods'] failed = stress_criteria['failed'] @@ -510,27 +616,15 @@ def prm_increment_pras(sw, t, iteration, combined_periods_write, failed_regions) ) ## shortfall data - # read the net shortfall (positive) and net surplus (negative) results - # by sample from PRAS run (MWh) - filepath = os.path.join(sw['casedir'], 'handoff', 'PRAS', - f'PRAS_{sw["t"]}i{iteration}-shortfall_samples.h5') - net_short = reeds.io.read_pras_results(filepath) - # get number of samples - n_samples = len(net_short) - # collapse dict of dataframes by sample in 1 dataframe (keep index to preserve hours) - net_short = pd.concat( - (df.assign(**{"sample": k}) for k, df in net_short.items()), ignore_index=False) - # convert to long format with shortfall by sample, hour, and r - net_short.index.names=['hour'] - net_short = net_short.reset_index().set_index(['sample','hour']) - net_short = net_short.sort_index(level=['sample', 'hour'], ascending=[True, True]) - net_short = net_short.melt( - ignore_index=False, var_name='r', value_name='net_short_mwh').reset_index() - - # zero-out negative values (net surplus) for determining regional unserved energy totals - net_short['net_short_mwh'] = net_short['net_short_mwh'].clip(lower=0) - # calaculate total regional net shortfall for all hours by sample - net_short_crit = net_short.groupby(['r','sample'], as_index=False)['net_short_mwh'].sum() + net_short_totals = get_shortfall_totals_by_sample(case=sw['casedir'], t=t, iteration=iteration) + n_samples = len(net_short_totals) + net_short_crit = ( + net_short_totals + .drop(columns=['USA'], errors='ignore') + .reset_index() + .melt(id_vars='sample', var_name='r', value_name='net_short_mwh') + ) + net_short_crit['net_short_mwh'] = net_short_crit['net_short_mwh'].clip(lower=0) ## get load data dfload = reeds.io.read_file( @@ -697,13 +791,18 @@ def main(sw, t, iteration=0, logging=True): ## TODO: Check whether to keep _neue_simple or not. # _neue_simple = get_and_write_neue(sw, write=True) # Check if EUE is added, if not add it since it's needed for selection of shoulder stress periods - stress_metrics = sw.GSw_PRM_StressThresholdMetrics.split('/') + stress_metrics = [m.upper() for m in sw.GSw_PRM_StressThresholdMetrics.split('/') if str(m).strip()] for stress_metric in stress_metrics: print(f"Calculating and writing annual {stress_metric} for iteration {iteration}") - dfmetric = get_annual_stress_metric(sw.casedir, t, stress_metric, iteration=iteration) - dfmetric.round(2).to_csv( - os.path.join(sw.casedir, 'outputs', f"{stress_metric}_{t}i{iteration}.csv") - ) + if stress_metric in CVAR_METRICS: + dfmetric = get_annual_cvar_stress_metric(case=sw.casedir, t=t, stress_metric=stress_metric, iteration=iteration, alpha=get_cvar_alpha(sw)) + else: + dfmetric = get_annual_stress_metric(sw.casedir, t, stress_metric, iteration=iteration) + dfmetric.round(2).to_csv(os.path.join(sw.casedir, 'outputs', f"{stress_metric}_{t}i{iteration}.csv")) + + # CVAR / NCVAR target check is check-only. + # It does not add stress periods and does not update PRM. + evaluate_cvar_target_check(sw=sw, t=t, iteration=iteration, stress_metrics=stress_metrics) except Exception as err: if int(sw['pras']) == 2: diff --git a/runreeds.py b/runreeds.py index 5ba301fb..ea061b8a 100644 --- a/runreeds.py +++ b/runreeds.py @@ -859,7 +859,7 @@ def setupEnvironment( "To build the environment for the first time, run:\n" " `conda env create -f environment.yml`\n" "To activate the created environment, run:\n" - " `conda activate reeds2` (or `activate reeds2` on Windows)\n" + " `conda activate /kfs2/projects/atm/Bcakire/conda_envs/reeds2_atm` (or `activate reeds2` on Windows)\n" "Do you want to continue without activating the environment?" ) confirm_env = str(input("Continue? y/[n]: ") or 'n') @@ -1292,8 +1292,9 @@ def write_batch_script( OPATH.writelines("module load conda \n") OPATH.writelines("module load gams \n") - OPATH.writelines("conda activate reeds2 \n") + OPATH.writelines("conda activate /kfs2/projects/atm/Bcakire/conda_envs/reeds2_atm \n") OPATH.writelines('export R_LIBS_USER="$HOME/rlib" \n\n\n') + OPATH.writelines("export JULIA_DEPOT_PATH=/kfs2/projects/atm/Bcakire/julia_depot \n") #%% Write the input_processing script calls big_comment('Input processing', OPATH)