compute_extrema function over domain for a UFL expression#187
compute_extrema function over domain for a UFL expression#187
compute_extrema function over domain for a UFL expression#187Conversation
|
Resolves #186 |
src/scifem/eval.py
Outdated
| cache_dir = ( | ||
| (Path.cwd() / f".scifem_extrema_cache_{mesh.comm.rank}_{u_sign}").absolute().as_posix() | ||
| ) |
There was a problem hiding this comment.
I don't think we should hard code in the cace dir. I think have an environment variable SCIFEM_CACHE_DIR or something like that which can be used? And perhaps also make it possible to pass this as an argument to the function? Finally I think the default cache dir should be Path.home() / ".cache" / "scifem" instead.
There was a problem hiding this comment.
We shouldn't have a global cache dir (as each rank needs its own cache dir, as described below).
src/scifem/eval.py
Outdated
| return X_phys, sign * result.fun | ||
|
|
||
|
|
||
| def compute_LP_average(u: ufl.core.expr.Expr, p: int, domain: dolfinx.mesh.Mesh): |
There was a problem hiding this comment.
This function is missing a docstring.
| def eval_J(x_ref): | ||
| if isinstance(u, dolfinx.fem.Function): | ||
| _x_p[: mesh.geometry.dim] = mesh.geometry.cmap.push_forward( | ||
| x_ref.reshape(-1, mesh.topology.dim), mesh_nodes | ||
| )[0] | ||
| try: | ||
| u_eval = u.eval(_x_p, _cell)[0] | ||
| except RuntimeError: # NOTE: Nonlinear pullback might fail on low precision | ||
| u_expr = dolfinx.fem.Expression( | ||
| u, | ||
| x_ref, | ||
| comm=MPI.COMM_SELF, | ||
| jit_options=jit_options, | ||
| dtype=mesh.geometry.x.dtype, | ||
| ) | ||
| u_eval = u_expr.eval(mesh, _cell)[0][0] | ||
| else: | ||
| u_expr = dolfinx.fem.Expression( | ||
| u, x_ref, comm=MPI.COMM_SELF, jit_options=jit_options, dtype=mesh.geometry.x.dtype | ||
| ) | ||
| u_eval = u_expr.eval(mesh, _cell)[0][0] | ||
| return np.float64(sign * u_eval) # SLSQP only supports float64 | ||
|
|
||
| def eval_dJ(x_ref): | ||
| u_grad_expr = dolfinx.fem.Expression( | ||
| ufl.Jacobian(mesh).T * ufl.grad(u), | ||
| x_ref, | ||
| comm=MPI.COMM_SELF, | ||
| jit_options=jit_options, | ||
| dtype=mesh.geometry.x.dtype, | ||
| ) | ||
| u_grad_eval = u_grad_expr.eval(mesh, _cell)[0][0] | ||
| return (sign * u_grad_eval).astype(np.float64) # SLSQP only supports float64 |
There was a problem hiding this comment.
I wonder it is would be more clean to put these to methods as instance methods on a class instead. The class could potentially be useful in other contexts.
There was a problem hiding this comment.
Not sure... We can talk about this tomorrow.
src/scifem/eval.py
Outdated
| if Path(cache_dir).exists(): | ||
| raise RuntimeError(f"Cache directory for extrema exists at {cache_dir}") | ||
| jit_options = { | ||
| "cffi_extra_compile_args": ["-march=native", "-O3"], | ||
| "cffi_libraries": ["m"], | ||
| "cache_dir": cache_dir, | ||
| } |
There was a problem hiding this comment.
Why do we need this specific cache dir in the first place? And why is it a problem if it already exist?
There was a problem hiding this comment.
Because we are running dolfinx.fem.Expression on MPI.COMM_SELF, for all the cells (local to this process) that are part of the optimization process. To avoid racing conditions to the cache if all processes tries to generate code for the same expression at the same point, we need a specific cache dir for each of them.
Secondly, as the optimization method will greedly evaluate both expr(X_ref) and grad(expr)(X_ref) this will make a huge cache that will slow any other DOLFINx operation down.
src/scifem/eval.py
Outdated
| for item in Path(cache_dir).iterdir(): | ||
| # Check if the item is a file | ||
| if item.is_file(): | ||
| item.unlink() # Delete the file |
There was a problem hiding this comment.
Each comm has its own cache dir.
Makes it possible to compute the min or max of a scalar valued ufl expression over a domain.