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

[S549767] Start Fluent from PyFluent #7

Merged
merged 9 commits into from
Jan 17, 2022
Merged

Conversation

mkundu1
Copy link
Contributor

@mkundu1 mkundu1 commented Jan 14, 2022

Add a launch_fluent method which will launch Fluent and return a Session instance. Currently launch_fluent supports only few common arguments to launch Fluent locally which are validated and converted to Fluent launch arguments using static information from launch_options.yaml file. Minor change is required in Fluent side to cleanly exit Fluent from PyFluent which has added in PR 290947.

PyFluent should cover some of the launch_mapdl arguments (#5) - will look into these separately. Some of these like start_timeout should be handled in the client side.

processor_count:
type: int
fluent_format: " -t{}"
journal_filename:
Copy link
Contributor

Choose a reason for hiding this comment

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

Note that Fluent supports multiple specifications of -i <joufile>.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@HKrishnan-ansys will add different type for journal_filename to handle it

Copy link
Collaborator

Choose a reason for hiding this comment

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

Unless you pass the -cloud option... :-) Learned that this week.

if self.__channel:
self.__channel.close()
Path(self.server_info_filepath).unlink(missing_ok=True)
Copy link
Contributor

Choose a reason for hiding this comment

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

This file can be deleted as soon as the connection is done, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right, thanks

argval = fluent_values[i]
launch_string += v['fluent_format'].replace('{}', str(argval))
server_info_filepath = get_server_info_filepath()
launch_string += f' -sifile="{server_info_filepath}"'
Copy link
Contributor

Choose a reason for hiding this comment

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

@mkundu1 Sorry to be finicky here but I am wondering if it is better to remove the file in this place in a try-finally block (you may want to put the unlink call also in a try-except block).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@HKrishnan-ansys No problem, yeah that's better.

Copy link
Contributor

@h-krishnan h-krishnan left a comment

Choose a reason for hiding this comment

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

Looks good.

Comment on lines 17 to 19
FLUENT_MAJOR_VERSION = '22'
FLUENT_MINOR_VERSION = '2'
LAUNCH_FLUENT_TIMEOUT = 100
Copy link
Contributor

Choose a reason for hiding this comment

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

These all will have to be user configurable.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, and is there a reason to split them apart into two variables? Why not just FLUENT_VERSION='22.2'

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed to FLUENT_VERSION='22.2'

Comment on lines 47 to 50
version = None,
precision = None,
processor_count = None,
journal_filename = None
Copy link
Contributor

Choose a reason for hiding this comment

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

Nitpick: there should be no whitespace between = for keyword args.

Copy link
Collaborator

@dnwillia-work dnwillia-work Jan 14, 2022

Choose a reason for hiding this comment

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

Yeah, is that part of the formatting guidelines already?


Returns
-------
An instance of ansys.fluent.session.Session.
Copy link
Contributor

Choose a reason for hiding this comment

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

Should follow numpy doc Returns style:

Suggested change
An instance of ansys.fluent.session.Session.
ansys.fluent.session.Session
Fluent session.

Copy link
Contributor Author

@mkundu1 mkundu1 Jan 17, 2022

Choose a reason for hiding this comment

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

Changed docstring to follow numpydoc style

