Skip to content

Commit 0c5ce29

Browse files
committed
More refactors
1 parent 3b58495 commit 0c5ce29

File tree

4 files changed

+302
-213
lines changed

4 files changed

+302
-213
lines changed

RATapi/examples/normal_reflectivity/DSPC_custom_layers.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import numpy as np
66

77
import RATapi as RAT
8+
import RATapi.wrappers
89

910

1011
def DSPC_custom_layers():
@@ -126,5 +127,10 @@ def DSPC_custom_layers():
126127

127128

128129
if __name__ == "__main__":
130+
import RATapi
131+
RATapi.wrappers.get_matlab_engine().editFile(r"C:\Users\steve\Documents\Development\python-RAT\RATapi\examples\normal_reflectivity\customBilayerDSPC.m")
132+
import time
133+
time.sleep(20)
129134
problem, results = DSPC_custom_layers()
135+
130136
RAT.plotting.plot_ref_sld(problem, results, True)

RATapi/wrappers.py

Lines changed: 81 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,32 @@
11
"""Wrappers for the interface between RATapi and MATLAB custom files."""
2-
2+
import atexit
33
import os
44
import pathlib
55
import platform
66
import shutil
77
import subprocess
8-
from contextlib import contextmanager
9-
from typing import Callable
8+
from contextlib import contextmanager, suppress
9+
from typing import Callable, Tuple
1010

1111
from numpy.typing import ArrayLike
1212

1313
import RATapi.rat_core
1414

1515
MATLAB_PATH_FILE = os.path.join(os.path.dirname(os.path.realpath(__file__)), "matlab.txt")
16+
__MATLAB_ENGINE = None
17+
18+
19+
def get_matlab_engine():
20+
return __MATLAB_ENGINE
1621

1722

1823
@contextmanager
19-
def cd(new_dir):
24+
def cd(new_dir: str):
2025
"""Context manager to change to a given directory and return to current directory on exit.
2126
2227
Parameters
2328
----------
24-
new_dir : string
29+
new_dir : str
2530
The path to change to
2631
"""
2732
prev_dir = os.getcwd()
@@ -32,38 +37,17 @@ def cd(new_dir):
3237
os.chdir(prev_dir)
3338

3439

35-
def set_matlab_path(matlab_exe_path):
36-
"""Set the path of MATLAB to use for custom functions.
37-
38-
This will also register the MATLAB COM server on Windows OS which could be slow.
39-
40-
Parameters
41-
----------
42-
matlab_path : string
43-
The full path of the MATLAB executable
44-
"""
45-
if not matlab_exe_path:
46-
return
47-
48-
if platform.system() == "Windows":
49-
process = subprocess.Popen(f'"{matlab_exe_path}" -batch "comserver(\'register\')"')
50-
process.wait()
51-
52-
with open(MATLAB_PATH_FILE, "w") as path_file:
53-
path_file.write(matlab_exe_path)
54-
55-
56-
def get_matlab_paths(matlab_exe_path):
40+
def get_matlab_paths(matlab_exe_path: str) -> Tuple[str, str]:
5741
"""Return paths for loading MATLAB engine C interface dynamic libraries.
5842
5943
Parameters
6044
----------
61-
matlab_exe_path : string
45+
matlab_exe_path : str
6246
The full path of the MATLAB executable
6347
6448
Returns
6549
-------
66-
paths : Tuple[string, string]
50+
paths : Tuple[str, str]
6751
The path of the MATLAB bin and the DLL location
6852
"""
6953
if not matlab_exe_path:
@@ -87,64 +71,82 @@ def get_matlab_paths(matlab_exe_path):
8771
return f"{bin_path.as_posix()}/", f"{dll_path.as_posix()}/"
8872

8973

90-
def start_matlab():
74+
def start_matlab(matlab_exe_path: str =""):
9175
"""Load MATLAB engine dynamic libraries and creates wrapper object.
92-
76+
77+
Parameters
78+
----------
79+
matlab_exe_path : str, default ""
80+
The full path of the MATLAB executable
81+
9382
Returns
9483
-------
9584
engine : Optional[RATapi.rat_core.MatlabEngine]
9685
A matlab engine object
97-
9886
"""
99-
try:
100-
with open(MATLAB_PATH_FILE) as path_file:
101-
matlab_path = path_file.read()
102-
except FileNotFoundError:
103-
matlab_path = ""
104-
105-
if not matlab_path:
106-
matlab_path = shutil.which("matlab")
107-
if matlab_path is None:
108-
matlab_path = ""
109-
else:
110-
print(matlab_path)
111-
temp = pathlib.Path(matlab_path)
112-
if temp.is_symlink():
113-
matlab_path = temp.readlink().as_posix()
114-
set_matlab_path(matlab_path)
87+
matlab_exe_path = find_existing_matlab()
11588

