| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | __title__ = "Tools for the work with CalculiX solver" |
| | __author__ = "Mario Passaglia" |
| | __url__ = "https://www.freecad.org" |
| |
|
| |
|
| | from PySide.QtCore import QProcess, QThread, QProcessEnvironment |
| | import tempfile |
| | import os |
| | import shutil |
| |
|
| | import FreeCAD |
| | import Fem |
| |
|
| | from . import writer |
| | from .. import settings |
| |
|
| | |
| | from femmesh import meshsetsgetter |
| | from femtools import membertools |
| |
|
| |
|
| | class CalculiXTools: |
| |
|
| | name = "CalculiX" |
| |
|
| | def __init__(self, obj): |
| | self.obj = obj |
| | self.process = QProcess() |
| | self.model_file = "" |
| | self.analysis = obj.getParentGroup() |
| | self.fem_param = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem") |
| | self._create_working_directory(obj) |
| |
|
| | def _create_working_directory(self, obj): |
| | """ |
| | Create working directory according to preferences |
| | """ |
| | if not os.path.isdir(obj.WorkingDirectory): |
| | gen_param = self.fem_param.GetGroup("General") |
| | if gen_param.GetBool("UseTempDirectory"): |
| | self.obj.WorkingDirectory = tempfile.mkdtemp(prefix="fem_") |
| | elif gen_param.GetBool("UseBesideDirectory"): |
| | root, ext = os.path.splitext(obj.Document.FileName) |
| | if root: |
| | self.obj.WorkingDirectory = os.path.join(root, obj.Label) |
| | os.makedirs(self.obj.WorkingDirectory, exist_ok=True) |
| | else: |
| | |
| | self.obj.WorkingDirectory = tempfile.mkdtemp(prefix="fem_") |
| | elif gen_param.GetBool("UseCustomDirectory"): |
| | self.obj.WorkingDirectory = gen_param.GetString("CustomDirectoryPath") |
| | os.makedirs(self.obj.WorkingDirectory, exist_ok=True) |
| |
|
| | def prepare(self): |
| | from femtools.checksanalysis import check_member_for_solver_calculix |
| |
|
| | self._clear_results() |
| |
|
| | message = check_member_for_solver_calculix( |
| | self.analysis, |
| | self.obj, |
| | membertools.get_mesh_to_solve(self.analysis)[0], |
| | membertools.AnalysisMember(self.analysis), |
| | ) |
| |
|
| | mesh_obj = membertools.get_mesh_to_solve(self.analysis)[0] |
| | meshdatagetter = meshsetsgetter.MeshSetsGetter( |
| | self.analysis, |
| | self.obj, |
| | mesh_obj, |
| | membertools.AnalysisMember(self.analysis), |
| | ) |
| | meshdatagetter.get_mesh_sets() |
| |
|
| | |
| | w = writer.FemInputWriterCcx( |
| | self.analysis, |
| | self.obj, |
| | mesh_obj, |
| | meshdatagetter.member, |
| | self.obj.WorkingDirectory, |
| | meshdatagetter.mat_geo_sets, |
| | ) |
| | self.model_file = w.write_solver_input() |
| | |
| | self.input_deck = os.path.splitext(os.path.basename(self.model_file))[0] |
| |
|
| | def compute(self): |
| | self._clear_results() |
| | ccx_bin = settings.get_binary("Calculix") |
| | env = QProcessEnvironment.systemEnvironment() |
| | num_cpu = self.fem_param.GetGroup("Ccx").GetInt( |
| | "AnalysisNumCPUs", QThread.idealThreadCount() |
| | ) |
| | env.insert("OMP_NUM_THREADS", str(num_cpu)) |
| | pastix_prec = "1" if self.obj.PastixMixedPrecision else "0" |
| | env.insert("PASTIX_MIXED_PRECISION", pastix_prec) |
| | self.process.setProcessEnvironment(env) |
| | self.process.setWorkingDirectory(self.obj.WorkingDirectory) |
| |
|
| | command_list = ["-i", os.path.join(self.obj.WorkingDirectory, self.input_deck)] |
| | self.process.start(ccx_bin, command_list) |
| |
|
| | return self.process |
| |
|
| | def update_properties(self): |
| | |
| | self._load_ccxfrd_results() |
| | self._load_ccxdat_results() |
| |
|
| | def _clear_results(self): |
| | |
| | |
| | dir_content = os.listdir(self.obj.WorkingDirectory) |
| | for f in dir_content: |
| | path = os.path.join(self.obj.WorkingDirectory, f) |
| | base, ext = os.path.splitext(path) |
| | if ext == ".vtm": |
| | |
| | os.remove(path) |
| | |
| | shutil.rmtree(base) |
| | elif ext == ".dat": |
| | |
| | os.remove(path) |
| |
|
| | def _load_ccxdat_results(self): |
| | |
| | dat = None |
| | for res in self.obj.Results: |
| | if res.isDerivedFrom("App::TextDocument"): |
| | dat = res |
| |
|
| | if not dat: |
| | |
| | dat = self.obj.Document.addObject("App::TextDocument", self.obj.Name + "Output") |
| | self.analysis.addObject(dat) |
| | tmp = self.obj.Results |
| | tmp.append(dat) |
| | self.obj.Results = tmp |
| |
|
| | files = os.listdir(self.obj.WorkingDirectory) |
| | for f in files: |
| | if f.endswith(".dat"): |
| | dat_file = os.path.join(self.obj.WorkingDirectory, f) |
| | with open(dat_file, "r") as file: |
| | dat.Text = file.read() |
| | break |
| |
|
| | def _load_ccxfrd_results(self): |
| | |
| | pipeline = None |
| | create = False |
| | for res in self.obj.Results: |
| | if res.isDerivedFrom("Fem::FemPostPipeline"): |
| | pipeline = res |
| |
|
| | if not pipeline: |
| | |
| | pipeline = self.obj.Document.addObject("Fem::FemPostPipeline", self.obj.Name + "Result") |
| | self.analysis.addObject(pipeline) |
| | tmp = self.obj.Results |
| | tmp.append(pipeline) |
| | self.obj.Results = tmp |
| | create = True |
| |
|
| | frd_result_prefix = os.path.join(self.obj.WorkingDirectory, self.input_deck) |
| | binary_mode = self.fem_param.GetGroup("Ccx").GetBool("BinaryOutput", False) |
| | Fem.frdToVTK(frd_result_prefix + ".frd", binary_mode) |
| | files = os.listdir(self.obj.WorkingDirectory) |
| | for f in files: |
| | if f.endswith(".vtm"): |
| | vtm_file = os.path.join(self.obj.WorkingDirectory, f) |
| | pipeline.read(vtm_file) |
| | pipeline.renameArrays(self.frd_var_conversion(self.obj.AnalysisType)) |
| | break |
| |
|
| | if create: |
| | |
| | pipeline.ViewObject.DisplayMode = "Surface" |
| | pipeline.ViewObject.SelectionStyle = "BoundBox" |
| | pipeline.ViewObject.Field = self.get_default_field(self.obj.AnalysisType) |
| |
|
| | def frd_var_conversion(self, analysis_type): |
| | common = { |
| | "CONTACT": "Contact Displacement", |
| | "PE": "Plastic Strain", |
| | "CELS": "Contact Energy", |
| | "ECD": "Current Density", |
| | "EMFB": "Magnetic Field", |
| | "EMFE": "Electric Field", |
| | "ENER": "Internal Energy Density", |
| | "DISP": "Displacement", |
| | "TOSTRAIN": "Strain", |
| | "STRESS": "Stress", |
| | "STR(%)": "Error", |
| | } |
| | thermo = {"FLUX": "Heat Flux", "T": "Temperature"} |
| | electrostatic = {"T": "Potential", "FLUX": "Electric Flux Density"} |
| |
|
| | match analysis_type: |
| | case "thermomech": |
| | common.update(thermo) |
| | case "electromagnetic": |
| | common.update(electrostatic) |
| |
|
| | return common |
| |
|
| | def get_default_field(self, analysis_type): |
| | match analysis_type: |
| | case "static" | "frequency" | "buckling": |
| | return "Displacement" |
| | case "thermomech": |
| | return "Temperature" |
| | case "electromagnetic": |
| | return "Potential" |
| |
|
| | def version(self): |
| | p = QProcess() |
| | ccx_bin = settings.get_binary("Calculix") |
| | p.start(ccx_bin, ["-v"]) |
| | p.waitForFinished() |
| | info = p.readAll().data().decode() |
| | return info |
| |
|