Commit 7330b4d9 authored by Baptiste Durand's avatar Baptiste Durand

Merge branch 'Improve_readability'

parents fad7449f 5332a022
...@@ -23,11 +23,13 @@ from ho_homog import ( ...@@ -23,11 +23,13 @@ from ho_homog import (
homog2d, homog2d,
materials, materials,
mesh_generate_2D, mesh_generate_2D,
mesh_tools,
part, part,
pckg_logger, pckg_logger,
toolbox_FEniCS,
periodicity, periodicity,
) )
from ho_homog.toolbox_FEniCS import function_errornorm
from ho_homog.toolbox_gmsh import process_gmsh_log
logger = logging.getLogger("demo_full_compare") logger = logging.getLogger("demo_full_compare")
logger_root = logging.getLogger() logger_root = logging.getLogger()
...@@ -54,40 +56,11 @@ logging.getLogger("UFL").setLevel(logging.DEBUG) ...@@ -54,40 +56,11 @@ logging.getLogger("UFL").setLevel(logging.DEBUG)
logging.getLogger("FFC").setLevel(logging.DEBUG) logging.getLogger("FFC").setLevel(logging.DEBUG)
fe.set_log_level(20) fe.set_log_level(20)
def process_gmsh_log(gmsh_log: list, detect_error=True):
"""Treatment of log messages gathered with gmsh.logger.get()"""
err_msg, warn_msg = list(), list()
for line in gmsh_log:
if "error" in line.lower():
err_msg.append(line)
if "warning" in line.lower():
warn_msg.append(line)
gmsh_logger.info("**********")
gmsh_logger.info(
f"{len(gmsh_log)} logging messages got from Gmsh : "
f"{len(err_msg)} errors, {len(warn_msg)} warnings."
)
if err_msg:
gmsh_logger.error("Gmsh errors details :")
for line in err_msg:
gmsh_logger.error(line)
if warn_msg:
gmsh_logger.warning("Gmsh warnings details :")
for line in warn_msg:
gmsh_logger.warning(line)
gmsh_logger.debug("All gmsh messages :")
gmsh_logger.debug(gmsh_log)
gmsh_logger.info("**********")
if detect_error and err_msg:
raise AssertionError("Gmsh logging messages signal errors.")
# * Step 1 : Modeling the geometry of the RVE # * Step 1 : Modeling the geometry of the RVE
geometry.init_geo_tools() geometry.init_geo_tools()
geometry.set_gmsh_option("Mesh.Algorithm", 6) geometry.set_gmsh_option("Mesh.Algorithm", 6)
geometry.set_gmsh_option("Mesh.MshFileVersion", 4.1) geometry.set_gmsh_option("Mesh.MshFileVersion", 4.1)
gmsh.option.setNumber("Geometry.Tolerance", 1e-16) gmsh.option.setNumber("Geometry.Tolerance", 1e-15)
gmsh.option.setNumber("Mesh.CharacteristicLengthExtendFromBoundary", 0) gmsh.option.setNumber("Mesh.CharacteristicLengthExtendFromBoundary", 0)
gmsh.option.setNumber("Mesh.CharacteristicLengthMax", 0.1) gmsh.option.setNumber("Mesh.CharacteristicLengthMax", 0.1)
gmsh.option.setNumber("Mesh.LcIntegrationPrecision", 1e-9) gmsh.option.setNumber("Mesh.LcIntegrationPrecision", 1e-9)
...@@ -113,7 +86,7 @@ gmsh.logger.stop() ...@@ -113,7 +86,7 @@ gmsh.logger.stop()
gmsh.model.mesh.renumberNodes() gmsh.model.mesh.renumberNodes()
gmsh.model.mesh.renumberElements() gmsh.model.mesh.renumberElements()
gmsh.write(str(rve_geo.mesh_abs_path)) gmsh.write(str(rve_geo.mesh_abs_path))
rve_path, *_ = mesh_generate_2D.msh_conversion(rve_geo.mesh_abs_path, ".xdmf") rve_path, *_ = mesh_tools.msh_conversion(rve_geo.mesh_abs_path, ".xdmf")
# * Step 3 : Build the mesh of the part from the mesh of the RVE # * Step 3 : Build the mesh of the part from the mesh of the RVE
...@@ -121,7 +94,7 @@ gmsh.logger.start() ...@@ -121,7 +94,7 @@ gmsh.logger.start()
part_geo = mesh_generate_2D.Gmsh2DPartFromRVE(rve_geo, (75, 1)) part_geo = mesh_generate_2D.Gmsh2DPartFromRVE(rve_geo, (75, 1))
process_gmsh_log(gmsh.logger.get()) process_gmsh_log(gmsh.logger.get())
gmsh.logger.stop() gmsh.logger.stop()
part_path, *_ = mesh_generate_2D.msh_conversion(part_geo.mesh_abs_path, ".xdmf") part_path, *_ = mesh_tools.msh_conversion(part_geo.mesh_abs_path, ".xdmf")
# * Step 4 : Defining the material properties # * Step 4 : Defining the material properties
E, nu = 1.0, 0.3 E, nu = 1.0, 0.3
...@@ -245,54 +218,6 @@ macro_fields = { ...@@ -245,54 +218,6 @@ macro_fields = {
"EGG": [U_hom[3]] + [0] * 11, "EGG": [U_hom[3]] + [0] * 11,
} }
per_scalar_fnct_cpp_code = """
#include <pybind11/pybind11.h>
#include <pybind11/eigen.h>
namespace py = pybind11;
#include <dolfin/function/Expression.h>
#include <dolfin/function/Function.h>
class PeriodicExpr : public dolfin::Expression
{
public:
PeriodicExpr() : dolfin::Expression() {}
void eval(
Eigen::Ref<Eigen::VectorXd> values,
Eigen::Ref<const Eigen::VectorXd> x, const ufc::cell& cell
) const override
{
Eigen::Vector2d coord_equi;
coord_equi[0] = x[0] -per_x*floor(x[0]/per_x);
coord_equi[1] = x[1] -per_y*floor(x[1]/per_y);
f->eval(values, coord_equi);
//passer par un pointeur ? *f->eval(values, coord_equi);
}
std::shared_ptr<dolfin::Function> f;
double per_x;
double per_y;
};
PYBIND11_MODULE(SIGNATURE, m)
{
py::class_<PeriodicExpr, std::shared_ptr<PeriodicExpr>, dolfin::Expression>
(m, "PeriodicExpr", py::dynamic_attr())
.def(py::init<>())
.def_readwrite("f", &PeriodicExpr::f)
.def_readwrite("per_x", &PeriodicExpr::per_x)
.def_readwrite("per_y", &PeriodicExpr::per_y);
}
"""
class PeriodicExpr(fe.UserExpression):
def value_shape(self):
return ()
key_conversion = {"U": "u", "Epsilon": "eps", "Sigma": "sigma"} key_conversion = {"U": "u", "Epsilon": "eps", "Sigma": "sigma"}
localztn_expr = dict() localztn_expr = dict()
for key, scd_dict in hom_model.localization.items(): for key, scd_dict in hom_model.localization.items():
...@@ -307,16 +232,9 @@ for key, scd_dict in hom_model.localization.items(): ...@@ -307,16 +232,9 @@ for key, scd_dict in hom_model.localization.items():
for i, field in enumerate(localztn_fields): for i, field in enumerate(localztn_fields):
new_fields = list() # 1 field per component of U, Sigma and Epsilon new_fields = list() # 1 field per component of U, Sigma and Epsilon
for component in field.split(): for component in field.split():
per_field = PeriodicExpr(degree=3) per_field = periodicity.PeriodicExpr(
periodic_cppcode = fe.compile_cpp_code( component, rve_geo.gen_vect, degree=3
per_scalar_fnct_cpp_code )
).PeriodicExpr()
periodic_cppcode.per_x = rve_geo.gen_vect[0, 0]
periodic_cppcode.per_y = rve_geo.gen_vect[1, 1]
component.set_allow_extrapolation(True)
periodic_cppcode.f = component.cpp_object()
# https://bitbucket.org/fenics-project/dolfin/issues/1041/compiledexpression-cant-be-initialized
per_field._cpp_object = periodic_cppcode
new_fields.append(per_field) new_fields.append(per_field)
localztn_expr[key][updated_key2].append(new_fields) localztn_expr[key][updated_key2].append(new_fields)
function_spaces = {"u": model.displ_fspace, "eps": model.strain_fspace} function_spaces = {"u": model.displ_fspace, "eps": model.strain_fspace}
...@@ -356,11 +274,8 @@ errors = dict() ...@@ -356,11 +274,8 @@ errors = dict()
for f_name in reconstr_sol.keys(): for f_name in reconstr_sol.keys():
dim = exact_sol[f_name].ufl_shape[0] dim = exact_sol[f_name].ufl_shape[0]
exact_norm = fe.norm(exact_sol[f_name], "L2") exact_norm = fe.norm(exact_sol[f_name], "L2")
difference = toolbox_FEniCS.function_errornorm( difference = function_errornorm(reconstr_sol[f_name], exact_sol[f_name], "L2")
reconstr_sol[f_name], exact_sol[f_name], "L2"
)
error = difference / exact_norm error = difference / exact_norm
errors[f_name] = error errors[f_name] = error
print(f_name, error, difference, exact_norm) print(f_name, error, difference, exact_norm)
logger.info(f"Relative error for {f_name} = {error}") logger.info(f"Relative error for {f_name} = {error}")
...@@ -112,7 +112,7 @@ macro_s = geo.PlaneSurface(macro_ll) ...@@ -112,7 +112,7 @@ macro_s = geo.PlaneSurface(macro_ll)
logger.info("Start boolean operations on surfaces") logger.info("Start boolean operations on surfaces")
pattern_s = [geo.PlaneSurface(ll) for ll in pattern_ll] pattern_s = [geo.PlaneSurface(ll) for ll in pattern_ll]
rve_s = geo.AbstractSurface.bool_cut(macro_s, pattern_s) rve_s = geo.surface_bool_cut(macro_s, pattern_s)
rve_s = rve_s[0] rve_s = rve_s[0]
logger.info("Done boolean operations on surfaces") logger.info("Done boolean operations on surfaces")
rve_s_phy = geo.PhysicalGroup([rve_s], 2, "partition_plein") rve_s_phy = geo.PhysicalGroup([rve_s], 2, "partition_plein")
......
...@@ -5,11 +5,13 @@ Created on 08/04/2019 ...@@ -5,11 +5,13 @@ Created on 08/04/2019
""" """
import logging import logging
import warnings
from pathlib import Path
import dolfin as fe import dolfin as fe
import numpy as np import numpy as np
import ho_homog
from pathlib import Path import ho_homog.materials as mat
from fenicstools import interpolate_nonmatching_mesh_any
from ho_homog.toolbox_FEniCS import ( from ho_homog.toolbox_FEniCS import (
DOLFIN_KRYLOV_METHODS, DOLFIN_KRYLOV_METHODS,
DOLFIN_LU_METHODS, DOLFIN_LU_METHODS,
...@@ -18,8 +20,12 @@ from ho_homog.toolbox_FEniCS import ( ...@@ -18,8 +20,12 @@ from ho_homog.toolbox_FEniCS import (
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
GEO_TOLERANCE = ho_homog.GEO_TOLERANCE try:
mat = ho_homog.materials from fenicstools import interpolate_nonmatching_mesh_any
except ImportError:
warnings.warn("Import of fenicstools has failed.", ImportWarning)
logger.warning("Import : fenicstools cannot be imported.")
# * For mechanical fields reconstruction # * For mechanical fields reconstruction
MACRO_FIELDS_NAMES = ["U", "E", "EG", "EGG"] MACRO_FIELDS_NAMES = ["U", "E", "EG", "EGG"]
...@@ -295,9 +301,8 @@ def reconstruction( ...@@ -295,9 +301,8 @@ def reconstruction(
macro_kinematic: dict, macro_kinematic: dict,
function_spaces: dict, function_spaces: dict,
localization_rules: dict = {}, localization_rules: dict = {},
trunc_order: int = 0,
output_request=("u", "eps"), output_request=("u", "eps"),
proj_solver=None, **kwargs,
): ):
""" """
One argument among localization_rules and trunc_order must be used. One argument among localization_rules and trunc_order must be used.
...@@ -318,31 +323,47 @@ def reconstruction( ...@@ -318,31 +323,47 @@ def reconstruction(
None can be used to indicate a component that is equal to 0 or irrelevant. None can be used to indicate a component that is equal to 0 or irrelevant.
function_spaces : dictionnary function_spaces : dictionnary
Function space into which each mechanical field have to be built. Function space into which each mechanical field have to be built.
keys : 'u', 'eps' or 'sigma' - keys : 'u', 'eps' or 'sigma'
values : FEniCS function space - values : FEniCS function space
localization_rules : dict, optional localization_rules : dict, optional
Indicates the rules that have to be followed for the construction of the fluctuations. The rules that have to be followed for the construction of the fluctuations.
(the default is {}, which means that the trunc_order argument will be used) Defaults to {} i.e. the trunc_order parameter will be used.
trunc_order : int, optional
Order of truncation for the reconstruction of the displacement field
following the higher order homogenization scheme defined in ???.
output_request : tuple of strings, optional output_request : tuple of strings, optional
Which fields have to be calculated. Fields that have to be calculated.
This can contain : 'u', eps' and 'sigma' The request must be consistent with the keys of other parameters :
(the default is ('u', 'eps'), displacement and strain fields will be reconstructed) - function_spaces
proj_solver : string - localization_rules
impose the use of a desired solver for the projections. outputs can be :
======= ===================
name Description
======= ===================
'u' displacement field
'eps' strain field
'sigma' stress field
======= ===================
Defaults to ('u', 'eps').
Return Return
------ ------
Dictionnary Dictionnary
Mechanical fields with microscopic fluctuations. Keys are "eps", "sigma" and "u" respectively for the strain, stress and displacement fields. Mechanical fields with microscopic fluctuations.
""" Keys are "eps", "sigma" and "u" respectively for the strain, stress and displacement fields.
# ? Changer les inputs ?
# ? Remplacer le dictionnaire de functionspace et output_request par un seul argument : list de tuples qui contiennent nom + function_space ? Other Parameters
# ? Comme ça on peut aussi imaginer reconstruire le même champs dans différents espaces ? ----------------
**kwargs :
Valid kwargs are
=============== ===== =============================================
Key Type Description
=============== ===== =============================================
interp_fnct str The name of the desired function for the interpolations. Allowed values are : "dolfin.interpolate" and "interpolate_nonmatching_mesh_any"
trunc_order int Order of truncation for the reconstruction of the displacement according to the notations used in ???. Override localization_rules parameter.
=============== ====== ============================================
# ! La construction de champs de localisation périodiques doit être faite en dehors de cette fonction.
"""
# TODO : récupérer topo_dim à partir des tenseurs de localisation, ou mieux, à partir des espaces fonctionnels # TODO : récupérer topo_dim à partir des tenseurs de localisation, ou mieux, à partir des espaces fonctionnels
# TODO : choisir comment on fixe la len des listes correspondantes aux composantes de u et de epsilon. # TODO : choisir comment on fixe la len des listes correspondantes aux composantes de u et de epsilon.
...@@ -351,14 +372,9 @@ def reconstruction( ...@@ -351,14 +372,9 @@ def reconstruction(
# TODO : translation_microstructure: np.array, optional # TODO : translation_microstructure: np.array, optional
# TODO : Vector, 1D array (shape (2,) or (3,)), position origin used for the description of the RVE with regards to the macroscopic origin. # TODO : Vector, 1D array (shape (2,) or (3,)), position origin used for the description of the RVE with regards to the macroscopic origin.
solver_param = {}
if proj_solver:
solver_param = {"solver_type": proj_solver}
# Au choix, utilisation de trunc_order ou localization_rules dans les kargs # Au choix, utilisation de trunc_order ou localization_rules dans les kargs
if localization_rules: trunc_order = kwargs.pop("trunc_order", None)
pass if trunc_order:
elif trunc_order:
localization_rules = { localization_rules = {
"u": [ "u": [
(MACRO_FIELDS_NAMES[i], MACRO_FIELDS_NAMES[i]) (MACRO_FIELDS_NAMES[i], MACRO_FIELDS_NAMES[i])
...@@ -375,6 +391,17 @@ def reconstruction( ...@@ -375,6 +391,17 @@ def reconstruction(
# * 'eps': [('E','E'), ('EG', 'EG')] # * 'eps': [('E','E'), ('EG', 'EG')]
# *} # *}
interpolate = kwargs.pop("interp_fnct", None)
if interpolate:
if interpolate == "dolfin.interpolate":
interpolate = fe.interpolate
elif interpolate == "interpolate_nonmatching_mesh_any":
interpolate = interpolate_nonmatching_mesh_any
else:
interpolate = fe.interpolate
else:
interpolate = fe.interpolate
reconstructed_fields = dict() reconstructed_fields = dict()
for mecha_field in output_request: for mecha_field in output_request:
...@@ -417,8 +444,7 @@ def reconstruction( ...@@ -417,8 +444,7 @@ def reconstruction(
macro_kin_funct[key] = list() macro_kin_funct[key] = list()
for comp in field: for comp in field:
if comp: if comp:
# macro_kin_funct[key].append(fe.interpolate(comp, scalar_fspace)) macro_kin_funct[key].append(interpolate(comp, scalar_fspace))
macro_kin_funct[key].append(interpolate_nonmatching_mesh_any(comp, scalar_fspace))
else: else:
macro_kin_funct[key].append(0) macro_kin_funct[key].append(0)
...@@ -431,8 +457,7 @@ def reconstruction( ...@@ -431,8 +457,7 @@ def reconstruction(
if not macro_comp: if not macro_comp:
continue continue
for i in range(vector_dim): for i in range(vector_dim):
# loc_comp = fe.interpolate(loc_tens_comps[i], scalar_fspace) loc_comp = interpolate(loc_tens_comps[i], scalar_fspace)
loc_comp = interpolate_nonmatching_mesh_any(loc_tens_comps[i], scalar_fspace)
contributions[i].append((macro_comp, loc_comp)) contributions[i].append((macro_comp, loc_comp))
# components = [sum(compnt_contrib) for compnt_contrib in contributions] # components = [sum(compnt_contrib) for compnt_contrib in contributions]
...@@ -452,21 +477,8 @@ def reconstruction( ...@@ -452,21 +477,8 @@ def reconstruction(
# * Components -> vector field # * Components -> vector field
field = fe.Function(fspace) field = fe.Function(fspace)
components_proj = list()
logger.info(f"Projection of reconstructed {mecha_field}...")
components_proj = components
# for scl_field in components:
# # if element_family in ("Discontinuous Lagrange", "Quadrature"):
# # comp = local_project(scl_field, scalar_fspace)
# # else:
# # comp = fe.project(scl_field, scalar_fspace, **solver_param)
# scl_field = scl_field.cpp_object()
# f = fe.Function(scalar_fspace)
# f.interpolate(scl_field)
# components_proj.append(comp)
# logger.info(f"Projection of reconstructed {mecha_field} DONE")
if len(value_shape) == 0: if len(value_shape) == 0:
components_proj = components_proj[0] components = components[0]
assigner.assign(field, components_proj) assigner.assign(field, components)
reconstructed_fields[mecha_field] = field reconstructed_fields[mecha_field] = field
return reconstructed_fields return reconstructed_fields
This diff is collapsed.
# coding: utf8
"""
Created on 09/10/2018
@author: baptiste
Définition de classes d'objets géométriques et de fonctions permettant de créer un modèle géométrique de RVE dans gmsh.
sources :
- https://deptinfo-ensip.univ-poitiers.fr/ENS/doku/doku.php/stu:python:pypoo
"""
import logging
import math
import warnings
from pathlib import Path
import gmsh
import matplotlib.pyplot as plt
import numpy as np
# nice shortcuts
model = gmsh.model
factory = model.occ
logger = logging.getLogger(__name__)
warnings.simplefilter("always")
# ? Doc: https://docs.python.org/3.6/library/warnings.html
def set_gmsh_option(option, val):
"""
Set a gmsh option to the given value and print a log message.
Parameters
----------
option : string
val : string or number
Type of valid val depend of the option.
See the gmsh reference manual for more information.
"""
if isinstance(val, (int, float)):
setter = gmsh.option.setNumber
getter = gmsh.option.getNumber
elif isinstance(val, str):
setter = gmsh.option.setString
getter = gmsh.option.getString
else:
raise TypeError("Wrong type of parameter for a gmsh option.")
preval = getter(option)
setter(option, val)
logger.info(f"Gmsh option {option} set to {val} (previously : {preval}).")
from .curves import Curve, AbstractCurve, Arc, Line, bndry_logger
from .physical import PhysicalGroup
from .point import Point
from .surfaces import (
AbstractSurface,
LineLoop,
PlaneSurface,
surface_bool_cut,
surface_bool_intersect,
)
from .tools import (
angle_between,
bisector,
dual_base,
macro_line_fragments,
offset,
remove_duplicates,
round_corner,
unit_vect,
)
from .transformations import plane_reflection, point_reflection, rotation, translation
def init_geo_tools():
"""
The Gmsh Python API must be initialized before using any functions.
In addition, some options are set to custom values.
"""
gmsh.initialize() # ? Utiliser l'argument sys.argv ? cf script boolean.py
set_gmsh_option("General.Terminal", 1)
set_gmsh_option("General.Verbosity", 5)
set_gmsh_option("Geometry.AutoCoherence", 0)
set_gmsh_option("Mesh.ColorCarousel", 2)
# * 0=by element type, 1=by elementary entity, 2=by physical entity, 3=by partition
set_gmsh_option("Mesh.MeshOnlyVisible", 0)
# TODO : Should be in the init file of the mesh_tools module.
set_gmsh_option("Mesh.CharacteristicLengthExtendFromBoundary", 0)
set_gmsh_option("Mesh.SaveAll", 0)
set_gmsh_option("Mesh.Binary", 0)
set_gmsh_option("Mesh.MshFileVersion", 2.2)
set_gmsh_option("Mesh.Algorithm", 5)
# * 2D mesh algorithm (1=MeshAdapt, 2=Automatic,...)
# info about gmsh module
logger.info(f"gmsh module path : {Path(gmsh.__file__).resolve()}")
logger.info("Gmsh SDK version : %s", gmsh.option.getString("General.Version"))
def reset():
"""Throw out all information about the created geometry and remove all gmsh models."""
PhysicalGroup.all_groups = dict()
gmsh.clear()
__all__ = [
# * geometry global
"logger",
"set_gmsh_option",
"init_geo_tools",
"reset",
# * points
"Point",
# * curves
"Curve",
"AbstractCurve",
"Arc",
"Line",
"bndry_logger",
# * physical entities
"PhysicalGroup",
# * surfaces
"LineLoop",
"PlaneSurface",
"AbstractSurface",
"surface_bool_cut",
"surface_bool_intersect",
# * tools
"angle_between",
"bisector",
"dual_base",
"macro_line_fragments",
"offset",
"remove_duplicates",
"round_corner",
"unit_vect",
# * transformations
"plane_reflection",
"point_reflection",
"rotation",
"translation",
]
# coding: utf8
"""
Created on 09/10/2018
@author: baptiste
Definition of the classes : Curve, Line, Arc and AbstractCurve.
Objects designed to represent geometrical entitites of dimension one
and instantiate them in a gmsh model.
"""
import logging
import warnings
from logging.handlers import RotatingFileHandler
from pathlib import Path
from . import factory, logger, model, np, plt
from .point import Point
bndry_logger = logging.getLogger("bndry")
bndry_logger.setLevel(logging.DEBUG)
bndry_logger.propagate = False