116-
if matlab_path:
117-
bin_path, dll_path = get_matlab_paths(matlab_path)
89+
if matlab_exe_path:
90+
bin_path, dll_path = get_matlab_paths(matlab_exe_path)
11891
os.environ["MATLAB_DLL_PATH"] = dll_path
11992
if platform.system() == "Windows":
12093
os.environ["PATH"] = dll_path + os.pathsep + os.environ["PATH"]
12194

12295
with cd(bin_path):
12396
engine = RATapi.rat_core.MatlabEngine()
124-
97+
# Ensure MATLAB is closed when Python shuts down.
98+
atexit.register(engine.close)
99+
125100
return engine
126101

127102

103+
def set_matlab_path(matlab_exe_path: str) -> None:
104+
"""Set the path of MATLAB to use for custom functions.
105+
106+
This will also register the MATLAB COM server on Windows OS which could be slow.
107+
108+
Parameters
109+
----------
110+
matlab_exe_path : str
111+
The full path of the MATLAB executable
112+
"""
113+
if not matlab_exe_path:
114+
return
115+
116+
global __MATLAB_ENGINE
117+
if __MATLAB_ENGINE is not None:
118+
__MATLAB_ENGINE.close()
119+
atexit.unregister(__MATLAB_ENGINE.close)
120+
__MATLAB_ENGINE = start_matlab(matlab_exe_path)
121+
122+
123+
if platform.system() == "Windows":
124+
process = subprocess.Popen(f'"{matlab_exe_path}" -batch "comserver(\'register\')"')
125+
process.wait()
126+
127+
with open(MATLAB_PATH_FILE, "w") as path_file:
128+
path_file.write(matlab_exe_path)
129+
130+
128131
class MatlabWrapper:
129132
"""Creates a python callback for a MATLAB function.
130133
131134
Parameters
132135
----------
133-
filename : string
136+
filename : str
134137
The path of the file containing MATLAB function
135138
"""
136139

137-
engine = start_matlab()
138-
139140
def __init__(self, filename) -> None:
140-
if self.engine is None:
141+
engine = get_matlab_engine()
142+
if engine is None:
141143
raise ValueError(
142144
"MATLAB is not found. Please use `set_matlab_path` to set the location of your MATLAB installation"
143145
) from None
144146

145147
path = pathlib.Path(filename)
146-
self.engine.cd(str(path.parent))
147-
self.engine.setFunction(path.stem)
148+
engine.cd(str(path.parent))
149+
engine.setFunction(path.stem)
148150

149151
def getHandle(self) -> Callable[[ArrayLike, ArrayLike, ArrayLike, int, int], tuple[ArrayLike, float]]:
150152
"""Return a wrapper for the custom dynamic library function.
@@ -157,7 +159,7 @@ def getHandle(self) -> Callable[[ArrayLike, ArrayLike, ArrayLike, int, int], tup
157159
"""
158160

159161
def handle(*args):
160-
return self.engine.invoke(*args)
162+
return get_matlab_engine().invoke(*args)
161163

162164
return handle
163165

@@ -191,3 +193,24 @@ def handle(*args):
191193
return self.engine.invoke(*args)
192194

193195
return handle
196+
197+
198+
def find_existing_matlab() -> str:
199+
matlab_exe_path = ""
200+
201+
with suppress(FileNotFoundError), open(MATLAB_PATH_FILE) as path_file:
202+
matlab_exe_path = path_file.read()
203+
204+
if not matlab_exe_path:
205+
matlab_exe_path = shutil.which("matlab")
206+
if matlab_exe_path is None:
207+
matlab_exe_path = ""
208+
else:
209+
temp = pathlib.Path(matlab_exe_path)
210+
if temp.is_symlink():
211+
matlab_exe_path = temp.readlink().as_posix()
212+
set_matlab_path(matlab_exe_path)
213+
214+
return matlab_exe_path
215+
216+
__MATLAB_ENGINE = start_matlab()

0 commit comments

Comments
 (0)