Source code for nrv.fmod.FEM._COMSOL_model
"""
NRV-:class:`.COMSOL_model` handling.
"""
import configparser
import os
import time
import mph
import numpy as np
from ...backend._file_handler import rmv_ext
from ...backend._log_interface import rise_warning
from ...backend._parameters import parameters
from ...utils._units import V
from ._FEM import *
# built in COMSOL models
dir_path = parameters.nrv_path + "/_misc"
material_library = os.listdir(dir_path + "/comsol_templates/")
###############
## Constants ##
###############
machine_config = configparser.ConfigParser()
config_fname = dir_path + "/NRV.ini"
machine_config.read(config_fname)
COMSOL_Ncores = int(machine_config.get("COMSOL", "COMSOL_CPU"))
COMSOL_Status = machine_config.get("COMSOL", "COMSOL_STATUS") == "True"
fem_verbose = True
[docs]
class COMSOL_model(FEM_model):
"""
A class for COMSOL Finite Element Models, inherits from FEM_model.
"""
[docs]
def __init__(self, fname, n_proc=None, handle_server=False):
"""
Creates a instance of a COMSOL Finite Element Model object.
Parameters
----------
fname : str
path to the COMSOL (.mph) model file
n_proc : int
number of COMSOL cores for computation. If None is specified, this number is taken from the NRV2.ini configuration file. Byu default set to None
handle_server : bool
if True, the instantiation creates the server, else a external server is used. Usefull for multiple cells sharing the same model
"""
if COMSOL_Status:
t0 = time.time()
super().__init__(n_proc=n_proc)
self.type = "COMSOL"
self.model_path = fname
f_in_librairy = rmv_ext(str(fname)) + ".mph"
self.__has_client = False
self.__has_server = False
if f_in_librairy in material_library:
self.fname = dir_path + "/comsol_templates/" + f_in_librairy
else:
self.fname = fname
# self.model_path = fname
if self.n_proc is None:
self.n_proc = COMSOL_Ncores
self.handle_server = handle_server
# start client and server
pass_info("Starting COMSOL server/client, this may take few seconds")
if self.handle_server:
self.server = mph.Server(cores=self.n_proc)
self.__has_server = True
else:
self.server = None
self.client = mph.start(cores=self.n_proc)
self.__has_client = True
self.client.caching(True)
pass_info("... loading the COMSOL model")
self.model = self.client.load(self.fname)
# self.client.caching(True)
# source
self.fname = fname
self.setup_timer += time.time() - t0
else:
## COMSOL TURNED OFF, no error, but only a warning and no computation (exit)
rise_warning(
"Bad implementation, a COMSOL simulation is implemented while NRV2 has COMSOL status turned OFF, unterminated computation, early exit without error",
abort=True,
)
[docs]
def save(self):
"""
Save the changes to the model file. (Avoid for the overal weight of the package)
"""
self.model.save()
[docs]
def clear(self):
"""
Clear the mesh and result section of the model
"""
self.model.clear()
self.model.reset()
[docs]
def close(self):
"""
Close the FEM simulation and the COMSOL link
"""
if self.__has_client:
self.client.disconnect()
del self.client
self.__has_client = False
if self.handle_server or self.__has_server:
self.server.stop()
del self.server
self.__has_server = False
def __del__(self):
"""
Ensure the COMSOL client/server resources are closed on destruction.
"""
self.close()
super().__del__()
#############################
## Access model parameters ##
#############################
[docs]
def get_parameters(self):
"""
Get the all the parameters in the model as a python dictionary.
Returns
-------
parameters
all parameters as dictionnary, names a keys, with corresponding values
"""
return self.model.parameters()
[docs]
def get_parameter(self, p_name):
"""
Get a specific parameter
Returns
-------
str
value of the parameter as in COMSOL (with unit)
"""
return self.model.parameter(p_name)
[docs]
def set_parameter(self, p_name, p_value):
"""
Set a parameter to a desired value
Parameters
----------
p_name : str
parameter name in the COMSOL model
p_value : str
parameter value as in COMSOL, with unit
"""
self.model.parameter(p_name, p_value)
###################
## Use the model ##
###################
[docs]
def get_meshes(self):
"""
Get the different meshes implemented in the model
Returns
-------
list
list of meshes implemented in the COMSOL model file
"""
return self.model.meshes()
[docs]
def build_and_mesh(self):
"""
Build the geometry and perform meshing process
"""
t0 = time.time()
pass_info("... Building model geometry")
self.model.build()
pass_info("... Meshing geometry")
self.model.mesh()
self.is_meshed = True
self.meshing_timer += time.time() - t0
[docs]
def solve(self):
"""
Solve the model
"""
if not self.is_meshed:
self.build_and_mesh()
pass_info("... Solving model")
t0 = time.time()
self.model.solve()
self.is_computed = True
self.solving_timer += time.time() - t0
######################
## results handling ##
######################
[docs]
def get_potentials(self, x, y, z):
"""
Get the potential on a line to get extracellular potential for axons stimulation.
Parameters
----------
x : np.array
array of x coordinates in the model
y : float
y-coordinate of the axon
z : float
z-coordinate of the axon
Returns:
--------
array
All potential for all paramtric sweeps (all electrodes in NRV2 models)\
(line: electrode selection, column: potential)
"""
t0 = time.time()
COMSOL_expressions = [
"at3(" + str(x[k]) + "[um], " + str(y) + "[um], " + str(z) + "[um], V)"
for k in range(len(x))
]
Voltage = self.model.evaluate(COMSOL_expressions) * V
self.access_res_timer += time.time() - t0
# transpose to be consistent check if no BUG generated
return np.asarray(Voltage).T
[docs]
def export(self, path=""):
"""
Export the figures of the COMSOL results and posprocess (in PNG format)
Parameters
----------
path : str
path address where to save graphics
"""
exports = self.model.exports()
for export in exports:
self.model.export(export, path + export + ".png")