Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support local+native usage #477

Merged
merged 11 commits into from
May 18, 2021
Merged

Support local+native usage #477

merged 11 commits into from
May 18, 2021

Conversation

jenhagg
Copy link
Collaborator

@jenhagg jenhagg commented May 6, 2021

Purpose

Add the remaining code changes so that the framework can be used locally, outside docker, on any os, using a local REISE.jl installation.

What the code is doing

  • Factor out launch_scenario into subclasses for each use case, and similarly for extracting output.
  • Add State.refresh method so state changes have up to date information without re-initializing the instance
  • Split up configuration into classes, making it easy to override values specific to a given environment
  • various cleanup

Testing

  • Ran a simulation end to end on windows. Ran into issues due to using julia 1.6 but upgrading PyCall and CSV packages fixed the issues.
  • Ran simulation on MacOS using julia 1.5 (there are issues with 1.6)
  • Ran simulation in plug to verify no regressions

Usage Example/Visuals

The PR for documentation is Breakthrough-Energy/docs#62. For testing locally, I changed the name of the data directory to ScenarioDataLocal (in both PowerSimData and REISE.jl) so it won't collide with the existing data from the server.

Time estimate

40 mins

Copy link
Contributor

@ahurli ahurli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the new classes for the different ways to access the engine! I'm curious as your thoughts about how this design can support multiple engines in the future. It looks like we're only supporting REISE.jl in our local implementation (as opposed to including REISE like the ssh implementation does), which is probably fine for now, but as we add new engines (e.g. switch, PowerSimulations.jl), would we add new launchers (e.g. NativeSwitchLauncher, 'NativeREISEjlLauncher`) or do you think there's a better way of doing that?


def get_deployment_mode():
mode = os.getenv("DEPLOYMENT_MODE")
if mode is None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know making the server deployment type the default is easiest for the team now, but should we think about moving to having local be the default at some point? As our user base grows, that seems more intuitive/easier-to-use to me.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it's unclear to me what the default should be in the future, since the docker roadmap and (corresponding user stories) is still being scoped out. But it's something to keep in mind.

@jenhagg
Copy link
Collaborator Author

jenhagg commented May 7, 2021

I like the new classes for the different ways to access the engine! I'm curious as your thoughts about how this design can support multiple engines in the future. It looks like we're only supporting REISE.jl in our local implementation (as opposed to including REISE like the ssh implementation does), which is probably fine for now, but as we add new engines (e.g. switch, PowerSimulations.jl), would we add new launchers (e.g. NativeSwitchLauncher, 'NativeREISEjlLauncher`) or do you think there's a better way of doing that?

Good point - I think we'll have to refactor a bit for that. Ideally if we have a standard interface to each engine, I'm hoping we can call them the same way, just with a different configuration. Maybe something like NativeLauncher(engine='switch') or equivalent. But the signatures you mentioned might also work - if we make it so that new classes like that can be defined almost entirely by inheritance, it could be just as simple to add. Thinking here of a class hierarchy like this:

class NativeLauncherBase:
class HttpLauncherBase:
class REISEjlBase:
# implementations here

and subclassing via

class NativeREISEjlLauncher(REISEjlBase, NativeLauncherBase):
    # inheritance does all the work
    pass

Not sure exactly if/how this would work, but just sharing potential design, mainly based on this post super considered super

@jenhagg jenhagg marked this pull request as ready for review May 12, 2021 19:59
@kasparm
Copy link
Contributor

kasparm commented May 14, 2021

Running into the following error when trying to run a scenario:

--> Launching simulation on local
Traceback (most recent call last):
  File "test.py", line 65, in <module>
    process_run = scenario.launch_simulation()
  File "/Users/kmueller/EGM/temp/testPSDWindows/PowerSimData/powersimdata/scenario/execute.py", line 178, in launch_simulation
    return self._launcher.launch_simulation(threads, solver, extract_data)
  File "/Users/kmueller/EGM/temp/testPSDWindows/PowerSimData/powersimdata/data_access/launcher.py", line 75, in launch_simulation
    return self._launch(threads, solver, extract_data)
  File "/Users/kmueller/EGM/temp/testPSDWindows/PowerSimData/powersimdata/data_access/launcher.py", line 196, in _launch
    return app.launch_simulation(self.scenario.scenario_id, threads, solver)
TypeError: launch_simulation() takes 1 positional argument but 3 were given

@rouille
Copy link
Collaborator

rouille commented May 14, 2021

Running into the following error when trying to run a scenario:

--> Launching simulation on local
Traceback (most recent call last):
  File "test.py", line 65, in <module>
    process_run = scenario.launch_simulation()
  File "/Users/kmueller/EGM/temp/testPSDWindows/PowerSimData/powersimdata/scenario/execute.py", line 178, in launch_simulation
    return self._launcher.launch_simulation(threads, solver, extract_data)
  File "/Users/kmueller/EGM/temp/testPSDWindows/PowerSimData/powersimdata/data_access/launcher.py", line 75, in launch_simulation
    return self._launch(threads, solver, extract_data)
  File "/Users/kmueller/EGM/temp/testPSDWindows/PowerSimData/powersimdata/data_access/launcher.py", line 196, in _launch
    return app.launch_simulation(self.scenario.scenario_id, threads, solver)
TypeError: launch_simulation() takes 1 positional argument but 3 were given

What REISE.jl did you check out?

@kasparm
Copy link
Contributor

kasparm commented May 14, 2021

Good point, not jon/windows.

@rouille
Copy link
Collaborator

rouille commented May 14, 2021

Good point, not jon/windows.

This is one of this cross-package feature we love so much!

@kasparm
Copy link
Contributor

kasparm commented May 14, 2021

Anybody go this setup to run a scenario? For me it hangs with the status "running".

@jenhagg
Copy link
Collaborator Author

jenhagg commented May 14, 2021

Anybody go this setup to run a scenario? For me it hangs with the status "running".

That's farther than I got

@rouille
Copy link
Collaborator

rouille commented May 14, 2021

Anybody go this setup to run a scenario? For me it hangs with the status "running".

What are you running?

@kasparm
Copy link
Contributor

kasparm commented May 14, 2021

@rouille
Copy link
Collaborator

rouille commented May 14, 2021

@rouille https://breakthrough-energy.github.io/docs/powersimdata/index.html#creating-a-scenario

You could try to run a Scenatio in ERCOT (see this example)

@jenhagg
Copy link
Collaborator Author

jenhagg commented May 14, 2021

Currently I'm able to run the simulation using julia once it's prepared. Narrowed down the segfault (on MacOS) to this line when we try to load julia modules within python. Not sure if I'm the only one experiencing this. Edit: it works with julia 1.5

None, which translates to gurobi
:param bool extract_data: whether the results of the simulation engine should
automatically extracted after the simulation has run. This defaults to True.
:return: (*subprocess.Popen*) or (*requests.Response*) - either the
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or (dict) ?

:raises ValueError: if invalid solver provided
"""
solvers = ("gurobi", "glpk")
if solver is not None and solver.lower() not in solvers:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also check solver is a str similarly as we did in _check_threads?

Copy link
Collaborator Author

@jenhagg jenhagg May 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not as worried about that here, since it will still fail due to not matching one of the predefined solvers or not having a .lower() method. Although, if you're referring to the error message we would get, then I could add it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was thinking about the error message.



class HttpLauncher(Launcher):
def _launch(self, threads=None, solver=None, extract_data=True):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need extract_data here, given it is not passed to request call anyway.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah each _launch method has to have the same signature, since we call it the same way (from Launcher.launch_simulation) without knowing which subclass it is. I think we might end up passing it to the request eventually though.



class NativeLauncher(Launcher):
def _launch(self, threads=None, solver=None, extract_data=True):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment for extract_data.

:param bool extract_data: whether the results of the simulation engine should
automatically extracted after the simulation has run. This defaults to True.
:param str solver: the solver used for optimization. This defaults to
None, which translates to gurobi
:return: (*subprocess.Popen*) or (*requests.Response*) - either the
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or (dict) ?


:raises NotImplementedError: if not running in container mode
:return: (*dict*) -- progress information, or None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should elaborate the returns for different launchers, SSH, HTTP, native?

Comment on lines +92 to +95
if mode == "1" or mode.lower() == "container":
return DeploymentMode.Container
if mode == "2" or mode.lower() == "local":
return DeploymentMode.Local
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we need two assignment patterns for mode: numbers and string, i.e. 1 == Container 2 == Local ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I guess one reason is to decouple the values we set for the environment variable from the internal details. Not sure of a concrete example of when we need this, but feels like it's more flexible if the numbers don't imply any particular behavior/semantics.

@rouille
Copy link
Collaborator

rouille commented May 18, 2021

What is the status of the tests?

  • Does it successfully run on Windows? (@jon-hagg and @danielolsen)
  • Does it successfully run on Mac? (@kasparm)
  • Does it successfully run with plug? (@jon-hagg )
  • Does it run with the client/server set up (@BainanXia and @danielolsen)?
    By successfully, I mean creating, running and annlyzing a scenario (full integration test)

@jenhagg
Copy link
Collaborator Author

jenhagg commented May 18, 2021

What is the status of the tests?

I've done successful tests natively on windows/mac and using plug.

Copy link
Collaborator

@rouille rouille left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it like that

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants