def main(): args = _parse_args() with_xhatshuffle = args.with_xhatshuffle with_lagrangian = args.with_lagrangian # This is multi-stage, so we need to supply node names hydro = PySPModel("./PySP/models/", "./PySP/nodedata/") rho_setter = None # Things needed for vanilla cylinders beans = (args, hydro.scenario_creator, hydro.scenario_denouement, hydro.all_scenario_names) # Vanilla PH hub hub_dict = vanilla.ph_hub(*beans, ph_extensions=None, rho_setter=rho_setter, all_nodenames=hydro.all_nodenames) # Standard Lagrangian bound spoke if with_lagrangian: lagrangian_spoke = vanilla.lagrangian_spoke( *beans, rho_setter=rho_setter, all_nodenames=hydro.all_nodenames) if with_xhatshuffle: xhatshuffle_spoke = vanilla.xhatshuffle_spoke(*beans, hydro.all_nodenames) list_of_spoke_dict = list() if with_lagrangian: list_of_spoke_dict.append(lagrangian_spoke) if with_xhatshuffle: list_of_spoke_dict.append(xhatshuffle_spoke) wheel = WheelSpinner(hub_dict, list_of_spoke_dict) wheel.spin() if wheel.global_rank == 0: # we are the reporting hub rank print( f"BestInnerBound={wheel.BestInnerBound} and BestOuterBound={wheel.BestOuterBound}" ) if write_solution: wheel.write_first_stage_solution('hydro_first_stage.csv') wheel.write_tree_solution('hydro_full_solution') hydro.close()
def main(): args = _parse_args() num_scen = args.num_scens with_fwph = args.with_fwph with_xhatlooper = args.with_xhatlooper with_xhatshuffle = args.with_xhatshuffle with_lagrangian = args.with_lagrangian with_fixer = args.with_fixer fixer_tol = args.fixer_tol with_cross_scenario_cuts = args.with_cross_scenario_cuts scensavail = [3, 5, 10, 25, 50, 100] if num_scen not in scensavail: raise RuntimeError("num-scen was {}, but must be in {}".\ format(num_scen, scensavail)) scenario_creator_kwargs = { "scenario_count": num_scen, "path": str(num_scen) + "scenarios_r1", } scenario_creator = uc.scenario_creator scenario_denouement = uc.scenario_denouement all_scenario_names = [f"Scenario{i+1}" for i in range(num_scen)] rho_setter = uc._rho_setter # Things needed for vanilla cylinders beans = (args, scenario_creator, scenario_denouement, all_scenario_names) ### start ph spoke ### if args.run_aph: hub_dict = vanilla.aph_hub( *beans, scenario_creator_kwargs=scenario_creator_kwargs, ph_extensions=MultiExtension, rho_setter=rho_setter) else: hub_dict = vanilla.ph_hub( *beans, scenario_creator_kwargs=scenario_creator_kwargs, ph_extensions=MultiExtension, rho_setter=rho_setter) # Extend and/or correct the vanilla dictionary ext_classes = [Gapper] if with_fixer: ext_classes.append(Fixer) if with_cross_scenario_cuts: ext_classes.append(CrossScenarioExtension) if args.xhat_closest_tree: ext_classes.append(XhatClosest) hub_dict["opt_kwargs"]["extension_kwargs"] = {"ext_classes": ext_classes} if with_cross_scenario_cuts: hub_dict["opt_kwargs"]["options"]["cross_scen_options"]\ = {"check_bound_improve_iterations" : args.cross_scenario_iter_cnt} if with_fixer: hub_dict["opt_kwargs"]["options"]["fixeroptions"] = { "verbose": args.with_verbose, "boundtol": fixer_tol, "id_fix_list_fct": uc.id_fix_list_fct, } if args.xhat_closest_tree: hub_dict["opt_kwargs"]["options"]["xhat_closest_options"] = { "xhat_solver_options": dict(), "keep_solution": True } if args.ph_mipgaps_json is not None: with open(args.ph_mipgaps_json) as fin: din = json.load(fin) mipgapdict = {int(i): din[i] for i in din} else: mipgapdict = None hub_dict["opt_kwargs"]["options"]["gapperoptions"] = { "verbose": args.with_verbose, "mipgapdict": mipgapdict } if args.default_rho is None: # since we are using a rho_setter anyway hub_dict.opt_kwargs.options["defaultPHrho"] = 1 ### end ph spoke ### # FWPH spoke if with_fwph: fw_spoke = vanilla.fwph_spoke( *beans, scenario_creator_kwargs=scenario_creator_kwargs) # Standard Lagrangian bound spoke if with_lagrangian: lagrangian_spoke = vanilla.lagrangian_spoke( *beans, scenario_creator_kwargs=scenario_creator_kwargs, rho_setter=rho_setter) # xhat looper bound spoke if with_xhatlooper: xhatlooper_spoke = vanilla.xhatlooper_spoke( *beans, scenario_creator_kwargs=scenario_creator_kwargs) # xhat shuffle bound spoke if with_xhatshuffle: xhatshuffle_spoke = vanilla.xhatshuffle_spoke( *beans, scenario_creator_kwargs=scenario_creator_kwargs) # cross scenario cut spoke if with_cross_scenario_cuts: cross_scenario_cuts_spoke = vanilla.cross_scenario_cuts_spoke( *beans, scenario_creator_kwargs=scenario_creator_kwargs) list_of_spoke_dict = list() if with_fwph: list_of_spoke_dict.append(fw_spoke) if with_lagrangian: list_of_spoke_dict.append(lagrangian_spoke) if with_xhatlooper: list_of_spoke_dict.append(xhatlooper_spoke) if with_xhatshuffle: list_of_spoke_dict.append(xhatshuffle_spoke) if with_cross_scenario_cuts: list_of_spoke_dict.append(cross_scenario_cuts_spoke) wheel = WheelSpinner(hub_dict, list_of_spoke_dict) wheel.spin() if args.solution_dir is not None: wheel.write_tree_solution(args.solution_dir, uc.scenario_tree_solution_writer) wheel.write_first_stage_solution( 'uc_cyl_nonants.npy', first_stage_solution_writer=sputils.first_stage_nonant_npy_serializer)
def main(): args = _parse_args() BFs = args.branching_factors xhat_scenario_dict = make_node_scenario_dict_balanced(BFs) all_nodenames = list(xhat_scenario_dict.keys()) with_xhatspecific = args.with_xhatspecific with_lagrangian = args.with_lagrangian with_xhatshuffle = args.with_xhatshuffle # This is multi-stage, so we need to supply node names #all_nodenames = ["ROOT"] # all trees must have this node # The rest is a naming convention invented for this problem. # Note that mpisppy does not have nodes at the leaves, # and node names must end in a serial number. ScenCount = np.prod(BFs) #ScenCount = _get_num_leaves(BFs) sc_options = {"args": args} scenario_creator_kwargs = aircond.kw_creator(sc_options) all_scenario_names = [f"scen{i}" for i in range(ScenCount)] #Scens are 0-based # print(all_scenario_names) scenario_creator = aircond.scenario_creator scenario_denouement = aircond.scenario_denouement primal_rho_setter = aircond.primal_rho_setter dual_rho_setter = aircond.dual_rho_setter # Things needed for vanilla cylinders beans = (args, scenario_creator, scenario_denouement, all_scenario_names) # Vanilla PH hub hub_dict = vanilla.ph_hub(*beans, scenario_creator_kwargs=scenario_creator_kwargs, ph_extensions=None, rho_setter=primal_rho_setter, all_nodenames=all_nodenames) # Standard Lagrangian bound spoke if with_lagrangian: lagrangian_spoke = vanilla.lagrangian_spoke( *beans, scenario_creator_kwargs=scenario_creator_kwargs, rho_setter=primal_rho_setter, all_nodenames=all_nodenames) # xhat specific bound spoke if with_xhatspecific: xhatspecific_spoke = vanilla.xhatspecific_spoke( *beans, xhat_scenario_dict, all_nodenames=all_nodenames, scenario_creator_kwargs=scenario_creator_kwargs) #xhat shuffle looper bound spoke if with_xhatshuffle: xhatshuffle_spoke = vanilla.xhatshuffle_spoke( *beans, all_nodenames=all_nodenames, scenario_creator_kwargs=scenario_creator_kwargs) list_of_spoke_dict = list() if with_lagrangian: list_of_spoke_dict.append(lagrangian_spoke) if with_xhatspecific: list_of_spoke_dict.append(xhatspecific_spoke) if with_xhatshuffle: list_of_spoke_dict.append(xhatshuffle_spoke) wheel = WheelSpinner(hub_dict, list_of_spoke_dict) wheel.spin() fname = 'aircond_cyl_nonants.npy' if wheel.global_rank == 0: print("BestInnerBound={} and BestOuterBound={}".\ format(wheel.BestInnerBound, wheel.BestOuterBound)) if write_solution: print(f"Writing first stage solution to {fname}") # all ranks need to participate because only the winner will write if write_solution: wheel.write_first_stage_solution('aircond_first_stage.csv') wheel.write_tree_solution('aircond_full_solution') wheel.write_first_stage_solution( fname, first_stage_solution_writer=first_stage_nonant_npy_serializer)
def main(): args = _parse_args() inst = args.instance_name num_scen = int(inst.split("-")[-3]) if args.num_scens is not None and args.num_scens != num_scen: raise RuntimeError("Argument num-scens={} does not match the number " "implied by instance name={} " "\n(--num-scens is not needed for netdes)") with_fwph = args.with_fwph with_xhatlooper = args.with_xhatlooper with_xhatshuffle = args.with_xhatshuffle with_lagrangian = args.with_lagrangian with_slamup = args.with_slamup with_cross_scenario_cuts = args.with_cross_scenario_cuts if args.default_rho is None: raise RuntimeError("The --default-rho option must be specified") path = f"{netdes.__file__[:-10]}/data/{inst}.dat" scenario_creator = netdes.scenario_creator scenario_denouement = netdes.scenario_denouement all_scenario_names = [f"Scen{i}" for i in range(num_scen)] scenario_creator_kwargs = {"path": path} # Things needed for vanilla cylinders beans = (args, scenario_creator, scenario_denouement, all_scenario_names) if with_cross_scenario_cuts: ph_ext = CrossScenarioExtension else: ph_ext = None # Vanilla PH hub hub_dict = vanilla.ph_hub(*beans, scenario_creator_kwargs=scenario_creator_kwargs, ph_extensions=ph_ext, rho_setter=None) if with_cross_scenario_cuts: hub_dict["opt_kwargs"]["options"]["cross_scen_options"]\ = {"check_bound_improve_iterations" : args.cross_scenario_iter_cnt} # FWPH spoke if with_fwph: fw_spoke = vanilla.fwph_spoke( *beans, scenario_creator_kwargs=scenario_creator_kwargs) # Standard Lagrangian bound spoke if with_lagrangian: lagrangian_spoke = vanilla.lagrangian_spoke( *beans, scenario_creator_kwargs=scenario_creator_kwargs, rho_setter=None) # xhat looper bound spoke if with_xhatlooper: xhatlooper_spoke = vanilla.xhatlooper_spoke( *beans, scenario_creator_kwargs=scenario_creator_kwargs) # xhat shuffle bound spoke if with_xhatshuffle: xhatshuffle_spoke = vanilla.xhatshuffle_spoke( *beans, scenario_creator_kwargs=scenario_creator_kwargs) # slam up bound spoke if with_slamup: slamup_spoke = vanilla.slamup_spoke( *beans, scenario_creator_kwargs=scenario_creator_kwargs) # cross scenario cuts spoke if with_cross_scenario_cuts: cross_scenario_cuts_spoke = vanilla.cross_scenario_cuts_spoke( *beans, scenario_creator_kwargs=scenario_creator_kwargs) list_of_spoke_dict = list() if with_fwph: list_of_spoke_dict.append(fw_spoke) if with_lagrangian: list_of_spoke_dict.append(lagrangian_spoke) if with_xhatlooper: list_of_spoke_dict.append(xhatlooper_spoke) if with_xhatshuffle: list_of_spoke_dict.append(xhatshuffle_spoke) if with_slamup: list_of_spoke_dict.append(slamup_spoke) if with_cross_scenario_cuts: list_of_spoke_dict.append(cross_scenario_cuts_spoke) wheel = WheelSpinner(hub_dict, list_of_spoke_dict) wheel.spin() if write_solution: wheel.write_first_stage_solution('netdes_build.csv') wheel.write_tree_solution('netdes_full_solution')
def main(): args = _parse_args() num_scen = args.num_scens crops_multiplier = args.crops_mult rho_setter = farmer._rho_setter if hasattr(farmer, '_rho_setter') else None if args.default_rho is None and rho_setter is None: raise RuntimeError( "No rho_setter so a default must be specified via --default-rho") if args.use_norm_rho_converger: if not args.use_norm_rho_updater: raise RuntimeError( "--use-norm-rho-converger requires --use-norm-rho-updater") else: ph_converger = NormRhoConverger else: ph_converger = None scenario_creator = farmer.scenario_creator scenario_denouement = farmer.scenario_denouement all_scenario_names = ['scen{}'.format(sn) for sn in range(num_scen)] scenario_creator_kwargs = { 'use_integer': False, "crops_multiplier": crops_multiplier, } scenario_names = [f"Scenario{i+1}" for i in range(num_scen)] # Things needed for vanilla cylinders beans = (args, scenario_creator, scenario_denouement, all_scenario_names) if args.run_async: # Vanilla APH hub hub_dict = vanilla.aph_hub( *beans, scenario_creator_kwargs=scenario_creator_kwargs, ph_extensions=None, rho_setter=rho_setter) else: # Vanilla PH hub hub_dict = vanilla.ph_hub( *beans, scenario_creator_kwargs=scenario_creator_kwargs, ph_extensions=None, ph_converger=ph_converger, rho_setter=rho_setter) ## hack in adaptive rho if args.use_norm_rho_updater: hub_dict['opt_kwargs']['extensions'] = NormRhoUpdater hub_dict['opt_kwargs']['options']['norm_rho_options'] = { 'verbose': True } # FWPH spoke if args.with_fwph: fw_spoke = vanilla.fwph_spoke( *beans, scenario_creator_kwargs=scenario_creator_kwargs) # Standard Lagrangian bound spoke if args.with_lagrangian: lagrangian_spoke = vanilla.lagrangian_spoke( *beans, scenario_creator_kwargs=scenario_creator_kwargs, rho_setter=rho_setter) # xhat looper bound spoke if args.with_xhatlooper: xhatlooper_spoke = vanilla.xhatlooper_spoke( *beans, scenario_creator_kwargs=scenario_creator_kwargs) # xhat shuffle bound spoke if args.with_xhatshuffle: xhatshuffle_spoke = vanilla.xhatshuffle_spoke( *beans, scenario_creator_kwargs=scenario_creator_kwargs) list_of_spoke_dict = list() if args.with_fwph: list_of_spoke_dict.append(fw_spoke) if args.with_lagrangian: list_of_spoke_dict.append(lagrangian_spoke) if args.with_xhatlooper: list_of_spoke_dict.append(xhatlooper_spoke) if args.with_xhatshuffle: list_of_spoke_dict.append(xhatshuffle_spoke) wheel = WheelSpinner(hub_dict, list_of_spoke_dict) wheel.spin() if write_solution: wheel.write_first_stage_solution('farmer_plant.csv') wheel.write_first_stage_solution('farmer_cyl_nonants.npy', first_stage_solution_writer=sputils. first_stage_nonant_npy_serializer) wheel.write_tree_solution('farmer_full_solution')
'scenario_creator': scenario_creator, 'scenario_denouement': scenario_denouement, "scenario_creator_kwargs": scenario_creator_kwargs }, } lagrangian_spoke = { "spoke_class": LagrangianOuterBound, "spoke_kwargs": dict(), "opt_class": PHBase, 'opt_kwargs': { 'options': hub_ph_options, 'all_scenario_names': all_scenario_names, 'scenario_creator': scenario_creator, "scenario_creator_kwargs": scenario_creator_kwargs, 'scenario_denouement': scenario_denouement, }, } list_of_spoke_dict = (ub_spoke, cut_spoke, ) #lagrangian_spoke) #list_of_spoke_dict = (ub_spoke, ) wheel = WheelSpinner(hub_dict, list_of_spoke_dict) wheel.spin() if wheel.global_rank == 0: # we are the reporting hub rank print(f"BestInnerBound={wheel.BestInnerBound} and BestOuterBound={wheel.BestOuterBound}") print("End time={} for global rank={}". \ format(datetime.datetime.now(), global_rank))
def main(): start_time = dt.datetime.now() # start options casename = "pglib-opf-master/pglib_opf_case14_ieee.m" # pglib_opf_case3_lmbd.m # pglib_opf_case118_ieee.m # pglib_opf_case300_ieee.m # pglib_opf_case2383wp_k.m # pglib_opf_case2000_tamu.m # do not use pglib_opf_case89_pegase egret_path_to_data = "/thirdparty/"+casename number_of_stages = 3 stage_duration_minutes = [5, 15, 30] if len(sys.argv) != number_of_stages + 3: print ("Usage: python ccop_multistage bf1 bf2 iters scenperbun(!) solver") exit(1) branching_factors = [int(sys.argv[1]), int(sys.argv[2])] PHIterLimit = int(sys.argv[3]) scenperbun = int(sys.argv[4]) solvername = sys.argv[5] seed = 1134 a_line_fails_prob = 0.1 repair_fct = FixFast verbose = False # end options # create an arbitrary xhat for xhatspecific to use xhat_dict = {"ROOT": "Scenario_1"} for i in range(branching_factors[0]): xhat_dict["ROOT_"+str(i)] = "Scenario_"+str(1 + i*branching_factors[1]) scenario_creator_kwargs = { "convex_relaxation": True, "epath": egret_path_to_data, } if scenario_creator_kwargs["convex_relaxation"]: # for initialization solve solver = pyo.SolverFactory(solvername) scenario_creator_kwargs["solver"] = None ##if "gurobi" in solvername: ##solver.options["BarHomogeneous"] = 1 else: solver = pyo.SolverFactory(solvername) if "gurobi" in solvername: solver.options["BarHomogeneous"] = 1 scenario_creator_kwargs["solver"] = solver md_dict = _md_dict(scenario_creator_kwargs["epath"]) if verbose: print("start data dump") print(list(md_dict.elements("generator"))) for this_branch in md_dict.elements("branch"): print("TYPE=",type(this_branch)) print("B=",this_branch) print("IN SERVICE=",this_branch[1]["in_service"]) print("GENERATOR SET=",md_dict.attributes("generator")) print("end data dump") lines = list() for this_branch in md_dict.elements("branch"): lines.append(this_branch[0]) acstream = np.random.RandomState() scenario_creator_kwargs["etree"] = etree.ACTree(number_of_stages, branching_factors, seed, acstream, a_line_fails_prob, stage_duration_minutes, repair_fct, lines) scenario_creator_kwargs["acstream"] = acstream all_scenario_names=["Scenario_"+str(i)\ for i in range(1,len(scenario_creator_kwargs["etree"].\ rootnode.ScenarioList)+1)] all_nodenames = scenario_creator_kwargs["etree"].All_Nodenames() options = dict() if scenario_creator_kwargs["convex_relaxation"]: options["solvername"] = solvername if "gurobi" in options["solvername"]: options["iter0_solver_options"] = {"BarHomogeneous": 1} options["iterk_solver_options"] = {"BarHomogeneous": 1} else: options["iter0_solver_options"] = None options["iterk_solver_options"] = None else: options["solvername"] = solvername # needs to be ipopt options["iter0_solver_options"] = None options["iterk_solver_options"] = None options["PHIterLimit"] = PHIterLimit options["defaultPHrho"] = 1 options["convthresh"] = 0.001 options["subsolvedirectives"] = None options["verbose"] = False options["display_timing"] = False options["display_progress"] = True options["iter0_solver_options"] = None options["iterk_solver_options"] = None options["branching_factors"] = branching_factors # try to do something interesting for bundles per rank if scenperbun > 0: nscen = branching_factors[0] * branching_factors[1] options["bundles_per_rank"] = int((nscen / n_proc) / scenperbun) if global_rank == 0: appfile = "acopf.app" if not os.path.isfile(appfile): with open(appfile, "w") as f: f.write("datetime, hostname, BF1, BF2, seed, solver, n_proc") f.write(", bunperank, PHIterLimit, convthresh") f.write(", PH_IB, PH_OB") f.write(", PH_lastiter, PH_wallclock, aph_frac_needed") f.write(", APH_IB, APH_OB") f.write(", APH_lastiter, APH_wallclock") if "bundles_per_rank" in options: nbunstr = str(options["bundles_per_rank"]) else: nbunstr = "0" oline = "\n"+ str(start_time)+","+socket.gethostname() oline += ","+str(branching_factors[0])+","+str(branching_factors[1]) oline += ", "+str(seed) + ", "+str(options["solvername"]) oline += ", "+str(n_proc) + ", "+ nbunstr oline += ", "+str(options["PHIterLimit"]) oline += ", "+str(options["convthresh"]) with open(appfile, "a") as f: f.write(oline) print(oline) # PH hub options["tee-rank0-solves"] = True hub_dict = { "hub_class": PHHub, "hub_kwargs": {"options": None}, "opt_class": PH, "opt_kwargs": { "options": options, "all_scenario_names": all_scenario_names, "scenario_creator": pysp2_callback, 'scenario_denouement': scenario_denouement, "scenario_creator_kwargs": scenario_creator_kwargs, "rho_setter": rho_setter.ph_rhosetter_callback, "extensions": None, "all_nodenames":all_nodenames, } } xhat_options = options.copy() xhat_options['bundles_per_rank'] = 0 # no bundles for xhat xhat_options["xhat_specific_options"] = {"xhat_solver_options": options["iterk_solver_options"], "xhat_scenario_dict": xhat_dict, "csvname": "specific.csv"} ub2 = { 'spoke_class': XhatSpecificInnerBound, "spoke_kwargs": dict(), "opt_class": Xhat_Eval, 'opt_kwargs': { 'options': xhat_options, 'all_scenario_names': all_scenario_names, 'scenario_creator': pysp2_callback, 'scenario_denouement': scenario_denouement, "scenario_creator_kwargs": scenario_creator_kwargs, 'all_nodenames': all_nodenames }, } list_of_spoke_dict = [ub2] wheel_spinner = WheelSpinner(hub_dict, list_of_spoke_dict) wheel_spinner.spin() spcomm = wheel_spinner.spcomm if wheel_spinner.global_rank == 0: ph_end_time = dt.datetime.now() IB = wheel_spinner.BestInnerBound OB = wheel_spinner.BestOuterBound print("BestInnerBound={} and BestOuterBound={}".\ format(IB, OB)) with open(appfile, "a") as f: f.write(", "+str(IB)+", "+str(OB)+", "+str(spcomm.opt._PHIter)) f.write(", "+str((ph_end_time - start_time).total_seconds()))
def main(): args = _parse_args() BFs = args.branching_factors if len(BFs) != 2: raise RuntimeError("Hydro is a three stage problem, so it needs 2 BFs") with_xhatshuffle = args.with_xhatshuffle with_lagrangian = args.with_lagrangian # This is multi-stage, so we need to supply node names all_nodenames = sputils.create_nodenames_from_branching_factors(BFs) ScenCount = BFs[0] * BFs[1] scenario_creator_kwargs = {"branching_factors": BFs} all_scenario_names = [f"Scen{i+1}" for i in range(ScenCount)] scenario_creator = hydro.scenario_creator scenario_denouement = hydro.scenario_denouement rho_setter = None # Things needed for vanilla cylinders beans = (args, scenario_creator, scenario_denouement, all_scenario_names) # Vanilla PH hub hub_dict = vanilla.ph_hub(*beans, scenario_creator_kwargs=scenario_creator_kwargs, ph_extensions=None, rho_setter=rho_setter, all_nodenames=all_nodenames, spoke_sleep_time=SPOKE_SLEEP_TIME) # Standard Lagrangian bound spoke if with_lagrangian: lagrangian_spoke = vanilla.lagrangian_spoke( *beans, scenario_creator_kwargs=scenario_creator_kwargs, rho_setter=rho_setter, all_nodenames=all_nodenames, spoke_sleep_time=SPOKE_SLEEP_TIME) # xhat looper bound spoke if with_xhatshuffle: xhatshuffle_spoke = vanilla.xhatshuffle_spoke( *beans, all_nodenames=all_nodenames, scenario_creator_kwargs=scenario_creator_kwargs, spoke_sleep_time=SPOKE_SLEEP_TIME) list_of_spoke_dict = list() if with_lagrangian: list_of_spoke_dict.append(lagrangian_spoke) if with_xhatshuffle: list_of_spoke_dict.append(xhatshuffle_spoke) wheel = WheelSpinner(hub_dict, list_of_spoke_dict) wheel.spin() if wheel.global_rank == 0: # we are the reporting hub rank print( f"BestInnerBound={wheel.BestInnerBound} and BestOuterBound={wheel.BestOuterBound}" ) if write_solution: wheel.write_first_stage_solution('hydro_first_stage.csv') wheel.write_tree_solution('hydro_full_solution')