PyXMake Developer Guide 1.0
PyXMake
Loading...
Searching...
No Matches
__init__.py
1# -*- coding: utf-8 -*-
2# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3# % VTL Module - Classes and Functions %
4# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5"""
6Module containing virtual testing & benchmark scripts.
7
8@note: PyXMake module
9Created on 20.03.2018
10
11@version: 1.0
12----------------------------------------------------------------------------------------------
13@requires:
14 -
15
16@change:
17 -
18
19@author: garb_ma [DLR-FA,STM Braunschweig]
20----------------------------------------------------------------------------------------------
21"""
22
23## @package PyXMake.VTL
24# Module containing virtual testing & benchmark scripts.
25## @author
26# Marc Garbade
27## @date
28# 18.03.2019
29## @par Notes/Changes
30# - Added documentation // mg 18.03.2019
31# - Renamed VTC with VTL // mg 04.04.2019
32
33import os, sys, platform
34import subprocess
35import io
36import posixpath
37import shlex
38import inspect
39import tempfile
40import argparse
41import importlib
42
43from ..Tools import Utility
44
45# Define global path definitions before loading subsequent verification example files.
46Scratch = tempfile.gettempdir() if getattr(sys,"frozen",False) or Utility.IsDockerContainer() else os.path.join(os.path.dirname(os.path.abspath(__file__)),"scratch")
47
48## @class PyXMake.VTL.Command
49# Parent class inherited from built-in exception.
50class Command(object):
51 """
52 Base class for all CLI commands
53 """
54 def __init__(self, *args, **kwargs):
55 """
56 Initialization of Command class object.
57 """
58 super(Command, self).__init__(*args, **kwargs)
59 ## String identifier of current instance.
60 self.CommandObjectKind = "PyXMake"
61
62 @staticmethod
63 def alias(*args, **kwargs):
64 """
65 Provides an aliases for a given script or command to be used in the CLI instead.
66 """
67 # Adding predefined aliases
68 alias = {"stm_conda_env":"config","stm_cara_software":"cara","py2x":"f2py","app":"pyinstaller"}
69 return alias
70
71 @classmethod
72 def cli(cls, *args, **kwargs):
73 """
74 Get a tuple of all valid command line options.
75
76 @note: Defaults to dropping all company (DLR) related prefixes from the scripts.
77 """
78 # Get a dictionary of all aliases
79 aliases = {v:k for k,v in cls.alias().items()}
80 # Verify that the input is a valid method
81 directories = [os.path.dirname(os.path.abspath(__file__)),
82 os.path.abspath(os.path.join(Utility.GetPyXMakePath(),"VTL","cmd",Utility.GetPlatform()))]
83 methods = [[path] + os.path.basename(y).split(".") for path in directories for y in os.listdir(path)
84 if os.path.isfile(os.path.join(path,y)) and not y.startswith(("__",".")) ]
85 # Remove all predefined prefixes from the command list except those referred to in aliases.
86 methods = [ x for x in methods if not x[1].lower().startswith("stm_") or x[1] in list(aliases.values()) ]
87 # Return a dictionary containing all valid choices and methods
88 return {"choices":[cls.alias().get(x[1],x[1]) for x in methods], "methods":methods }
89
90 @classmethod
91 def parse(cls, command, config, args):
92 """
93 Parse user-supplied TOML file if given and add its arguments to a CLI command.
94 """
95 # Initial empty command and default delimn
96 parsed_command = ""; parsed_data = {}; delimn = " ";
97 # Verify that the given method is a valid identifier. Take potential aliases into account
98 try: method = next(iter(key for key, value in cls.alias().items() if args.method[0] in value))
99 except StopIteration: method = args.method[0]
100 # Check if a TOML configuration can be found and no additional CLI commands are given.
101 if config and args.method[-1].lower() not in ["-h","--help"] and any(method in Utility.PathLeaf(x) for x in command.split(delimn)):
102 print("==================================")
103 print("Processing info from %s" % config)
104 print("==================================")
105 # Required local imports for this method.
106 import tomlkit
107 import json
108 # Collect all info from the TOML file.
109 data = tomlkit.parse(open(config).read())["tool"]
110 # Get current class instance programmatically
111 base = cls().CommandObjectKind
112 # Support naming convention in TOML files. Abbreviations for settings should be collected under PyXMake.
113 if data.get(base.lower(),{}): data = data[base.lower()].get(args.method[0],{})
114 else: data = {}
115 # This only happens when PyXMake is not part of the tools dictionary or the method follows the unconventional development scheme.
116 if not data: data = tomlkit.parse(open(config).read())["tool"].get(args.method[0],{})
117 # Create a list from the command
118 command = command.split(delimn)
119 # Get index of executable
120 index = [n for n, x in enumerate(command, 1) if method in x][0]
121 # Complex dictionary might be parsed as a list of single dictionaries. Catch this case.
122 for key, value in data.items():
123 # Only meaningful when value is a list.
124 if isinstance(value,list):
125 try:
126 parsed_dict = {}
127 # Check if the dictionary can be merged into one. Required for PyXMake supported methods.
128 for x in value: parsed_dict.update(x)
129 parsed_data[key] = parsed_dict
130 # Continue with next key if successful
131 continue
132 # Fail gracefully
133 except: pass
134 # The value is either not a list or not a list of dictionaries.
135 parsed_data[key] = value
136 # Loop over all entries and add its values to the CLI command.
137 for key, value in parsed_data.items():
138 try:
139 # Check if value is a PATH object. Verify compatibility.
140 if os.path.isdir(os.path.normpath(value)) or os.path.isfile(os.path.normpath(value)):
141 value = os.path.normpath(value)
142 # Fail gracefully.
143 except: pass
144 # Add a new named argument to the CLI command.
145 parsed_command += " --%s " % str(key)
146 if isinstance(value, str): parsed_command += "%s" % str(value)
147 if isinstance(value, list): parsed_command += delimn.join(["'%s'" % x for x in value])
148 if isinstance(value, dict): parsed_command += "'%s'" % json.dumps(value)
149 # User input takes precedence, thus parsed settings have to be put in between.
150 command.insert(index,parsed_command)
151 # Reassemble command
152 command = delimn.join(command)
153 # Return the command
154 return command
155
156 @classmethod
157 def verify(cls, selection, **kwargs):
158 """
159 Return command script if verified.
160 """
161 # Check if an alias is used as a selection identifier.
162 aliases = kwargs.get("aliases",{v:k for k, v in cls.alias().items()})
163 # If methods are not given, execute CLI script and get the last elements
164 methods = kwargs.get("methods",cls.cli()["methods"])
165 # Get valid alias for selection if exits. Defaults to proceed with selection.
166 identifier = aliases.get(selection,selection)
167 found = [str(identifier) in x if len(x) == len(str(identifier)) else False for x in list(zip(*methods))[1]]
168 # Unknown option
169 if not any(found): raise ValueError("Unknown CLI option %s" % str(selection))
170 # Obtain the correct file extension
171 path,method,ext = [methods[i] for i,x in enumerate(found) if x][0]
172 # This is the command script
173 script = os.path.join(path,".".join([str(method),ext]))
174 # Return command line script
175 return script
176
177 @classmethod
178 def run(cls, namespace=None, **kwargs):
179 """
180 Execute command line parser for VTL module.
181 """
182 # Local CLI function
183 def __cli(namespace, **kwargs):
184 """
185 CLI parser for VTL module.
186 """
187 commands = [];
188 # Verify that the input is a valid method
189 script = cls.verify(namespace)
190 # Do not process platform scripts here.
191 if not script.endswith(".py"): raise ValueError
192 # Execute wrapper within this script or parse the command to the script
193 argv = " ".join(sys.argv).split(namespace,1)[1].split()
194 commands.append(" ".join([sys.executable,script]+argv))
195 return commands
196 # Local CMD function
197 def __cmd(namespace, **kwargs):
198 """
199 CMD script execution for VTL module.
200 """
201 commands = []; script = None
202 # Check if the namespace is already a valid CLI command
203 if Utility.GetExecutable(namespace): script = namespace
204 # Check if script is pending
205 if not script:
206 # Verify that the input is a valid method
207 script = cls.verify(namespace)
208 # Execute wrapper within this script or parse the command to the script
209 if Utility.GetPlatform() in ["linux"]: commands.append(" ".join(["chmod","u+x",script]))
210 # Add command
211 argv = " ".join(sys.argv).split(namespace)[1].split()
212 commands.append(" ".join([script]+argv))
213 return commands
214 # Local variables
215 delimn = " "
216 # Use supplied configuration file. Can be empty. Only from poetry
217 config = os.getenv("pyx_poetry_config",os.path.join(os.getcwd(),"pyproject.toml"))
218 # No configuration file is given and default path does not exist
219 if not os.path.exists(config): config = None
220 # Support undocumented user options
221 # Collect information from command line. Collect all parameters after '--'.
222 try:
223 dash_index = sys.argv.index("--")
224 dashed_args = sys.argv[dash_index+1:]
225 sys.argv = sys.argv[:dash_index]
226 except ValueError: dashed_args = []
227 ## Get function from parser
228 # Check whether the given project ID string coincides with the selected method.
229 # Correctly identify this as a command line parameter and proceed accordingly.
230 if not namespace or (namespace and config and namespace in sys.argv[-1] and not namespace in sys.argv[-2]):
231 parser, options = cls.main(is_subparser=True)
232 # Select method
233 args, _ = parser.parse_known_args()
234 # Default module execution
235 commands = [delimn.join([sys.executable,"-m"] + args.method)]
236 # Fetch the correct method. Only if not already defined.
237 if not namespace: namespace = str(args.method[0])
238 # Function is given directly
239 else: commands = []; options = kwargs
240 # CLI script is executed from main with configuration file within the current working directory
241 if namespace and config:
242 try:
243 _ = args
244 # If method contains multiple values, take the latter
245 if args.method[0] != args.method[-1]: args.method.pop(0)
246 # Only refer to pyproject.toml directly if not values are given
247 if len(args.method) != 1: config = None
248 ## The upper section has not been executed successfully.
249 # Thus, no values from a configuration file are processed. This is most-likely not an error.
250 # Proceed as usual
251 except UnboundLocalError: config = None
252 # Try both. Raise error if none works
253 try: commands = __cli(namespace, **options)
254 except ValueError:
255 try: commands = __cmd(namespace, **options)
256 except: commands = []
257 else: pass
258 # This should never happen
259 if not commands: raise ValueError("Unknown CLI option")
260 # Execute all commands in order
261 for i, command in enumerate(commands,1):
262 # Only the last command in vein is an actual build command
263 if i == len(commands):
264 # Add command line options from a supplied TOML file
265 if config: command = cls.parse(command,config,args)
266 # Add command line options w/o verification after '--'. Can be combined with TOML file or run without
267 command = str(delimn.join([command]+dashed_args)).strip()
268 # Execute all commands in order. Including preparation commands
269 subprocess.call(shlex.split(command,posix=not os.name.lower() in ["nt"]),stderr=sys.stderr, stdout=sys.stdout)
270 pass
271
272 @classmethod
273 def main(cls,**kwargs):
274 """
275 Main entrypoint of PyXMake CLI. All other subparsers are derived from here.
276 """
277 # Import local version identifier
278 from PyXMake import __version__
279 # A dictionary of all valid identifiers
280 options = cls.cli()
281 # This argument is equal for all callables.
282 argument = ["method", {"metavar":'namespace', "type":str, "nargs":argparse.REMAINDER, "choices":options["choices"],
283 "help":'An option identifier. Unknown arguments are ignored. Allowed values are: '+', '.join(options["choices"]) } ]
284 # Get current class base name programmatically
285 base = cls().CommandObjectKind
286 # Equal for both methods
287 parser = argparse.ArgumentParser(prog=base, description='CLI wrapper options for %s.' % base)
288 parser.add_argument('-v', '--version', action='version', version='%(prog)s ' + "(%s)" % __version__)
289 # Execute as main or return the parser object
290 if not kwargs.get("is_subparser",False):
291 # Execute as main
292 subparsers = parser.add_subparsers(dest='namespace')
293 # Add run to parser object
294 __run = subparsers.add_parser('run', help='Run a predefined CLI command.')
295 __run.add_argument(argument[0],**argument[1])
296 # Add info to parser object
297 subparsers.add_parser('info', help='Get information about the current version of %s' % base)
298 # Treat function as CLI command
299 args = parser.parse_args()
300 if args.namespace in ["run"]: Command.run(namespace=str(args.method[0]), **options)
301 elif args.namespace in ["info"]: parser.parse_args(['--version'])
302 else: parser.print_help(sys.stdout)
303 # Return nothing if called directly.
304 return 0
305 else:
306 # Return the parser object.
307 parser.add_argument(argument[0],**argument[1])
308 # Return argparse and options interface
309 return parser, options
310
311def GetSourceCode(key_opt=0):
312 """
313 Get a list of source codes files associated with the build of MCODAC, BoxBeam or BEOS.
314
315 @param: key_opt: An integer value representing the source code files.
316 @type: key_opt: integer
317 """
318 ## Source code files of MCODAC in the correct build order (excluding interface source code file).
319 mcd_src_files = ["mcd_data", "mcd_error", "mcd_plugins", "mcd_tools", "mcd_contact", "mcd_material",
320 "mcd_load", "mcd_element", "mcd_subbuckling", "mcd_dmginitiation",
321 "mcd_dmgevolution", "mcd_fracture", "mcd_dmginfluence", "mcd_degradation",
322 "mcd_dg8", "mcd_dmgtolerance","mcd_iostream", "mcd_fatigue", "mcd_prony",
323 "mcd_wrapper", "mcd_toplevel", "mcd_main"]
324
325 ## Source code files of BoxBeam in the correct build order (excluding interface source code file).
326 box_src_files = ["box_data", "box_tools", "box_main"]
327
328 ## Source code files of BEOS in the correct build order (excluding interface source code file).
329 beos_src_files = ["beos_data","beos_tools","beos_main"]
330
331 ## Source code files of MUESLI in the correct build order (excluding interface source code file).
332 muesli_src_files = ["material.cpp", "tensor.cpp", "brownmiller.cpp", "jcfailure.cpp", "arrheniustype.cpp", "arrudaboyce.cpp",
333 "finitestrain.cpp", "fisotropic.cpp", "fplastic.cpp", "johnsoncook.cpp", "mooney.cpp", "neohook.cpp", "reducedfinitestrain.cpp",
334 "svk.cpp", "yeoh.cpp", "zerilliarmstrong.cpp", "fluid.cpp", "newtonian.cpp", "thermofinitestrain.cpp",
335 "fmechmass.cpp", "interface_abaqus.cpp", "interface_lsdyna.cpp", "mtensor.cpp", "mmatrix.cpp",
336 "mrealvector.cpp","smallstrain.cpp", "elastic.cpp", "reducedsmallstrain.cpp", "sdamage.cpp", "splastic.cpp",
337 "viscoelastic.cpp", "viscoplastic.cpp", "smallthermo.cpp", "conductor.cpp", "utils.cpp"]
338
339 # Source code files of CompDam (NASA's material library) in correct build order (excluding interface source code files).
340 compdam_src_files = ["vumatArgs.for","version.for.nogit","forlog.for","matrixUtil.for","matProp.for","stateVar.for","parameters.for", "schapery.for",
341 "stress.for","strain.for","schaefer.for","plasticity.for","fiberDamage.for","friction.for","cohesive.for","DGD.for"]
342 #"vucharlength.for","vexternaldb.for","CompDam_DGD.for","vumatWrapper.for","UMAT.for"
343
344 # Source code files of DispModule
345 dispmod_src_files = ["dispmodule.f90","disp_i1mod.f90","disp_i2mod.f90","disp_i4mod.f90","disp_i8mod.f90","disp_l1mod.f90","disp_r16mod.f90"]
346
347 # Source code files of TOMS
348 toms_src_files = ["toms790.f","toms661.f90"]
349
350 # Source code files of various other libraries
351 misc_src_files =["nms.f90","pchip.f90","slatec.f90","interp.f90"]
352
353 # Source code for PyCODAC application
354 pyc_src_files = ["__exe__.py"]
355
356 # Select correct files for output
357 if key_opt==0:
358 source = mcd_src_files
359 elif key_opt==1:
360 source = box_src_files
361 elif key_opt==2:
362 source = beos_src_files
363 elif key_opt==3:
364 source = muesli_src_files
365 elif key_opt==4:
366 source = compdam_src_files
367 elif key_opt==5:
368 source = dispmod_src_files
369 elif key_opt==6:
370 source = toms_src_files
371 elif key_opt==7:
372 source = misc_src_files
373 elif key_opt==8:
374 source = pyc_src_files
375 else:
376 raise NotImplementedError
377 # Return list of source code files in correct order.
378 return source
379
381 """
382 Return the recommended preprocessor command line.
383
384 @param: make_opt: An integer value representing or qualified name the requested preprocessor
385 @type: pre_opt: integer or string
386 """
387 delimn = " "
388 # All supported preprocessor
389 Supported = ["fpp", "cpp"]
390 # Parse both string and integer representation
391 PreprocessorDict = {x:y for x, y in enumerate(Supported)}
392 # Get string instance
393 preprocessor = pre_opt
394 if isinstance(pre_opt, int): preprocessor = PreprocessorDict[pre_opt]
395 # Raise error if preprocessor is not supported
396 if preprocessor not in Supported: raise NotImplementedError
397 # Get preprocessing command in dependence of FPP oder CPP
398 precmd = [preprocessor ,"-P"]
399 if preprocessor in ["fpp"]: precmd.extend(["-e"])
400 if preprocessor in ["cpp"]: precmd.extend(["--traditional"])
401 # Return assembled
402 return delimn.join(precmd)
403
404def GetBuildCommand(make_opt=0, _format="fixed", _arch=Utility.GetArchitecture()):
405 """
406 Return the recommended command line during the build process for selected make operations.
407
408 @param: make_opt: An integer value representing the make operation.
409 @type: make_opt: integer
410 """
411 # Recover information from cmake to retrieve build settings
412 try:
413 with open(os.path.join(os.getcwd(),"build","CMakeCache.txt")) as f:
414 sequential = [x.strip().split("=")[-1] for x in f.readlines() if "sequential" in x.lower()][-1]
415 multithreading = not Utility.GetBoolean(sequential)
416 except: multithreading = True
417
418 ## Build command for Java applications under Windows
419 win_java_command = "-nologo -O3 -Qopenmp -Qauto_scalar -Qparallel -Qmkl:parallel -Qopt-matmul -fpp -DDEBUG -recursive \
420 -reentrancy:threaded -"+_format+" -extend_source:132 -fpe:0 -heap-arrays1 -iface:cref -libs:static -threads -MT -W1 "
421
422 ## Build command for Fortran applications under Windows
423 win_fortran_command = "-nologo -O3 -QxSSE3 -Qopenmp -Qauto_scalar -Qparallel -Qmkl:parallel -Qopt-matmul -fpp -DDEBUG -recursive \
424 -reentrancy:threaded -"+_format+" -extend_source:132 -fpe:0 -fp:precise -traceback -heap-arrays1 -iface:cref -libs:dll -threads -MD -W1"
425
426 ## Build command for C++ applications under Windows
427 win_cxx_command = "-nologo -Os -W0 -MD -TP -EHsc -Qpar -clr:nostdlib -fp:precise -std:c++latest "
428 win_cxx_command +="-FI pyx_muesli_def.h "
429 win_cxx_command +="-FI algorithm -FI ctime "
430 win_cxx_command +="-FI iso646.h "
431 win_cxx_command +="-FI pyx_muesli_undef.h "
432
433 ## Build command for Python applications under Windows
434 win_pyd_command = ' -DNO_APPEND_FORTRAN '
435 win_pyd_command += ' --f90flags="-'+_format+' -fpe:0 -fp:precise -threads -recursive -Qauto-scalar -Qopenmp -Qmkl:parallel -heap-arrays:1" '
436 win_pyd_command += ' --opt="-O2" --arch="-QaxSSE3 /assume:nounderscore" --quiet --compiler=msvc '
437
438 ## Build command for Python applications under Linux
439 lin_pyd_command = '--quiet --fcompiler=intelem --opt="-O2" --f90flags="-'+_format+' -fp-model precise -fpe0 -recursive -parallel -auto -qopenmp -qopt-matmul -mkl:parallel"'
440
441 ## Build command for Fortran applications under Linux
442 lin_fortran_command = "-fpp -qopenmp -DCLUSTER -DDEBUG -DUSE_MUESLI -fPIC -auto "
443 lin_fortran_command += "-mkl:parallel -extend_source -O2 -"+_format+" -parallel -fpe0 -traceback -recursive "
444 lin_fortran_command += "-nostandard-realloc-lhs"
445
446 # Build command for ABAQUS applications under windows
447 win_abq_command = "ECHO import os >> %ABQ_ENV_FILE% "
448 win_abq_command += "&& ECHO usub_lib_dir=os.getcwd() >> %ABQ_ENV_FILE% "
449 win_abq_command += "&& ( "
450 win_abq_command += "ECHO compile_fortran=compile_fortran[:3] + ["
451 win_abq_command += "'/heap-arrays:1', "
452 win_abq_command += "'/O3', " #<-- Optimization level
453 win_abq_command += "'/fpe:0', " # <-- Floating point exception handling / Prevents division through zero
454 win_abq_command += "'/traceback', " # <-- Traceback errors
455 win_abq_command += "'/Qopt-matmul', "
456 win_abq_command += "'/threads', "
457 ## Some flags are not supported when using the latest Intel Fortran environment
458 # Deactivate everything related to multithreading
459 if not Utility.GetExecutable("oneapi-cli") or multithreading:
460 win_abq_command += "'/MD', " # <-- Multithreading CRT library
461 win_abq_command += "'/Qparallel', " # <-- Use multithreading standard libraries
462 win_abq_command += "'/Qmkl:parallel', " # <-- Use MKL libraries
463 win_abq_command += "'/Qopenmp', " # <-- Use OpenMP library
464 win_abq_command += "'/libs:dll', "
465 else:
466 win_abq_command += "'/Qmkl:sequential', " # <-- Use MKL libraries
467
468 # win_abq_command += "'/warn:all', "
469 # win_abq_command += "'/warn:nousage', "
470 # win_abq_command += "'/warn:interfaces', "
471 # win_abq_command += "'/check:pointer', "
472 # win_abq_command += "'/check:uninit', "
473 # win_abq_command += "'/check:format', "
474 # win_abq_command += "'/check:output_conversion', "
475 # win_abq_command += "'/check:arg_temp_created', "
476 # win_abq_command += "'/check:bounds', " # <-- Array boundary check
477 # win_abq_command += "'/debug:extended', " # <-- Debug function information
478 # win_abq_command += "'/debug-parameters:all', " # <-- Debug parameter information
479 # win_abq_command += "'/Ob0', " # <-- Function inlining
480 # win_abq_command += "'/Zi', " # <-- Debugging # CompLine += "'/watch:all', "
481
482 win_abq_command += "] + compile_fortran[3:] ) >> %ABQ_ENV_FILE% "
483 win_abq_command += "&& ( "
484 if not Utility.GetExecutable("oneapi-cli") or multithreading:
485 win_abq_command += "ECHO link_sl=link_sl[:3] + ["
486 # Overwrite CRT section in ABQ, since its implementation is flawed (or at least is not modifiable enough for our purposes).
487 win_abq_command += "r'/def:"+os.getenv("pyx_abaqus_def","%E")+"', '/INCLUDE:_DllMainCRTStartup', '/FORCE'] + link_sl[3:] + ["
488 else:
489 # Remove shared Intel Fortran core library for multithreading
490 win_abq_command += "ECHO link_sl=['LINK','/NODEFAULTLIB:LIBCMT.LIB','/FORCE','/dll','/def:%E','/out:%U','%F','%A','%L','%B',"
491 # win_abq_command+= "'/DEBUG', " # <-- Debugging
492 # win_abq_command += "'/VERBOSE', " # <-- Show commands
493 win_abq_command += "%pyx_libs%] ) >> %ABQ_ENV_FILE% "
494 win_abq_command += '&& %pyx_abaqus% make -library %pyx_file%'
495
496 lin_per_command = "";
497 # Fetch startup directory (defaults to user's home directory)
498 lin_per_command += 'if [ -z ${HOME+x} ]; then : ; else HOME=${PWD};fi; '
499 # Set working directory
500 lin_per_command += "WorkDir=$PWD;"
501 lin_per_command += "ZipArchive='peridigm.zip';"
502 # The directory where all the magic happens - should not exist in advance
503 lin_per_command += 'TempDir=$HOME/"${ZipArchive%.*}"/;'
504 # Internal directory names
505 lin_per_command += "builddir='build'; srcdir='src';"
506 # File names
507 lin_per_command += "file_cmake='cmake.cmake';"
508 lin_per_command += "file_cmake_log='cmake.log';"
509 lin_per_command += "file_make_log='make.log';"
510 lin_per_command += "file_make_install_log='make_install.log';"
511 lin_per_command += "file_peridigm_bin='Peridigm';"
512 # Number of CPUs for make
513 lin_per_command += "make_cpus=$((`nproc`+1));"
514 # Folder structure
515 lin_per_command += "if [ -d ${TempDir} ]; then"
516 lin_per_command += " echo 'Directory '${TempDir}' already exists. Exit.';"
517 lin_per_command += " exit 0;"
518 lin_per_command += "fi;"
519 # Create folder structure
520 lin_per_command +="mkdir ${TempDir};"
521 lin_per_command +="cd ${TempDir};"
522 lin_per_command +="mkdir ${builddir};"
523 lin_per_command +="mkdir ${srcdir};"
524 lin_per_command +="cd ${srcdir};"
525 # Build from ZIP
526 lin_per_command +="unzip -q ../../${ZipArchive} -d . &&"
527 lin_per_command +="mv peridigm-master peridigm 2>/dev/null &&"
528 lin_per_command +="cd ../${builddir};"
529 lin_per_command +="cd $WorkDir;"
530 lin_per_command+="mv $ZipArchive $TempDir/ &&"
531 lin_per_command +="cd $TempDir/$builddir;"
532 # Create cmake file
533 lin_per_command +="echo 'rm -f CMakeCache.txt' >> ${file_cmake};"
534 lin_per_command +="echo 'rm -rf CMakeFiles/' >> ${file_cmake};"
535 lin_per_command +="echo '' >> ${file_cmake};"
536 lin_per_command +=r"echo 'cmake \' >> ${file_cmake};"
537 lin_per_command +=r"echo '-D USE_MCODAC:BOOL=TRUE \' >> ${file_cmake};"
538 lin_per_command +=r"echo '-D MESH_CONVERTER_DIR:STRING=mesh_converter \' >> ${file_cmake};"
539 lin_per_command +=r"echo '-D CMAKE_BUILD_TYPE:STRING=Release \' >> ${file_cmake};"
540 lin_per_command +=r"echo '-D CMAKE_INSTALL_PREFIX='${TempDir}${builddir}' \' >> ${file_cmake};"
541 lin_per_command +=r"echo '-D CMAKE_CXX_COMPILER:STRING="+'"mpicxx"'+r" \' >> ${file_cmake};"
542 lin_per_command+="echo '-D CMAKE_CXX_FLAGS:STRING="+'"-DUSE_MCODAC -O2 -Wall -std=c++11 -pedantic -Wno-long-long -ftrapv -Wno-deprecated"'+r" \' >> ${file_cmake};"
543 lin_per_command+="echo '-D CMAKE_EXE_LINKER_FLAGS:STRING="+'"${pyx_per_linking:--L${HOME}/mcodac/bin -lperuser}"'+r" \' >> ${file_cmake};"
544 lin_per_command +="echo ${TempDir}${srcdir}'/peridigm/' >> ${file_cmake};"
545 # Execute cmake file
546 lin_per_command +="chmod u+x ${file_cmake};"
547 lin_per_command +="./${file_cmake} > ${file_cmake_log} 2>&1;"
548 # Execute make command
549 lin_per_command +="make -j ${make_cpus} > ${file_make_log} 2>&1;"
550 lin_per_command +="make install > ${file_make_install_log} 2>&1;"
551 # Clean up workspace
552 lin_per_command +="cd bin;"
553 lin_per_command +="cp ${file_peridigm_bin} ${pyx_per_output:-${HOME}}/${file_peridigm_bin};"
554 lin_per_command +="cd $WorkDir;"
555 lin_per_command +="rm -rf $TempDir;"
556
557 lin_tri_command = "";
558 lin_tri_command += "cmake "
559 lin_tri_command += "-DTPL_LAPACK_LIBRARIES='/sw/MPI/GCC/8.2.0-2.31.1/OpenMPI/3.1.3/ScaLAPACK/2.0.2-OpenBLAS-0.3.5/lib/liblapack.a' "
560 lin_tri_command += "-DTPL_ENABLE_HDF5:BOOL=ON -DTPL_ENABLE_Matio=OFF "
561 lin_tri_command += "-DTPL_ENABLE_MPI=ON -DTrilinos_ENABLE_ALL_PACKAGES=ON "
562 lin_tri_command += "-DCMAKE_INSTALL_PREFIX=/home/garb_ma/mcodac/external/trilinos /home/garb_ma/trilinos"
563
564 # Architecture dependencies are related to the PYTHON version used in the build process, thus the check is made here.
565 if ('x86' in _arch):
566 # Custom compiler commands for f2py.
567 win_pyd_command += " --fcompiler=intelv"
568
569 elif ('x64' in _arch):
570 # Custom compiler commands for f2py.
571 win_pyd_command += " --fcompiler=intelvem"
572
573 if make_opt==0:
574 command = win_pyd_command
575 elif make_opt==1:
576 command = win_java_command
577 elif make_opt==2:
578 command = win_fortran_command
579 elif make_opt==3:
580 command = win_cxx_command
581 elif make_opt==4:
582 command = lin_pyd_command
583 elif make_opt==5:
584 command = lin_fortran_command
585 # In dependence to 3rd party FE software
586 elif make_opt==6:
587 command = win_abq_command
588 elif make_opt==7:
589 command = lin_per_command
590 else:
591 raise NotImplementedError
592
593 return command
594
595def GetIncludeDirectory(base_path, key_opt=0, make_opt=0, architecture="x64"):
596 """
597 Return all mandatory include directories for the requested make operation in dependence of key_opt.
598
599 @param: key_opt, make_opt: [An integer value representing the source code file (e.g. MCODAC, BoxBeam, BEOS),
600 An integer value representing the make operation.]
601 @type: key_opt, make_opt: integer
602 """
603 ## Source code files of MCODAC in the correct build order (excluding interface source code file).
604 if key_opt == 0 and make_opt in range(6):
605 try:
606 inc = list(Utility.PathWalk(os.path.join(base_path,"include",Utility.GetPlatform(),architecture), startswith=(".", "__")))[0][1]
607 except IndexError:
608 inc = ""
609 elif key_opt == 8:
610 ## Add base path to overall search path (if valid and existing)
611 if os.path.exists(base_path):
612 sys.path.append(base_path)
613 ## Access required packages
614 try:
615 # Check for python OCC
616 import OCC
617 # Get qualified package name
618 __occ_name = OCC.__name__
619 # Get absolute package paths
620 __occ_src_path = OCC.__path__[0]
621 except ImportError:
622 # OCC is not part of the current environment
623 __occ_name = ""; __occ_src_path = ""
624 try:
625 # Import PyCODAC to build library locally during setup.
626 import PyCODAC
627 # Get qualified package name
628 __pyc_name = PyCODAC.__name__
629 # Get absolute package paths
630 __pyc_src_path = PyCODAC.PyCODACPath
631 except ImportError:
632 # This script is not executed as plug-in for PyCODAC
633 __pyc_name = ""; __pyc_src_path = ""
634 pass
635 ## Add dependencies
636 inc = ["six.py", "ipykernel_launcher.py",
637 # Add PyCODAC packages
638 os.path.join(__pyc_name,"__init__.py"),
639 os.path.join(__pyc_name,"Paths.log"),
640 os.path.join(__pyc_name,"Tools","__init__.py"),
641 os.path.join(__pyc_name,"Tools","Utility.py"),
642 os.path.join(__pyc_name,"Core","cmd"),
643 # Add compiled binaries
644 os.path.join(__pyc_name,"Core","bin",Utility.GetPlatform(),architecture),
645 os.path.join(__pyc_name,"Study","bin",architecture),
646 os.path.join(__pyc_name,"Study","cmd",Utility.GetPlatform()),
647 # Add PyCODAC databases
648 os.path.join(__pyc_name,"Database"),
649 # Add JupyterLab configuration and application files
650 os.path.join(__pyc_name,"Plugin","JupyterLab","bin", Utility.GetPlatform()),
651 os.path.pathsep.join([os.path.join(__pyc_src_path,"Plugin","JupyterLab","config","jupyter_notebook_utils.py"),"."]),
652 os.path.pathsep.join([os.path.join(__pyc_src_path,"Plugin","JupyterLab","config"),os.path.join(__pyc_name,"Plugin","JupyterLab","config",".")]),
653 # Add configuration file for Smetana
654 os.path.join(__pyc_name,"Plugin","Smetana","config","pyc_smetana.config"),
655 # Add images directly to executable
656 os.path.pathsep.join([os.path.join(__pyc_src_path, "GUI","res","pyc_lab_icon.png"), os.path.join(__pyc_name, "GUI", "res",".")]),
657 os.path.pathsep.join([os.path.join(__pyc_src_path, "GUI","res","pyc_dic_analysis.png"),os.path.join(__pyc_name, "GUI", "res",".")]),
658 os.path.pathsep.join([os.path.join(__pyc_src_path, "GUI","res","stmlab_icon.png"), os.path.join(__pyc_name, "GUI", "res",".")]),
659 os.path.pathsep.join([os.path.join(__pyc_src_path, "GUI","res","stm_main.png"),os.path.join(__pyc_name, "GUI", "res",".")]),
660 os.path.pathsep.join([os.path.join(__pyc_src_path, "GUI","res","stm_lightweight.png"),os.path.join(__pyc_name, "GUI", "res",".")]),
661 os.path.pathsep.join([os.path.join(__pyc_src_path, "GUI","res","stm_vph.png"), os.path.join(__pyc_name, "GUI", "res",".")])]
662
663 # Added OCC module explicitly
664 if os.path.exists(__occ_src_path): inc.insert(3,os.path.pathsep.join([__occ_src_path,os.path.join(__occ_name,".")]))
665
666 try:
667 # Compatibility support for latest version of JupyterLab
668 from PyCODAC import __hook__
669 ## Add named modules in site package explicitly
670 imported = set([x for x in dir(__hook__) if not x.startswith("_")]) - set(dir(__builtins__)) - set(["PyCODAC","PyXMake"])
671 data = [importlib.import_module(x) for x in imported]
672 inc.extend([os.path.pathsep.join([os.path.dirname(x.__file__),os.path.join(x.__name__,".")]) if x.__file__.endswith("__init__.py") else Utility.PathLeaf(x.__file__) for x in data])
673 ## Add all metadata information as well (forward compatibility)
674 dist_search_paths = set([os.path.abspath(os.path.join(x.__file__, os.path.pardir, os.path.pardir)) if x.__file__.endswith("__init__.py") else os.path.abspath(os.path.join(x.__file__, os.path.pardir)) for x in data])
675 inc.extend([os.path.pathsep.join([os.path.join(path,Utility.PathLeaf(y)),os.path.join(Utility.PathLeaf(y),".")]) for path in dist_search_paths for y in os.listdir(path) for found in data if y.startswith(found.__name__) and y.endswith(".dist-info")])
676 except Exception as _: pass
677 else:
678 inc = []
679 return inc
680
681def GetLinkDependency(key_opt=0, make_opt=0, architecture="x64"):
682 """
683 Return all mandatory external libraries for the requested make operation in dependence of key_opt.
684
685 @param: key_opt, make_opt: [An integer value representing the source code file (e.g. MCODAC, BoxBeam, BEOS),
686 An integer value representing the make operation.]
687 @type: key_opt, make_opt: integer
688 """
689 ## Source code files of MCODAC in the correct build order (excluding interface source code file).
690 if key_opt == 0 and make_opt in range(6):
691 lib = [x + architecture for x in ["interp", "pchip", "bbeam","muesli","dispmodule","toms"]]
692 elif key_opt == 8:
693 ## Add additional libraries
694 lib = [x for x in sorted(set(sys.path), key=lambda x: 'site-packages' in x)]
695 else:
696 lib = []
697 return lib
698
699def GetEnvironment(env_opt=0):
700 """
701 Return predefined source environment to initiate a build process.
702
703 @param: env_opt An integer value representing a predefined environment
704 @type: env_opt: integer
705 """
706 if env_opt == 0:
707 ## Return Intel Fortran environment (DLR institute cluster)
708 env = [posixpath.join(Utility.AsDrive("cluster",posixpath.sep),"software","slurm","etc","env.d","ifort2016.sh")]
709 elif env_opt == 1:
710 ## Return Intel Fortran environment & PYTHON 2.7 (DLR institute cluster)
711 env = [posixpath.join(Utility.AsDrive("cluster",posixpath.sep),"software","slurm","etc","env.d","ifort2016.sh"),
712 posixpath.join(Utility.AsDrive("cluster",posixpath.sep),"software","python2.7","env.sh")],
713 elif env_opt ==2:
714 ## Return Intel Fortran environment & PYTHON 3.5 (DLR institute cluster)
715 env = [posixpath.join(Utility.AsDrive("cluster",posixpath.sep),"software","slurm","etc","env.d","ifort2016.sh"),
716 posixpath.join(Utility.AsDrive("cluster",posixpath.sep),"software","python3.5","env.sh")],
717 elif env_opt ==3:
718 ## Return Intel Fortran environment & PYTHON 3.x (DLR institute cluster)
719 env = [posixpath.join(Utility.AsDrive("cluster",posixpath.sep),"software","slurm","etc","env.d","ifort2016.sh"),
720 posixpath.join(Utility.AsDrive("cluster",posixpath.sep),"software","mcodac","anaconda","envs","Conda36","env.sh")],
721 elif env_opt == 4:
722 ## Return Intel Fortran environment & ABAQUS environment (DLR's HPC cluster; CARA)
723 # Use a more module environments instead of source files.
724 env =["module purge; module load iccifort; module load iimpi; module load ABAQUS/2023"]
725 elif env_opt ==5:
726 ## Return environment required to build Trilinos on DLR's HPC cluster.
727 # Order is important, since some modules cannot be loaded later.
728 env = ["module purge; module load GCC; module load OpenMPI; module load OpenBLAS; module load netCDF; \
729 module load Boost; module load ScaLAPACK; module load GCCcore/8.3.0; module load HDF5; \
730 module load GCCcore/10.2.0; module load CMake/3.18.4;"]
731 else:
732 raise NotImplementedError
733 return env
734
735# Silently install all dependencies on the first start
736if not getattr(sys, 'frozen', False): subprocess.check_call([sys.executable,os.path.join(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))),"__install__.py")])
737
738# Create useful names for import. Each script acts also as a minimum working example.
739try:
740 # Use lazy import when running as CLI
741 if os.isatty(sys.stdout.fileno()) and os.path.exists(os.path.dirname(__file__)): raise RuntimeError
742 # Probe import capabilities
743 from .doxygen import main as _
744except (ImportError, RuntimeError, io.UnsupportedOperation) as _:
745 # Register all functions dynamically.
746 for x in os.listdir(os.path.dirname(__file__)):
747 if not x.endswith(".py") or x.startswith("_"): continue
748 func = x.split(".")[0]
749 exec("""def %s(*args, **kwargs): mod = importlib.import_module('.%s',__name__); return mod.main(*args, **kwargs)""" % (func, func))
750else:
751 # Build documentation
752 from .doxygen import main as doxygen
753 from .sphinx import main as sphinx
754 from .latex import main as latex
755 # API builds
756 if sys.version_info >= (3,7) and os.path.exists(os.path.join(Utility.GetPyXMakePath(),"API")):
757 from .api import main as api
758 # Dependency builds
759 from .pyreq import main as pyreq
760 # Bundle builds
761 from .app import main as app
762 from .bundle import main as bundle
763 from .archive import main as archive
764 from .openapi import main as openapi
765 # Host builds
766 from .abaqus import main as abaqus
767 from .cmake import main as cmake
768 from .ifort import main as ifort
769 from .gfortran import main as gfortran
770 from .java import main as java
771 from .py2x import main as py2x
772 from .cxx import main as cxx
773 # Remote builds
774 from .ssh_ifort import main as ssh_ifort
775 from .ssh_f2py import main as ssh_f2py
776 from .ssh_make import main as ssh_make
777 # Utilities
778 from .stm_svn2git import main as svn2git
779 # Unit testing
780 from .coverage import main as coverage
781
782## Collect compatibility alias from 3rd party extensions module
783try: from PyXMake.Plugin import bdist_wheel as build #@UnresolvedImport
784except: pass
785
786## Adding compatibility alias for f2py. py2x as a name is deprecated in future releases.
787# It remains solely for backwards compatibility.
788try: setattr(sys.modules[__name__],"f2py", getattr(sys.modules[__name__],"py2x"))
789except: pass
790
791## Backwards compatibility for function calls
792setattr(sys.modules[__name__],"run", getattr(Command,"run"))
793setattr(sys.modules[__name__],"main",getattr(Command,"main"))
794
795if __name__ == '__main__':
796 pass
Parent class inherited from built-in exception.
run(cls, namespace=None, **kwargs)
Definition __init__.py:178
__init__(self, *args, **kwargs)
Definition __init__.py:54
str CommandObjectKind
String identifier of current instance.
Definition __init__.py:60
alias(*args, **kwargs)
Definition __init__.py:63
verify(cls, selection, **kwargs)
Definition __init__.py:157
main(cls, **kwargs)
Definition __init__.py:273
cli(cls, *args, **kwargs)
Definition __init__.py:72
parse(cls, command, config, args)
Definition __init__.py:91
Imports additional features provided by and for 3rd party packages.
Definition __build.py:1
GetPreprocessingCommand(pre_opt=0)
Definition __init__.py:380
GetBuildCommand(make_opt=0, _format="fixed", _arch=Utility.GetArchitecture())
Definition __init__.py:404
GetSourceCode(key_opt=0)
Definition __init__.py:311
GetEnvironment(env_opt=0)
Definition __init__.py:699
GetLinkDependency(key_opt=0, make_opt=0, architecture="x64")
Definition __init__.py:681
GetIncludeDirectory(base_path, key_opt=0, make_opt=0, architecture="x64")
Definition __init__.py:595