Comment on lines 52 to 53
"""Start Fluent locally in server mode.
Parameters
Copy link
Contributor

Choose a reason for hiding this comment

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

There has to be whitespace between short description and the Parameters section.

Suggested change
"""Start Fluent locally in server mode.
Parameters
"""Start Fluent locally in server mode.
Parameters

Whether to use the '2d' or '3d' version of Fluent. Default is '3d'.

precision : str, optional
Whether to use the single-precision or double-precision version of Fluent. Default is double-precision.
Copy link
Contributor

Choose a reason for hiding this comment

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

Be sure to escape literals.

Also, consider that the PEP8 standard for docstring length is 72:

PEP8 has some maximum line length guidelines, starting with “Limit all lines to a maximum of 79 characters” but “for flowing long blocks of text with fewer structural restrictions (docstrings or comments), the line length should be limited to 72 characters.”

I'm ok if we go to 79, but any longer and it won't be rendered correctly in some "old school" editors.

Suggested change
Whether to use the single-precision or double-precision version of Fluent. Default is double-precision.
Whether to use the single-precision or double-precision version of Fluent. Default is ``"double-precision"``.

Copy link
Collaborator

@dnwillia-work dnwillia-work Jan 14, 2022

Choose a reason for hiding this comment

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

Agree we should stick to PEP8 here.

Comment on lines 88 to 89
print(f'Default value {argval} is chosen for {k} as the passed value '
f'{old_argval} is outside allowed_values {allowed_values}.')
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't print, either warn or raise. This won't show up in unit testing and users could potentially be passing invalid args.

if argval is None and v.get('required', None) == True:
argval = default
if argval is not None:
allowed_values = v.get('allowed_values', None)
Copy link
Contributor

Choose a reason for hiding this comment

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

Note that None is redundant for get. If a key does not exist, None is returned by default

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, that would clean it up.

for k, v in all_options.items():
argval = argvals.get(k, None)
default = v.get('default', None)
if argval is None and v.get('required', None) == True:
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm sure you know, but: https://stackoverflow.com/questions/27276610/boolean-identity-true-vs-is-true

Suggested change
if argval is None and v.get('required', None) == True:
if argval is None and v.get('required') is True:

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks

launch_string += f' -sifile="{server_info_filepath}"'
if not os.getenv('PYFLUENT_SHOW_SERVER_GUI'):
launch_string += ' -hidden'
print(f'Launching Fluent with cmd: {launch_string}')
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be using logging here, avoid printing always.

https://dev.docs.pyansys.com/guidelines/logging.html

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've added a basic logger class to log to console - will expand that to include log files, etc. in future.

while True:
if Path(server_info_filepath).stat().st_mtime > sifile_last_mtime:
time.sleep(1)
print('\nFluent process is successfully launched.')
Copy link
Contributor

Choose a reason for hiding this comment

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

See logging note.

def parse_server_info_file(filename: str):
with open(filename, "rb") as f:
lines = f.readlines()
return (lines[0].strip(), lines[1].strip())
Copy link
Contributor

Choose a reason for hiding this comment

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

Unnecessary tuple:

Suggested change
return (lines[0].strip(), lines[1].strip())
return lines[0].strip(), lines[1].strip()

self.__channel = grpc.insecure_channel(address)
datamodel_stub = DataModelGrpcModule.DataModelStub(self.__channel)
self.service = DatamodelService(datamodel_stub, password)
self.tui = Session.tui(self.service)

def stop(self):
def close(self):
Copy link
Contributor

Choose a reason for hiding this comment

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

Provide documentation here please.

README.rst Outdated
@@ -40,5 +36,5 @@ In Python (client-side):
session.tui.display.objects.contour['contour-1'].color_map.size()
session.tui.display.objects.contour['contour-1'].rename('my-contour')
del session.tui.display.objects.contour['my-contour']
session.stop()
session.close()
Copy link
Contributor

Choose a reason for hiding this comment

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

Curious, and it's up to you, but should we have close or exit. Close implies you're closing the connection, while exit implies you're shutting down the fluent instance. I can't tell (as a user/onlooker) what close is doing since there's no docstring.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think it means close the connection right? i.e. I can connect again later?

Copy link
Contributor Author

@mkundu1 mkundu1 Jan 17, 2022

Choose a reason for hiding this comment

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

I've changed this to session.exit. session.exit closes the connection and exit Fluent. At present we cannot re-use the session object - will check if we should do it. Perhaps, we shall recommend users to use session within a with context.

print(f'Launching Fluent with cmd: {launch_string}')
sifile_last_mtime = Path(server_info_filepath).stat().st_mtime
kwargs = get_subprocess_kwargs_for_detached_process()
subprocess.Popen(launch_string, **kwargs)
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider piping stdout/stderr to a listener thread that writes to a logger. That way users can determine why fluent fails to launch. I've tried a similar implementation for MAPDL, but there's an issue with stdout/stderr piping otherwise we would have implemented it; I hope you don't encounter the same issue here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, I'll check this.

print('\nFluent process is successfully launched.')
break
if counter == 0:
print('\nThe launch process has been timed out.')
Copy link
Contributor

Choose a reason for hiding this comment

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

Warn or raise.

break
time.sleep(1)
counter -= 1
print(f'Waiting for Fluent to launch...{counter:2} seconds remaining', end='\r')
Copy link
Contributor

Choose a reason for hiding this comment

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

Log this.

README.rst Outdated
@@ -40,5 +36,5 @@ In Python (client-side):
session.tui.display.objects.contour['contour-1'].color_map.size()
session.tui.display.objects.contour['contour-1'].rename('my-contour')
del session.tui.display.objects.contour['my-contour']
session.stop()
session.close()
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think it means close the connection right? i.e. I can connect again later?

Copy link
Collaborator

@dnwillia-work dnwillia-work left a comment

Choose a reason for hiding this comment

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

Mainak, please incorporate the suggestions from Alex.

return str(exe_path)

def get_server_info_filepath():
server_info_dir = os.getenv('SERVER_INFO_DIR')
Copy link
Collaborator

@dnwillia-work dnwillia-work Jan 14, 2022

Choose a reason for hiding this comment

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

What sets up this environment variable? I guess we should also add keyword options to pass the IP/hostname and port to the lauch_fluent function?

Copy link
Contributor Author

@mkundu1 mkundu1 Jan 17, 2022

Choose a reason for hiding this comment

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

Right, we'll need to change launcher.py to pass the host/port info for docker launch. Current it assumes Fluent is launched from a local machine. SERVER_INFO_DIR is documented feature in Fluent to specify the location of the server-info file.

Copy link
Contributor

Choose a reason for hiding this comment

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

@mkundu1 Could we use temp folder instead of Path.cwd() if SERVER_INFO_DIR is not specified?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, we can use the temp folder, will change it in the next PR.

@mkundu1
Copy link
Contributor Author

mkundu1 commented Jan 17, 2022

Thanks @akaszynski and @dnwillia-ansys for your valuable suggestions. I've incorporated most of them. There are a couple remaining, will look into those in subsequent PRs.

journal_filename : str, optional
Read the specified journal file.

start_timeout : float, optional
Copy link
Contributor

Choose a reason for hiding this comment

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

@mkundu1 Is this float or int?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will fix in next PR

@mkundu1 mkundu1 merged commit d740850 into main Jan 17, 2022
@mkundu1 mkundu1 deleted the mkundu/s549767/start-fluent branch March 4, 2022 04:41
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.

5 participants