diff --git a/docs/examples/0_introduction.md b/docs/examples/0_introduction.md deleted file mode 100644 index 1cc4a4b..0000000 --- a/docs/examples/0_introduction.md +++ /dev/null @@ -1,8 +0,0 @@ -# Introduction - -This is a collection of examples of how to use the FEDEM python api. - - -The examples are partly presentations of models and partly models that can be downloaded and executed. diff --git a/docs/examples/F_digital_twins.md b/docs/examples/F_digital_twins.md deleted file mode 100644 index 3986017..0000000 --- a/docs/examples/F_digital_twins.md +++ /dev/null @@ -1,232 +0,0 @@ ---- -title: Digital Twins - -vis modellen i brukergrensesnittet med exkterne funksjoner og sensorer - - -vis et script som lytter etter data fra en disk område ---- -# High Fidelity Digital Twins - -External functions and virtual sensors and a first introduction to the Digital Twin concept using fedempy - -## Introduction -The purpose of this Section is to introduce the concept of Digital Twins and how to use fedempy to create Digital Twins. - -``` mermaid -graph LR - A[Start] --> B{Error?}; - B -->|Yes| C[Hmm...]; - C --> D[Debug]; - D --> B; - B ---->|No| E[Yay!]; -``` - - -## Load driven Digital Twin -Kjøring av digitale tvilling med minimal fedempy avtrykk -```python -""" -00-loader-Base_case - -Low code version of the Loader model - -Developed by Knut Morten Okstad and refatured by Oeystein Stranden -""" -# from os import environ, path -# from sys import argv -from pathlib import Path - -from fedempy.fmm_solver import FmmSolver -from fedempy.modeler import FedemModeler -from fedempy.enums import FmDof, FmDofStat, FmLoadType, FmType, FmVar - -from _clean_path import clean_path - -# Global constants -RELATIVE_PATH = '00-loader-Base_case/' -MODEL_FILE = '00-loader-Base_case.fmm' -PARTS_PATH = 'parts/' - -# Prepare run directory (=RELATIVE_PATH) -model_file = Path(RELATIVE_PATH) / MODEL_FILE -# parts_path = '../'*RELATIVE_PATH.count('/') + PARTS_PATH -clean_path(model_file.parent) - -my_model = FedemModeler(str(model_file), force_new=True) - -p1 = my_model.make_fe_part(PARTS_PATH + 'Front.flm') -p2 = my_model.make_fe_part(PARTS_PATH + 'Boom.flm') -p3 = my_model.make_fe_part(PARTS_PATH + 'Bucket.flm') -p4 = my_model.make_fe_part(PARTS_PATH + 'BellCrank.flm') -p5 = my_model.make_fe_part(PARTS_PATH + 'BucketLink.flm') -my_model.edit_part([p1, p2, p3, p4, p5], - alpha2=0.00286, component_modes=0, consistent_mass=True) - -my_model.edit_part(p2, Tx=0.01080263, Tz=-0.77487206) -my_model.edit_part(p3, Tx=-0.64636636, Tz=-2.0328088, Ry=-30, Rz=-180) -my_model.edit_part(p4, Tx=-3.2499752, Ty=-2.8376081, Tz=0.04694241, Ry=21.814096) -my_model.edit_part(p5, Tx=-2.041544, Ty=-0.92750001, Tz=0.12191465, Ry=-4.9156169) - -# Create revolute joints coupling the FE parts together and to ground -joints = [] -joints.append(my_model.make_joint('Rev1', FmType.REVOLUTE_JOINT, 8)) -joints.append(my_model.make_joint('Rev2', FmType.REVOLUTE_JOINT, 11)) -joints.append(my_model.make_joint('Rev3', FmType.REVOLUTE_JOINT, 19, 7)) -joints.append(my_model.make_joint('Rev4', FmType.REVOLUTE_JOINT, 24, 6)) -joints.append(my_model.make_joint('Rev5', FmType.REVOLUTE_JOINT, 38, 22)) -joints.append(my_model.make_joint('Rev6', FmType.REVOLUTE_JOINT, 33, 17)) -joints.append(my_model.make_joint('Rev7', FmType.REVOLUTE_JOINT, 49, 21)) -joints.append(my_model.make_joint('Rev8', FmType.REVOLUTE_JOINT, 48, 20)) -joints.append(my_model.make_joint('Rev9', FmType.REVOLUTE_JOINT, 56, 47)) -joints.append(my_model.make_joint('Rv10', FmType.REVOLUTE_JOINT, 55, 35)) - -my_model.edit_joint(joints[2], Ry=90, Rz=-90) -[my_model.edit_joint(id, Rx=90) for id in joints[3:]] - - -f1 = my_model.make_function('Lift Cylinders', - frequency=0.625, amplitude=0.15, delay=0.25, mean_value=0.15, end=0.8) - -f2 = my_model.make_function('Boom Cylinder', - slope=-0.5, start_ramp=0.5, end_ramp=1.2) - -f3 = my_model.make_function('Rotation Front', - frequency=0.5, amplitude=0.262, delay=0.25, mean_value=0.262, end=1.0) - -f4 = my_model.make_function('Bucket Load', - slope=50000, start_ramp=0.7, end_ramp=1.2) - -my_model.edit_joint(joints[0], constraints={'Rz' : FmDofStat.SPRING}, - spring={'Rz' : 1.0e9}, length={'Rz' : f3}) - -cyl = my_model.make_spring('Lift cylinder', [(9, 23), (10, 18)], - init_Stiff_Coeff=1.0e9, length=f1) - -cyl.append(my_model.make_spring('Boom cylinder', (5, 50), - init_Stiff_Coeff=1.0e9, length=f2)) - -my_model.make_load('Load', FmLoadType.FORCE, 39, (0, 0, -1), fn=f4) - -my_model.fm_solver_setup(t_inc=0.01, t_end=1.6) -my_model.save() -my_model.close() - -# Solver setup and execution -my_solver = FmmSolver() -my_solver.solve_all(str(model_file), True, True) - -my_solver.solver_close() -my_solver.close_model(False) -``` - -## Response driven Digital Twin -```yaml -target_file: 02-loader-y100.fmm -file_exists: OVERWRITE! - -fe_parts: - p1: [parts/Front.ftl] - p2: [parts/Boom.ftl] - p3: [parts/Bucket.ftl] - p4: [parts/BellCrank.ftl] - p5: [parts/BucketLink.ftl] - -triads_from_fe_parts: - Front_fix_bottom: [Front bottom bearing housing to ground, p1, node, 441] - front_fix_top: [Front top bearing housing to ground, p1, node, 211] - front_right_housing: [Front right bearing housing, p1, node, 38] - front_left_housing: [Front left bearing housing, p1, node, 58] - front_top_cylinder_attachment: [Front top cylinder attachment point, p1, node, 18] - front_right_cylinder_attachment: [Front right cylinder attachment point, p1, node, 222] - front_left_cylinder_attachment: [Front left cylinder attachment point, p1, node, 235] - - boom_rear_right_houding: [Boom right rear bearing housing, p2, node, 1038] - boom_rear_left_housing: [Boom left rear bearing housing, p2, node, 178] - boom_front_right_housing: [Boom right front bearing housing, p2, node, 715] - boom_front_left_housing: [Boom left front bearing housing, p2, node, 5] - boom_right_bellcrank_housing: [Boom right bearing housing to BellCrank, p2, node, 545] - boom_left_bellcrank_housing: [Boom left bearing housing to BellCrank, p2, node, 424] - boom_right_cylinder_attachment: [Boom right cylinder attachment point, p2, node, 829] - boom_left_cylinder_attachment: [Boom left cylinder attachment point, p2, node, 119] - - bucket_right_housing: [Bucket right housing, p3, node, 509] - bucket_mid_housing: [Bucket midt housing, p3, node, 451] - bucket_left_housing: [Bucket left housing, p3, node, 284] - - bellcrank_top_housing: [BellCrank top housing, p4, node, 420] - bellcrank_bottom_housing: [BellCrank bottom housing, p4, node, 14] - bellcrank_right_housing: [BellCrank right housing, p4, node, 276] - bellcrank_left_housing: [BellCrank left housing, p4, node, 244] - - bucketlink_rear_houding: [BucketLink rear housing, p5, node, 575] - bucketlink_front_housing: [BucketLink front housing, p5, node, 174] - -edit_fe_parts: - p1: &link_property - alpha2: 0.00286 - component_modes: 0 - consistent_mass: True - p2: - <<: *link_property - Tx: 0.01080263 - Tz: -0.77487206 - - p3: - <<: *link_property - Tx: -0.64636636 - Tz: -2.0328088 - Ry: -30 - Rz: -180 - p4: - <<: *link_property - Tx: -3.2499752 - Ty: -2.8376081 - Tz: 0.04694241 - Ry: 21.814096 - p5: - <<: *link_property - Tx: -2.041544 - Ty: -0.92750001 - Tz: 0.12191465 - Ry: -4.9156169 - -joints: - j01: [Front bottom to ground, BALL_JOINT, Front_fix_bottom] - j02: [Front top to ground, BALL_JOINT, front_fix_top] - - j03: [Front to boom right, BALL_JOINT, front_right_housing, boom_rear_right_houding] - j04: [Front to boom left, BALL_JOINT, front_left_housing, boom_rear_left_housing] - - j05: [Boom to bucket right, BALL_JOINT, boom_front_right_housing, bucket_right_housing] - j06: [Boom to bucket left, BALL_JOINT, boom_front_left_housing, bucket_left_housing] - - j07: [Bucket link rear, BALL_JOINT, bucketlink_rear_houding, bellcrank_bottom_housing] - j08: [Bucket link front, BALL_JOINT, bucketlink_front_housing, bucket_mid_housing] - - j09: [Boom to bellCrank right, BALL_JOINT, boom_right_bellcrank_housing, bellcrank_right_housing] - j10: [Boom to BellCrank left, BALL_JOINT, boom_left_bellcrank_housing, bellcrank_left_housing] - -hydraulic_cylinders: # User-defined function - Top_cylinder: [Top cylinder, front_top_cylinder_attachment, - bellcrank_top_housing, 0.15, 0.12, 0.05, 0.7, 0.7, 1.0e+4] - - Right_cylinder: [Right cylinder, front_right_cylinder_attachment, - boom_right_cylinder_attachment, 0.15, 0.12, 0.05, 0.7, 0.7, 1.0e+6] - - Left_cylinder: [Left cylinder, front_left_cylinder_attachment, - boom_left_cylinder_attachment, 0.15, 0.12, 0.05, 0.7, 0.7, 1.0e+6] - -fedem_objects: - p10: - kind: PART - ORIGINAL_FE_FILE: parts/Front.ftl - - COORDINATE_SYSTEM: - - [1.0, 0.0, 0.0, 1.0] - - [0.0, 1.0, 0.0, 1.0] - - [0.0, 0.0, 1.0, 1.0] - - DESCR: Front no. 2 | tagging_2 - -``` diff --git a/docs/examples/digital_twins.md b/docs/examples/digital_twins.md new file mode 100644 index 0000000..dae9f22 --- /dev/null +++ b/docs/examples/digital_twins.md @@ -0,0 +1,229 @@ +# Digital Twins models + +The concept of Digital Twins of a physical asset can be modelled in FEDEM +where loads and/or actuators get their input from external sources, such as +sensors on the real asset, and produce real-time simulated response data. +The model can then be wrapped in a FMU (Functional Mockup Unit) based on the +[FMI-standard](https://fmi-standard.org) for use in a co-simulation environment. + +To demonstrate how this can be done, we consider a simple car front suspension +model consisting of three FE parts: + +* Lower control arm +* Upper control arm +* Knuckle + +The three FE parts are connected using Ball joint objects, +while an Axial Spring and a Damper are used to represent the physical damper. + +![car suspension](../images/sla_model.png) + +## Digital twin input + +A vertical external force is applied in the wheel hub Triad in the model. +The force magnitude is defined using an External function object in FEDEM +(indicated by the "**E**" icon in the Objects tree in the image above). +As opposed to all the other Function types which can use various response +quantities (including the time itself) as function argument, External functions +are typically assigned their value by the calling process managing the time step +loop of the numerical simulation. + +## Digital twin output + +In this model we want to monitor the force and deflection in the damper, +in addition to the vertical deflection of the steering pin Triad. +For this purpose, three other Function objects are defined simply as 1-to-1 +functions, where the respective response quantities are defined as arguments. +These three functions (marked with the "**S**" icon in the Objects three) then +serve as virtual sensors which can be evaluated by the calling process during +the time step loop. + +## Creating the digital twin using fedempy + +Here is a low-code python script creating the car suspension model shown above. +The FE models used are found [here](linked_files/sla_FEparts.zip). + +[Download...](linked_files/sla_modeling.py) + +```python +from os import mkdir, path +from pathlib import Path + +from fedempy.modeler import FedemModeler +from fedempy.enums import FmDof, FmDofStat, FmType, FmVar + +# Global constants +RELATIVE_PATH = '02-car-front-suspension/' +MODEL_FILE = '02-sla-dtwin.fmm' +PARTS_PATH = 'parts/' + +# Prepare run directory (=RELATIVE_PATH) +model_file = Path(RELATIVE_PATH) / MODEL_FILE +if not path.isdir(model_file.parent): + mkdir(model_file.parent) + +# Create a new FEDEM model +my_model = FedemModeler(str(model_file), force_new=True) + +# Load the FE parts +lca = my_model.make_fe_part(PARTS_PATH + 'lca.nas') +knuckle = my_model.make_fe_part(PARTS_PATH + 'knuckle.nas') +uca = my_model.make_fe_part(PARTS_PATH + 'uca.nas') +ground = 2 # base id of the reference plane will always be 2 + +# Lower control arm joints to ground +j1 = my_model.make_joint('Fix 1', FmType.BALL_JOINT, + my_model.make_triad('lca fixed', node=11911, on_part=lca)) +j2 = my_model.make_joint('Fix 2', FmType.BALL_JOINT, + my_model.make_triad('lca fixed', node=11912, on_part=lca)) + +# Knuckle to lower control arm joint +j3 = my_model.make_joint('Lower Ball', FmType.BALL_JOINT, + my_model.make_triad('knuckle lower', node=3, on_part=knuckle), + my_model.make_triad('lca tip', node=11910, on_part=lca)) + +# Upper control arm to knuckle joint +j4 = my_model.make_joint('Upper Ball', FmType.BALL_JOINT, + my_model.make_triad('uca tip', node=2160, on_part=uca), + my_model.make_triad('knuckle upper', node=4, on_part=knuckle)) + +# Upper control arm joints to ground +j5 = my_model.make_joint('Fix 3', FmType.BALL_JOINT, + my_model.make_triad('uca fixed', node=2159, on_part=uca)) +j6 = my_model.make_joint('Fix 4', FmType.BALL_JOINT, + my_model.make_triad('uca fixed', node=2158, on_part=uca)) + +# Steering pin triad +t0 = my_model.make_triad('Steering pin', node=2, on_part=knuckle) +my_model.edit_triad(t0, constraints={'Tx' : FmDofStat.FIXED}) + +# Damper +t1 = my_model.make_triad('Damper pin', node=11909, on_part=lca) +t2 = my_model.make_triad('Damper ground', pos=(0.0, 0.0, 0.3), on_part=ground) +s1 = my_model.make_spring('Damper', (t1, t2), init_Stiff_Coeff=7.5e6) +d1 = my_model.make_damper('Damper', (t1, t2), init_Damp_Coeff=3.0e3) + +# Wheel hub triad with external force +t3 = my_model.make_triad('Wheel hub', node=1, on_part=knuckle) +my_model.edit_triad(t3, load={'Tz' : my_model.make_function('Wheel force')}) + +# Ouput sensors +o1 = my_model.make_sensor('Damper force', (s1, d1), FmVar.FORCE) +o2 = my_model.make_sensor('Damper deflection', s1, FmVar.DEFLECTION) +o3 = my_model.make_sensor('Steering pin deflection', t0, FmVar.POS, FmDof.TZ) + +my_model.fm_solver_setup(t_inc=0.005, t_end=2.5, t_quasi=-1.0) +my_model.fm_solver_tol(1.0e-6,1.0e-6,1.0e-6) +my_model.close(True, True) +``` + +## Exporting a FMU from FEDEM + +Before the model can be exported, the FE models need to be reduced into +superelements since the FMU will only conduct the dynamics simulation +of the mechanism model. This can be done with `fedempy` using: + + $ python -m fedempy.fmm_solver -f 02-sla-dtwin.fmm --reduce-only --save-model + +Alternatively, you can open the generated model in the FEDEM GUI and perform the +model reduction there. + +To export the FMU, open the model in the GUI (and perform the model reduction, +if not already done so), and open the Model Export dialog box shown below. +You find this in the *File* menu (*File --> Export --> Export Digital Twin...*). + +![FMU export](../images/fedem-fmu-export.png) + +This dialog box shows three alternative ways of exporting the model, +but only the **FMU** option is relevant here. So enable that toggle. +Then use the **Browse...** button to selected the name for the fmu-file. +In the right side of the dialog, you find a list of the input- and output +indicators in the model, corresponding to the external functions and output +sensors defined in the modelling script. + +Press the **Export** button to generate the fmu, and thenexit the FEDEM GUI. + +## Testing the FMU + +To verify that the created FMU works, you can use a tool such as +[FMPy](https://github.com/CATIA-Systems/FMPy) which also has a GUI from where +you can controll the simulation. It is also convenient to make a python script +to run it from console or to integrate with a larger simulation environment. + +We here present a simple python driver, which just runs though the simulation +as it is set up in the model. The script takes the input for each time step +from a specified input file, and prints the output sensor values to the console. +Use this as a template for more advanced co-simulation tasks with FEDEM FMUs. + +[Download...](linked_files/run_fedem_fmu.py) + +```python +from fmpy import fmi2, read_model_description, extract +from pandas import read_csv +from sys import argv + + +def run(fmu_file, input_file=None, instance_name="my instance"): + """ + Runs the specified FMU + """ + # Read the model description + print("Reading model description from", fmu_file) + model = read_model_description(fmu_file) + + # Extract the FMU itself + print("Extracting", fmu_file, "...") + unzipdir = extract(fmu_file) + + # Setup the FMU + print("Setup the FMU", model.coSimulation.modelIdentifier) + fmu = fmi2.FMU2Slave(guid=model.guid, unzipDirectory=unzipdir, + modelIdentifier=model.coSimulation.modelIdentifier, + instanceName=instance_name) + fmu.instantiate() + fmu.enterInitializationMode() + fmu.exitInitializationMode() + + # Get some size parameters from the FMU + num_params = fmu.getInteger([1, 2]) + num_inputs = num_params[0] # Number of input sensors + num_output = num_params[1] # Number of output sensors + + # Read the input values into a Dataframe + if num_inputs > 0 and input_file is not None: + inputs = read_csv(input_file, sep="\t") + + # List of external function indices + inpIdx = range(num_inputs) + # List of output sensor indices + outIdx = range(num_inputs,num_inputs+num_output) + + # Time loop, this will run through the FEDEM simulation + # using the time domain setup in the model file used to export the FMU. + # The two parameters to the doStep() call are dummies (time and step size). + # They are not used in the FMU so the values are arbitrary (2*0.0 is fine). + istep = 0 + while not fmu.getBooleanStatus(fmi2.fmi2Terminated): + if num_inputs > 0: # Set the external function values for this step + fmu.setReal([*inpIdx], inputs.iloc[istep]) + fmu.doStep(0.0, 0.0) + time = fmu.getRealStatus(fmi2.fmi2LastSuccessfulTime) + output = fmu.getReal([*outIdx]) + istep += 1 + print(f"Here are the outputs at step={istep} time={time}:", output) + + # Finished, close down + fmu.terminate() + fmu.freeInstance() + + +if __name__ == "__main__": + if len(argv) > 3: + run(argv[1], argv[2], argv[3]) + elif len(argv) > 2: + run(argv[1], argv[2]) + elif len(argv) > 1: + run(argv[1]) + else: + print(f"Usage: {argv[0]} [] []") +``` diff --git a/docs/examples/introduction.md b/docs/examples/introduction.md new file mode 100644 index 0000000..8960d92 --- /dev/null +++ b/docs/examples/introduction.md @@ -0,0 +1,7 @@ +# Introduction + +This is a collection of examples on how to use the FEDEM python api to create and edit +mechanism models, and to perform the numerical simulation. + +The examples are partly presentations of models, +and partly models that can be downloaded and simulated. diff --git a/docs/examples/linked_files/loader_FEparts.zip b/docs/examples/linked_files/loader_FEparts.zip new file mode 100644 index 0000000..9baa6b5 Binary files /dev/null and b/docs/examples/linked_files/loader_FEparts.zip differ diff --git a/docs/examples/linked_files/loader_modeling_and_solving.py b/docs/examples/linked_files/loader_modeling_and_solving.py index 1e89939..5a5ce2a 100644 --- a/docs/examples/linked_files/loader_modeling_and_solving.py +++ b/docs/examples/linked_files/loader_modeling_and_solving.py @@ -1,140 +1,140 @@ ''' -01-loader-With_triads_specifications +01-loader-with_triads_specifications -Low code version of the Loader model similar as the 00-loader-Base_case, -but where the triads specifications are embeded in the script +Low-code version of the Loader model similar to the 00-loader-base_case, +but where the triad specifications are embedded in the script. Developed by Oeystein Stranden ''' +from os import mkdir, path from pathlib import Path from fedempy.fmm_solver import FmmSolver from fedempy.modeler import FedemModeler -from fedempy.enums import FmDof, FmDofStat, FmLoadType, FmType, FmVar -from fedempy2.modeler2 import Model as FedemModeler2, get_node -from _clean_path import clean_path +from fedempy.enums import FmDofStat, FmLoadType, FmType # Global constants -RELATIVE_PATH = '01-loader-With_triads_specifications/' -MODEL_FILE = '01-loader-With_triads_specifications.fmm' +RELATIVE_PATH = '01-loader-with_triads_specifications/' +MODEL_FILE = '01-loader-with_triads_specifications.fmm' PARTS_PATH = 'parts/' -# Prepare run directory (=RELATIVE_PATH) -model_file = Path(RELATIVE_PATH) / MODEL_FILE -clean_path(model_file.parent) - - -my_model = FedemModeler(str(model_file), force_new=True) - -# Note that the ftl part files do not contain the triad markers -files = [ +# Note that the ftl-files do not contain the triad markers +FILES = [ PARTS_PATH + 'Front.ftl', PARTS_PATH + 'Boom.ftl', PARTS_PATH + 'Bucket.ftl', PARTS_PATH + 'BellCrank.ftl', - PARTS_PATH + 'BucketLink.ftl'] + PARTS_PATH + 'BucketLink.ftl', +] + +# Prepare run directory (=RELATIVE_PATH) +model_file = Path(RELATIVE_PATH) / MODEL_FILE +if not path.isdir(model_file.parent): + mkdir(model_file.parent) + +# Create a new FEDEM model +my_model = FedemModeler(str(model_file), force_new=True) -parts = [my_model.make_fe_part(f) for f in files] +# Load the FE parts +parts = [my_model.make_fe_part(f) for f in FILES] +# Edit some part properties my_model.edit_part(parts, alpha2=0.00286, component_modes=0, consistent_mass=True) -# Functions +# Create functions f1 = my_model.make_function('Lift Cylinders', - frequency=0.625, amplitude=0.15, delay=0.25, mean_value=0.15, end=0.8) + frequency=0.625, amplitude=0.15, delay=0.25, mean_value=0.15, end=0.8) f2 = my_model.make_function('Boom Cylinder', - slope=-0.5, start_ramp=0.5, end_ramp=1.2) + slope=-0.5, start_ramp=0.5, end_ramp=1.2) f3 = my_model.make_function('Rotation Front', - frequency=0.5, amplitude=0.262, delay=0.25, mean_value=0.262, end=1.0) + frequency=0.5, amplitude=0.262, delay=0.25, mean_value=0.262, end=1.0) f4 = my_model.make_function('Bucket Load', - slope=50000, start_ramp=0.7, end_ramp=1.2) - + slope=50000, start_ramp=0.7, end_ramp=1.2) # Front top joint to ground -t0t = my_model.make_triad('Front_fix_bottom', get_node(files[0], 'node', 441), on_part=parts[0]) +t0t = my_model.make_triad('Front_fix_bottom', node=441, on_part=parts[0]) j01 = my_model.make_joint('Rev1', FmType.REVOLUTE_JOINT, t0t) # Front bottom joint to ground -t0b = my_model.make_triad('front_fix_top', get_node(files[0], 'node', 211), on_part=parts[0]) +t0b = my_model.make_triad('front_fix_top', node=211, on_part=parts[0]) j02 = my_model.make_joint('Rev2', FmType.REVOLUTE_JOINT, t0b) # Front to boom joint on right -t0r = my_model.make_triad('front_right_housing', get_node(files[0], 'node', 38), on_part=parts[0]) -t1r = my_model.make_triad('boom_rear_right_houding', get_node(files[1], 'node', 1038), on_part=parts[1]) +t0r = my_model.make_triad('front_right_housing', node=38, on_part=parts[0]) +t1r = my_model.make_triad('boom_rear_right_houding', node=1038, on_part=parts[1]) j03 = my_model.make_joint('Rev3', FmType.REVOLUTE_JOINT, t0r, t1r) # Front to boom joint on left -t0l = my_model.make_triad('front_left_housing', get_node(files[0], 'node', 58), on_part=parts[0]) -t1l = my_model.make_triad('boom_rear_left_housing', get_node(files[1], 'node', 178), on_part=parts[1]) +t0l = my_model.make_triad('front_left_housing', node=58, on_part=parts[0]) +t1l = my_model.make_triad('boom_rear_left_housing', node=178, on_part=parts[1]) j04 = my_model.make_joint('Rev4', FmType.REVOLUTE_JOINT, t0l, t1l) # Top cylinder -t0t = my_model.make_triad('front_top_cylinder_attachment', get_node(files[0], 'node', 18), on_part=parts[0]) -t3t = my_model.make_triad('bellcrank_top_housing', get_node(files[3], 'node', 420), on_part=parts[3]) -ct = my_model.make_spring('Boom cylinder', (t0t, t3t), init_Stiff_Coeff=1.0e9, length=f2) +t0t = my_model.make_triad('front_top_cylinder_attachment', node=18, on_part=parts[0]) +t3t = my_model.make_triad('bellcrank_top_housing', node=420, on_part=parts[3]) +ct = my_model.make_spring('Boom cylinder', (t0t, t3t), init_Stiff_Coeff=1.0e9, length=f2) # Right cylinder -t0rc = my_model.make_triad('front_right_cylinder_attachment', get_node(files[0], 'node', 222), on_part=parts[0]) -t1rc = my_model.make_triad('boom_right_cylinder_attachment', get_node(files[1], 'node', 829), on_part=parts[1]) -cr = my_model.make_spring('Lift cylinder', (t0rc, t1rc), init_Stiff_Coeff=1.0e9, length=f1) +t0rc = my_model.make_triad('front_right_cylinder_attachment', node=222, on_part=parts[0]) +t1rc = my_model.make_triad('boom_right_cylinder_attachment', node=829, on_part=parts[1]) +cr = my_model.make_spring('Lift cylinder', (t0rc, t1rc), init_Stiff_Coeff=1.0e9, length=f1) # Left Cylinder -t0lc = my_model.make_triad('front_left_cylinder_attachment', get_node(files[0], 'node', 235), on_part=parts[0]) -t1lc = my_model.make_triad('boom_left_cylinder_attachment', get_node(files[1], 'node', 119), on_part=parts[1]) -cl = my_model.make_spring('Lift cylinder', (t0lc, t1lc), init_Stiff_Coeff=1.0e9, length=f1) +t0lc = my_model.make_triad('front_left_cylinder_attachment', node=235, on_part=parts[0]) +t1lc = my_model.make_triad('boom_left_cylinder_attachment', node=119, on_part=parts[1]) +cl = my_model.make_spring('Lift cylinder', (t0lc, t1lc), init_Stiff_Coeff=1.0e9, length=f1) # Bocket to boom right -t1br = my_model.make_triad('boom_front_right_housing', get_node(files[1], 'node', 715), on_part=parts[1]) -t2br = my_model.make_triad('bucket_right_housing', get_node(files[2], 'node', 509), on_part=parts[2]) -j05 = my_model.make_joint('Rev5', FmType.REVOLUTE_JOINT, t1br, t2br) +t1br = my_model.make_triad('boom_front_right_housing', node=715, on_part=parts[1]) +t2br = my_model.make_triad('bucket_right_housing', node=509, on_part=parts[2]) +j05 = my_model.make_joint('Rev5', FmType.REVOLUTE_JOINT, t1br, t2br) # Bocket to boom left -t1bl = my_model.make_triad('boom_front_left_housing', get_node(files[1], 'node', 5), on_part=parts[1]) -t2bl = my_model.make_triad('bucket_left_housing', get_node(files[2], 'node', 284), on_part=parts[2]) -j06 = my_model.make_joint('Rev6', FmType.REVOLUTE_JOINT, t1bl, t2bl) +t1bl = my_model.make_triad('boom_front_left_housing', node=5, on_part=parts[1]) +t2bl = my_model.make_triad('bucket_left_housing', node=284, on_part=parts[2]) +j06 = my_model.make_joint('Rev6', FmType.REVOLUTE_JOINT, t1bl, t2bl) # Bellcrank to book right| -t1rbc = my_model.make_triad('boom_right_bellcrank_housing', get_node(files[1], 'node', 545), on_part=parts[1]) -t3rbc = my_model.make_triad('bellcrank_right_housing', get_node(files[3], 'node', 276), on_part=parts[3]) -j07 = my_model.make_joint('Rev7', FmType.REVOLUTE_JOINT, t1rbc, t3rbc) +t1rbc = my_model.make_triad('boom_right_bellcrank_housing', node=545, on_part=parts[1]) +t3rbc = my_model.make_triad('bellcrank_right_housing', node=276, on_part=parts[3]) +j07 = my_model.make_joint('Rev7', FmType.REVOLUTE_JOINT, t1rbc, t3rbc) # Bellcrank to book left -t1lbc = my_model.make_triad('boom_left_bellcrank_housing', get_node(files[1], 'node', 424), on_part=parts[1]) -t3lbc = my_model.make_triad('bellcrank_left_housing', get_node(files[3], 'node', 244), on_part=parts[3]) -j08 = my_model.make_joint('Rev8', FmType.REVOLUTE_JOINT, t1lbc, t3lbc) +t1lbc = my_model.make_triad('boom_left_bellcrank_housing', node=424, on_part=parts[1]) +t3lbc = my_model.make_triad('bellcrank_left_housing', node=244, on_part=parts[3]) +j08 = my_model.make_joint('Rev8', FmType.REVOLUTE_JOINT, t1lbc, t3lbc) # Bucketlink rear -t3rbl = my_model.make_triad('bellcrank_bottom_housing', get_node(files[3], 'node', 14), on_part=parts[3]) -t4rbl = my_model.make_triad('bucketlink_rear_houding', get_node(files[4], 'node', 575), on_part=parts[4]) -j09 = my_model.make_joint('Rev9', FmType.REVOLUTE_JOINT, t3rbl, t4rbl) +t3rbl = my_model.make_triad('bellcrank_bottom_housing', node=14, on_part=parts[3]) +t4rbl = my_model.make_triad('bucketlink_rear_houding', node=575, on_part=parts[4]) +j09 = my_model.make_joint('Rev9', FmType.REVOLUTE_JOINT, t3rbl, t4rbl) # Bucketlink front -t2fbl = my_model.make_triad('bucket_mid_housing', get_node(files[2], 'node', 451), on_part=parts[2]) -t4fbl = my_model.make_triad('bucketlink_front_housing', get_node(files[4], 'node', 174), on_part=parts[4]) -j10 = my_model.make_joint('Rev10', FmType.REVOLUTE_JOINT, t2fbl, t4fbl) - +t2fbl = my_model.make_triad('bucket_mid_housing', node=451, on_part=parts[2]) +t4fbl = my_model.make_triad('bucketlink_front_housing', node=174, on_part=parts[4]) +j10 = my_model.make_joint('Rev10', FmType.REVOLUTE_JOINT, t2fbl, t4fbl) my_model.make_load('Load', FmLoadType.FORCE, 39, (0, 0, -1), fn=f4) -# Moving parts to the final location +# Moving parts to their final location my_model.edit_part(parts[1], Tx=0.01080263, Tz=-0.77487206) my_model.edit_part(parts[2], Tx=-0.64636636, Tz=-2.0328088, Ry=-30, Rz=-180) my_model.edit_part(parts[3], Tx=-3.2499752, Ty=-2.8376081, Tz=0.04694241, Ry=21.814096) my_model.edit_part(parts[4], Tx=-2.041544, Ty=-0.92750001, Tz=0.12191465, Ry=-4.9156169) # Joint orientation and properties -my_model.edit_joint(j01, constraints={'Rz' : FmDofStat.SPRING}, spring={'Rz' : 1.0e9}, length={'Rz' : f3}) +my_model.edit_joint(j01, + constraints={'Rz' : FmDofStat.SPRING}, + spring={'Rz' : 1.0e9}, + length={'Rz' : f3}) [my_model.edit_joint(j, Rx=90) for j in [j03, j04, j05, j06, j07, j08, j09, j10]] my_model.fm_solver_setup(t_inc=0.01, t_end=1.6) -my_model.save() -my_model.close() +my_model.close(True, True) # Solver setup and execution my_solver = FmmSolver() my_solver.solve_all(str(model_file), True, True) - -my_solver.solver_done() -my_solver.close_model(False) diff --git a/docs/examples/linked_files/run_fedem_fmu.py b/docs/examples/linked_files/run_fedem_fmu.py new file mode 100644 index 0000000..db206eb --- /dev/null +++ b/docs/examples/linked_files/run_fedem_fmu.py @@ -0,0 +1,78 @@ +""" +Simple python driver running a FEDEM FMU using the fmpy tool. + +This script assumes the environment variable FEDEM_SOLVER contains the +full path to the shared object library of the FEDEM dynamics solver +(fedem_solver_core.dll on Windows, libfedem_solver_core.so on Linux). + +Usage: python -m run_fedem_fmu +""" + +from fmpy import fmi2, read_model_description, extract +from pandas import read_csv +from sys import argv + + +def run(fmu_file, input_file=None, instance_name="my instance"): + """ + Runs the specified FMU + """ + # Read the model description + print("Reading model description from", fmu_file) + model = read_model_description(fmu_file) + + # Extract the FMU itself + print("Extracting", fmu_file, "...") + unzipdir = extract(fmu_file) + + # Setup the FMU + print("Setup the FMU", model.coSimulation.modelIdentifier) + fmu = fmi2.FMU2Slave(guid=model.guid, unzipDirectory=unzipdir, + modelIdentifier=model.coSimulation.modelIdentifier, + instanceName=instance_name) + fmu.instantiate() + fmu.enterInitializationMode() + fmu.exitInitializationMode() + + # Get some size parameters from the FMU + num_params = fmu.getInteger([1, 2]) + num_inputs = num_params[0] # Number of input sensors + num_output = num_params[1] # Number of output sensors + + # Read the input values into a Dataframe + if num_inputs > 0 and input_file is not None: + inputs = read_csv(input_file, sep="\t") + + # List of external function indices + inpIdx = range(num_inputs) + # List of output sensor indices + outIdx = range(num_inputs,num_inputs+num_output) + + # Time loop, this will run through the FEDEM simulation + # using the time domain setup in the model file used to export the FMU. + # The two parameters to the doStep() call are dummies (time and step size). + # They are not used in the FMU so the values are arbitrary (2*0.0 is fine). + istep = 0 + while not fmu.getBooleanStatus(fmi2.fmi2Terminated): + if num_inputs > 0: # Set the external function values for this step + fmu.setReal([*inpIdx], inputs.iloc[istep]) + fmu.doStep(0.0, 0.0) + time = fmu.getRealStatus(fmi2.fmi2LastSuccessfulTime) + output = fmu.getReal([*outIdx]) + istep += 1 + print(f"Here are the outputs at step={istep} time={time}:", output) + + # Finished, close down + fmu.terminate() + fmu.freeInstance() + + +if __name__ == "__main__": + if len(argv) > 3: + run(argv[1], argv[2], argv[3]) + elif len(argv) > 2: + run(argv[1], argv[2]) + elif len(argv) > 1: + run(argv[1]) + else: + print(f"Usage: {argv[0]} [] []") diff --git a/docs/examples/linked_files/sla_FEparts.zip b/docs/examples/linked_files/sla_FEparts.zip new file mode 100644 index 0000000..16a1a74 Binary files /dev/null and b/docs/examples/linked_files/sla_FEparts.zip differ diff --git a/docs/examples/linked_files/sla_modeling.py b/docs/examples/linked_files/sla_modeling.py new file mode 100644 index 0000000..1e8f07a --- /dev/null +++ b/docs/examples/linked_files/sla_modeling.py @@ -0,0 +1,77 @@ +''' +02-car-front-suspension + +Low-code version of the car front suspension model (Short-Long Arm). + +Developed by Knut Morten Okstad (kmo@openfedem.org) +''' + +from os import mkdir, path +from pathlib import Path + +from fedempy.modeler import FedemModeler +from fedempy.enums import FmDof, FmDofStat, FmType, FmVar + +# Global constants +RELATIVE_PATH = '02-car-front-suspension/' +MODEL_FILE = '02-sla-dtwin.fmm' +PARTS_PATH = 'parts/' + +# Prepare run directory (=RELATIVE_PATH) +model_file = Path(RELATIVE_PATH) / MODEL_FILE +if not path.isdir(model_file.parent): + mkdir(model_file.parent) + +# Create a new FEDEM model +my_model = FedemModeler(str(model_file), force_new=True) + +# Load the FE parts +lca = my_model.make_fe_part(PARTS_PATH + 'lca.nas') +knuckle = my_model.make_fe_part(PARTS_PATH + 'knuckle.nas') +uca = my_model.make_fe_part(PARTS_PATH + 'uca.nas') +ground = 2 # base id of the reference plane will always be 2 + +# Lower control arm joints to ground +j1 = my_model.make_joint('Fix 1', FmType.BALL_JOINT, + my_model.make_triad('lca fixed', node=11911, on_part=lca)) +j2 = my_model.make_joint('Fix 2', FmType.BALL_JOINT, + my_model.make_triad('lca fixed', node=11912, on_part=lca)) + +# Knuckle to lower control arm joint +j3 = my_model.make_joint('Lower Ball', FmType.BALL_JOINT, + my_model.make_triad('knuckle lower', node=3, on_part=knuckle), + my_model.make_triad('lca tip', node=11910, on_part=lca)) + +# Upper control arm to knuckle joint +j4 = my_model.make_joint('Upper Ball', FmType.BALL_JOINT, + my_model.make_triad('uca tip', node=2160, on_part=uca), + my_model.make_triad('knuckle upper', node=4, on_part=knuckle)) + +# Upper control arm joints to ground +j5 = my_model.make_joint('Fix 3', FmType.BALL_JOINT, + my_model.make_triad('uca fixed', node=2159, on_part=uca)) +j6 = my_model.make_joint('Fix 4', FmType.BALL_JOINT, + my_model.make_triad('uca fixed', node=2158, on_part=uca)) + +# Steering pin triad +t0 = my_model.make_triad('Steering pin', node=2, on_part=knuckle) +my_model.edit_triad(t0, constraints={'Tx' : FmDofStat.FIXED}) + +# Damper +t1 = my_model.make_triad('Damper pin', node=11909, on_part=lca) +t2 = my_model.make_triad('Damper ground', pos=(0.0, 0.0, 0.3), on_part=ground) +s1 = my_model.make_spring('Damper', (t1, t2), init_Stiff_Coeff=7.5e6) +d1 = my_model.make_damper('Damper', (t1, t2), init_Damp_Coeff=3.0e3) + +# Wheel hub triad with external force +t3 = my_model.make_triad('Wheel hub', node=1, on_part=knuckle) +my_model.edit_triad(t3, load={'Tz' : my_model.make_function('Wheel force')}) + +# Ouput sensors +o1 = my_model.make_sensor('Damper force', (s1, d1), FmVar.FORCE) +o2 = my_model.make_sensor('Damper deflection', s1, FmVar.DEFLECTION) +o3 = my_model.make_sensor('Steering pin deflection', t0, FmVar.POS, FmDof.TZ) + +my_model.fm_solver_setup(t_inc=0.005, t_end=2.5, t_quasi=-1.0) +my_model.fm_solver_tol(1.0e-6,1.0e-6,1.0e-6) +my_model.close(True, True) diff --git a/docs/examples/A_loader.md b/docs/examples/loader.md similarity index 65% rename from docs/examples/A_loader.md rename to docs/examples/loader.md index 4b80447..4c722a9 100644 --- a/docs/examples/A_loader.md +++ b/docs/examples/loader.md @@ -1,6 +1,8 @@ # The Loader -This model represents a simplified front-end of a front loader and consists of the following FE Parts: +This example consists of a simplified model of a tractor front-end loader. +The model consists of the following five FE Parts which can be downloaded +[here](linked_files/loader_FEparts.zip). * Loader Front Frame * Lifting Arm @@ -8,7 +10,7 @@ This model represents a simplified front-end of a front loader and consists of t * Bell crank * Bucket link -The hydraulic cylinders are represented by Axial Springs and Dampers, +The hydraulic cylinders are represented by Axial Springs while the joints are represented by Revolute joint objects. ![loader](../images/loader_model.png) @@ -27,51 +29,55 @@ Beam elements are used to model the cylinders, improving the mass distribution a ## Case 4: Improved cylinder control, using PID controller --> -## Solver control with Python API +## Model and solver control using the Python API -```python -from pathlib import Path +To create and solve this model, the following items needs to be +imported from the [fedempy](../python_api.md) module. +```python from fedempy.fmm_solver import FmmSolver from fedempy.modeler import FedemModeler from fedempy.enums import FmDof, FmDofStat, FmLoadType, FmType, FmVar - -... ``` -## Load driven Digital Twin +## Low-code modelling and solving + +Here is a complete python script that creates and runs the Loader model. +Make sure to unzip the file with the [FE models](linked_files/loader_FEparts.zip) +before running the script. [Download...](linked_files/loader_modeling_and_solving.py) ```python +from os import mkdir, path from pathlib import Path from fedempy.fmm_solver import FmmSolver from fedempy.modeler import FedemModeler -from fedempy.enums import FmDof, FmDofStat, FmLoadType, FmType, FmVar - -from _clean_path import clean_path +from fedempy.enums import FmDofStat, FmLoadType, FmType # Global constants -RELATIVE_PATH = '00-loader-Base_case/' -MODEL_FILE = '00-loader-Base_case.fmm' +RELATIVE_PATH = '00-loader-base_case/' +MODEL_FILE = '00-loader-base_case.fmm' PARTS_PATH = 'parts/' # Prepare run directory (=RELATIVE_PATH) model_file = Path(RELATIVE_PATH) / MODEL_FILE -# parts_path = '../'*RELATIVE_PATH.count('/') + PARTS_PATH -clean_path(model_file.parent) +if not path.isdir(model_file.parent): + mkdir(model_file.parent) +# Create a new FEDEM model my_model = FedemModeler(str(model_file), force_new=True) +# Load the FE parts p1 = my_model.make_fe_part(PARTS_PATH + 'Front.flm') p2 = my_model.make_fe_part(PARTS_PATH + 'Boom.flm') p3 = my_model.make_fe_part(PARTS_PATH + 'Bucket.flm') p4 = my_model.make_fe_part(PARTS_PATH + 'BellCrank.flm') p5 = my_model.make_fe_part(PARTS_PATH + 'BucketLink.flm') -my_model.edit_part([p1, p2, p3, p4, p5], - alpha2=0.00286, component_modes=0, consistent_mass=True) +# Edit some part properties +my_model.edit_part([p1, p2, p3, p4, p5], alpha2=0.00286, component_modes=0, consistent_mass=True) my_model.edit_part(p2, Tx=0.01080263, Tz=-0.77487206) my_model.edit_part(p3, Tx=-0.64636636, Tz=-2.0328088, Ry=-30, Rz=-180) my_model.edit_part(p4, Tx=-3.2499752, Ty=-2.8376081, Tz=0.04694241, Ry=21.814096) @@ -93,43 +99,46 @@ joints.append(my_model.make_joint('Rv10', FmType.REVOLUTE_JOINT, 55, 35)) my_model.edit_joint(joints[2], Ry=90, Rz=-90) [my_model.edit_joint(id, Rx=90) for id in joints[3:]] - +# Create functions f1 = my_model.make_function('Lift Cylinders', - frequency=0.625, amplitude=0.15, delay=0.25, mean_value=0.15, end=0.8) + frequency=0.625, amplitude=0.15, delay=0.25, mean_value=0.15, end=0.8) f2 = my_model.make_function('Boom Cylinder', - slope=-0.5, start_ramp=0.5, end_ramp=1.2) + slope=-0.5, start_ramp=0.5, end_ramp=1.2) f3 = my_model.make_function('Rotation Front', - frequency=0.5, amplitude=0.262, delay=0.25, mean_value=0.262, end=1.0) + frequency=0.5, amplitude=0.262, delay=0.25, mean_value=0.262, end=1.0) f4 = my_model.make_function('Bucket Load', - slope=50000, start_ramp=0.7, end_ramp=1.2) + slope=50000, start_ramp=0.7, end_ramp=1.2) my_model.edit_joint(joints[0], constraints={'Rz' : FmDofStat.SPRING}, - spring={'Rz' : 1.0e9}, length={'Rz' : f3}) + spring={'Rz' : 1.0e9}, length={'Rz' : f3}) cyl = my_model.make_spring('Lift cylinder', [(9, 23), (10, 18)], - init_Stiff_Coeff=1.0e9, length=f1) + init_Stiff_Coeff=1.0e9, length=f1) cyl.append(my_model.make_spring('Boom cylinder', (5, 50), - init_Stiff_Coeff=1.0e9, length=f2)) + init_Stiff_Coeff=1.0e9, length=f2)) my_model.make_load('Load', FmLoadType.FORCE, 39, (0, 0, -1), fn=f4) my_model.fm_solver_setup(t_inc=0.01, t_end=1.6) -my_model.save() -my_model.close() +my_model.close(True, True) # Solver setup and execution my_solver = FmmSolver() my_solver.solve_all(str(model_file), True, True) - -my_solver.solver_close() -my_solver.close_model(False) ``` -## Low-code modeling +## No-code modeling + +It is also possible to create the FEDEM model without writing python code. +Instead, the model is described using YAML-syntax. + +To create this model, execute the command: + + $ python -m fedempy.yaml_parser -f loader_model.yaml [Download...](linked_files/loader_model.yaml) @@ -145,34 +154,34 @@ fe_parts: p5: [parts/BucketLink.ftl] triads_from_fe_parts: - Front_fix_bottom: [Front bottom bearing housing to ground, p1, node, 441] - front_fix_top: [Front top bearing housing to ground, p1, node, 211] - front_right_housing: [Front right bearing housing, p1, node, 38] - front_left_housing: [Front left bearing housing, p1, node, 58] - front_top_cylinder_attachment: [Front top cylinder attachment point, p1, node, 18] - front_right_cylinder_attachment: [Front right cylinder attachment point, p1, node, 222] - front_left_cylinder_attachment: [Front left cylinder attachment point, p1, node, 235] - - boom_rear_right_houding: [Boom right rear bearing housing, p2, node, 1038] - boom_rear_left_housing: [Boom left rear bearing housing, p2, node, 178] - boom_front_right_housing: [Boom right front bearing housing, p2, node, 715] - boom_front_left_housing: [Boom left front bearing housing, p2, node, 5] - boom_right_bellcrank_housing: [Boom right bearing housing to BellCrank, p2, node, 545] - boom_left_bellcrank_housing: [Boom left bearing housing to BellCrank, p2, node, 424] - boom_right_cylinder_attachment: [Boom right cylinder attachment point, p2, node, 829] - boom_left_cylinder_attachment: [Boom left cylinder attachment point, p2, node, 119] - - bucket_right_housing: [Bucket right housing, p3, node, 509] - bucket_mid_housing: [Bucket midt housing, p3, node, 451] - bucket_left_housing: [Bucket left housing, p3, node, 284] - - bellcrank_top_housing: [BellCrank top housing, p4, node, 420] - bellcrank_bottom_housing: [BellCrank bottom housing, p4, node, 14] - bellcrank_right_housing: [BellCrank right housing, p4, node, 276] - bellcrank_left_housing: [BellCrank left housing, p4, node, 244] - - bucketlink_rear_houding: [BucketLink rear housing, p5, node, 575] - bucketlink_front_housing: [BucketLink front housing, p5, node, 174] + Front_fix_bottom: [Front bottom bearing housing to ground, p1, node, 441] + front_fix_top: [Front top bearing housing to ground, p1, node, 211] + front_right_housing: [Front right bearing housing, p1, node, 38] + front_left_housing: [Front left bearing housing, p1, node, 58] + front_top_cylinder_attachment: [Front top cylinder attachment point, p1, node, 18] + front_right_cylinder_attachment: [Front right cylinder attachment point, p1, node, 222] + front_left_cylinder_attachment: [Front left cylinder attachment point, p1, node, 235] + + boom_rear_right_houding: [Boom right rear bearing housing, p2, node, 1038] + boom_rear_left_housing: [Boom left rear bearing housing, p2, node, 178] + boom_front_right_housing: [Boom right front bearing housing, p2, node, 715] + boom_front_left_housing: [Boom left front bearing housing, p2, node, 5] + boom_right_bellcrank_housing: [Boom right bearing housing to BellCrank, p2, node, 545] + boom_left_bellcrank_housing: [Boom left bearing housing to BellCrank, p2, node, 424] + boom_right_cylinder_attachment: [Boom right cylinder attachment point, p2, node, 829] + boom_left_cylinder_attachment: [Boom left cylinder attachment point, p2, node, 119] + + bucket_right_housing: [Bucket right housing, p3, node, 509] + bucket_mid_housing: [Bucket midt housing, p3, node, 451] + bucket_left_housing: [Bucket left housing, p3, node, 284] + + bellcrank_top_housing: [BellCrank top housing, p4, node, 420] + bellcrank_bottom_housing: [BellCrank bottom housing, p4, node, 14] + bellcrank_right_housing: [BellCrank right housing, p4, node, 276] + bellcrank_left_housing: [BellCrank left housing, p4, node, 244] + + bucketlink_rear_houding: [BucketLink rear housing, p5, node, 575] + bucketlink_front_housing: [BucketLink front housing, p5, node, 174] edit_fe_parts: p1: &link_property @@ -183,7 +192,6 @@ edit_fe_parts: <<: *link_property Tx: 0.01080263 Tz: -0.77487206 - p3: <<: *link_property Tx: -0.64636636 @@ -204,20 +212,20 @@ edit_fe_parts: Ry: -4.9156169 joints: - j01: [Front bottom to ground, BALL_JOINT, Front_fix_bottom] - j02: [Front top to ground, BALL_JOINT, front_fix_top] + j01: [Front bottom to ground, BALL_JOINT, Front_fix_bottom] + j02: [Front top to ground, BALL_JOINT, front_fix_top] - j03: [Front to boom right, BALL_JOINT, front_right_housing, boom_rear_right_houding] - j04: [Front to boom left, BALL_JOINT, front_left_housing, boom_rear_left_housing] + j03: [Front to boom right, BALL_JOINT, front_right_housing, boom_rear_right_houding] + j04: [Front to boom left, BALL_JOINT, front_left_housing, boom_rear_left_housing] - j05: [Boom to bucket right, BALL_JOINT, boom_front_right_housing, bucket_right_housing] - j06: [Boom to bucket left, BALL_JOINT, boom_front_left_housing, bucket_left_housing] + j05: [Boom to bucket right, BALL_JOINT, boom_front_right_housing, bucket_right_housing] + j06: [Boom to bucket left, BALL_JOINT, boom_front_left_housing, bucket_left_housing] - j07: [Bucket link rear, BALL_JOINT, bucketlink_rear_houding, bellcrank_bottom_housing] - j08: [Bucket link front, BALL_JOINT, bucketlink_front_housing, bucket_mid_housing] + j07: [Bucket link rear, BALL_JOINT, bucketlink_rear_houding, bellcrank_bottom_housing] + j08: [Bucket link front, BALL_JOINT, bucketlink_front_housing, bucket_mid_housing] - j09: [Boom to bellCrank right, BALL_JOINT, boom_right_bellcrank_housing, bellcrank_right_housing] - j10: [Boom to BellCrank left, BALL_JOINT, boom_left_bellcrank_housing, bellcrank_left_housing] + j09: [Boom to bellCrank right, BALL_JOINT, boom_right_bellcrank_housing, bellcrank_right_housing] + j10: [Boom to BellCrank left, BALL_JOINT, boom_left_bellcrank_housing, bellcrank_left_housing] hydraulic_cylinders: # User-defined function Top_cylinder: [Top cylinder, front_top_cylinder_attachment, diff --git a/docs/images/fedem-fmu-export.png b/docs/images/fedem-fmu-export.png new file mode 100644 index 0000000..8e48b23 Binary files /dev/null and b/docs/images/fedem-fmu-export.png differ diff --git a/docs/images/sla_model.png b/docs/images/sla_model.png new file mode 100644 index 0000000..e58414f Binary files /dev/null and b/docs/images/sla_model.png differ diff --git a/mkdocs.yml b/mkdocs.yml index 52da360..ac9f143 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -26,14 +26,14 @@ nav: - Discussions: discussions.md - Issues (Bugs): issues.md - Examples: - - Introduction: examples/0_introduction.md - - The Loader: examples/A_loader.md + - Introduction: examples/introduction.md + - The Loader: examples/loader.md #- Simple: examples/A_simple.md #- Advanced: examples/B_advanced.md #- Model imports: examples/C_imports.md #- Industrial applications: examples/D_industrial_applications.md #- Simulators: examples/E_simulators.md - #- Digital Twins: examples/F_digital_twins.md + - Digital Twins: examples/digital_twins.md #- Benchmarks: examples/G_case_studies.md #- Physics cases: examples/H_physics.md #- API: examples/I_api.md