Skip to content

Commit

Permalink
Merge pull request #10 from BAMresearch/sensors_fatih
Browse files Browse the repository at this point in the history
Sensors fatih
  • Loading branch information
soenmezmehmet authored Jan 17, 2024
2 parents b7e9971 + 617994f commit d19cae7
Show file tree
Hide file tree
Showing 37 changed files with 632 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import ufl
from petsc4py.PETSc import ScalarType
from dolfinx import fem
import dolfinx as df

from fenicsxconcrete.finite_element_problem.linear_elasticity import LinearElasticity
from fenicsxconcrete.util import ureg

from mpi4py import MPI
from nibelungenbruecke.scripts.data_generation.generator_model_base_class import GeneratorModel
from nibelungenbruecke.scripts.data_generation.nibelungen_experiment import NibelungenExperiment
from nibelungenbruecke.scripts.utilities.sensor_translators import Translator

class GeneratorFeniCSXConcrete(GeneratorModel):
def __init__(self, model_path: str, sensor_positions_path: str, model_parameters: dict, output_parameters: dict = None):
super().__init__(model_path, sensor_positions_path, model_parameters, output_parameters)
self.material_parameters = self.model_parameters["material_parameters"] # currently it is an empty dict!!

def LoadGeometry(self):
''' Load the meshed geometry from a .msh file'''
pass

def GenerateModel(self):
self.experiment = NibelungenExperiment(self.model_path, self.material_parameters)

default_p = self._get_default_parameters()
default_p.update(self.experiment.default_parameters())
self.problem = LinearElasticity(self.experiment, default_p)

def GenerateData(self):
#Generating Translator object
T = Translator(self.sensor_positions)

# Translation from MKP data format (currently supports "move" operations only!)
_, meta_output_path = T.translator_to_sensor(self.model_parameters["df_output_path"], self.model_parameters["meta_output_path"])

self.problem.import_sensors_from_metadata(meta_output_path)
self.problem.solve()

#Paraview output
if self.model_parameters["paraview_output"]:
with df.io.XDMFFile(self.problem.mesh.comm, self.model_parameters["paraview_output_path"]+"/"+self.model_parameters["model_name"]+".xdmf", "w") as xdmf:
xdmf.write_mesh(self.problem.mesh)
xdmf.write_function(self.problem.fields.displacement)

# Reverse translation to MKP data format
T.translator_to_MKP(self.problem, self.model_parameters["save_to_MKP_path"])

@staticmethod
def _get_default_parameters():
default_parameters = {
"rho":7750 * ureg("kg/m^3"),
"E":210e9 * ureg("N/m^2"),
"nu":0.28 * ureg("")
}
return default_parameters

110 changes: 110 additions & 0 deletions nibelungenbruecke/scripts/data_generation/nibelungen_experiment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
from collections.abc import Callable

import dolfinx as df
import numpy as np
import pint
import ufl
from mpi4py import MPI
from petsc4py.PETSc import ScalarType

from fenicsxconcrete.boundary_conditions.bcs import BoundaryConditions
from fenicsxconcrete.boundary_conditions.boundary import line_at, point_at
from fenicsxconcrete.experimental_setup.base_experiment import Experiment
from fenicsxconcrete.util import Parameters, ureg


class NibelungenExperiment(Experiment):
def __init__(self, model_path, parameters: dict[str, pint.Quantity]) -> None:

self.model_path = model_path

default_p = Parameters()
default_p.update(parameters)
super().__init__(default_p)


def setup(self):
try:
self.mesh, cell_tags, facet_tags = df.io.gmshio.read_from_msh(self.model_path, MPI.COMM_WORLD, 0)

except Exception as e:
raise Exception(f"An error occurred during mesh setup: {e}")

@staticmethod
def default_parameters() -> dict[str, pint.Quantity]:
setup_parameters = {}
# setup_parameters["load"] = 10000 * ureg("N/m^2")
setup_parameters["length"] = 1 * ureg("m")
setup_parameters["dim"] = 3 * ureg("")

return setup_parameters


def create_displacement_boundary(self, V) -> list:
"""defines displacement boundary as fixed at bottom
Args:
V: function space
Returns:
list of dirichlet boundary conditions
"""

bc_generator = BoundaryConditions(self.mesh, V)


if self.p["dim"] == 3:
# fix line in the left
bc_generator.add_dirichlet_bc(
np.array([0.0, 0.0, 0.0], dtype=ScalarType),
boundary=self.boundary_left(),
method="geometrical",
)
# line with dof in x direction on the right
bc_generator.add_dirichlet_bc(np.float64(0.0), self.boundary_right(), 1, "geometrical", 0)
bc_generator.add_dirichlet_bc(np.float64(0.0), self.boundary_right(), 2, "geometrical", 0)

return bc_generator.bcs

def boundary_left(self) -> Callable:
"""specifies boundary at bottom
Returns:
fct defining boundary
"""

if self.p["dim"] == 3:
return line_at([0, 0], ["x", "z"])

def boundary_right(self) -> Callable:
"""specifies boundary at bottom
Returns:
fct defining boundary
"""

if self.p["dim"] == 3:
return line_at([self.p["length"], 0], ["x", "z"])


def create_body_force(self, v: ufl.argument.Argument) -> ufl.form.Form:
"""defines body force
Args:
v: test function
Returns:
form for body force
"""

force_vector = np.zeros(self.p["dim"])
force_vector[-1] = -self.p["rho"] * self.p["g"] # works for 2D and 3D

f = df.fem.Constant(self.mesh, ScalarType(force_vector))
L = ufl.dot(f, v) * ufl.dx

return L
32 changes: 32 additions & 0 deletions nibelungenbruecke/scripts/utilities/BAM_Beispieldatensatz.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from os import PathLike
from typing import Union, Tuple

import pandas as pd
import json
import datetime


def load_bam(file: Union[str, bytes, PathLike],
columns: list) -> Tuple[pd.DataFrame, dict]:
# load file
with open(file=file, mode='r') as f:
j = json.load(f)
# create objects
df = pd.DataFrame(columns=j['df']['columns'], index=j['df']['index'], data=j['df']['data'])
meta = j['meta']
# filter by columns given
df_filtered = df[columns]
meta_filtered = {column: meta[column] for column in columns}

return df_filtered, meta_filtered


def save_bam(df: pd.DataFrame, meta: dict, path: str) -> None:
# bring data into required format
df_dict = json.loads(df.to_json(orient='split'))
output = json.dumps({'df': df_dict, 'meta': meta})
# write to file
filename = path + '/' + datetime.datetime.now().strftime('%Y%m%d%H%M%S') + '.json'
with open(filename, 'x') as f:
f.write(output)

69 changes: 69 additions & 0 deletions nibelungenbruecke/scripts/utilities/sensor_translators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from os import PathLike
from typing import Union, Tuple

import pandas as pd
import json
import datetime

from nibelungenbruecke.scripts.utilities.BAM_Beispieldatensatz import load_bam
from nibelungenbruecke.scripts.utilities.BAM_Beispieldatensatz import save_bam


class Translator():

def __init__(self, path_to_json:Union[str, bytes, PathLike], **kwargs):
self.columns = ["Temp", "Move"]
self.path_to_json = path_to_json
self.kwargs = kwargs


def _default_parameters(self):
default_parameters_data = {
"sensors": [
{
"id": "DisplacementSensor",
"type": "DisplacementSensor",
"sensor_file": "displacement_sensor",
"units": "meter",
"dimensionality": "[length]",
"where": [1, 0.0, 0.0]
}
]
}
return default_parameters_data

def translator_to_sensor(self, df_output_path, meta_output_path):

self.df, self.meta = load_bam(self.path_to_json, self.columns)

if "Move" in self.meta.keys():
sensor = self._default_parameters()

if "coordinate" in self.meta["Move"].keys():
sensor["sensors"][0]["where"] = self.meta["Move"]["coordinate"] # geodesic_to_cartesian(dictionary[key][x])

if "units" in self.meta["Move"].keys():
sensor["sensors"][0]["units"] = self.meta["Move"]["units"]


with open(meta_output_path, "w") as f:
json.dump(sensor, f, indent=4)


df_dict = json.loads(self.df.to_json(orient='split'))

with open(df_output_path, "w") as f:
json.dump(df_dict, f)

return df_output_path, meta_output_path


def translator_to_MKP(self, sensor_obj, path) -> None:
self.df, self.meta = load_bam(self.path_to_json, self.columns)
movement = sensor_obj.sensors["DisplacementSensor"].data[0]
for i in range(len(movement)):
self.df["Move"][i] = movement[i]*1000

self.meta["Move"]["coordinate"] = sensor_obj.sensors["DisplacementSensor"].where

save_bam(self.df, self.meta, path)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This folder contains the files necessary to generate a document in LaTeX.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import json
import importlib
import sys
from nibelungenbruecke.scripts.utilities import checks

### CONFIG TASKS ###
doit_parameters_path = "input/settings/doit_parameters.json"
checks.assert_path_exists(doit_parameters_path)

with open(doit_parameters_path, 'r') as file:
DOIT_CONFIG = json.load(file)

# Import all tasks specified in the JSON file marked as true
for task, load_task in DOIT_CONFIG.items():
if load_task:
module = importlib.import_module("nibelungenbruecke.tasks." + task + "_tasks")
functions = {name: value for name, value in vars(module).items() if callable(value)and name.startswith("task_")}
for name, value in functions.items():
globals()[name] = value

# Run tasks if __name__ == '__main__'

if __name__ == "__main__":

from doit.doit_cmd import DoitMain
from doit.cmd_base import ModuleTaskLoader
import sys

DoitMain(ModuleTaskLoader(globals())).run(sys.argv[1:])
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Here the input files for a given example are saved. They should be sufficient for defining the workflow parameters and are unique for each project. The current files belong to the solution of a simple workflow based on the Nibelungenbrücke. However, if a new example is to be implemented after installing `nibelungenbruecke`, it is recommended to copy newly the repository and delete the folder with the package (as it will already be available globaly in the environment). The input files are grouped in several folders:
- `data` contains the data points to use in the inference procedure.
- `models` contains the geometry and mesh used in the demonstrator. If they are not provided beforehand, they are generated during the workflow and saved to this folder.
- `sensors` contains JSON files that indicate the sensor positions and characteristics.
- `settings` contains JSON files with the settings to run each of the modules of the workflow.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Input data for inference/prediction procedure. Currently only HDF5 format is supported.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Generated geometry and mesh models of the Nibelungenbrücke in Worms. Generated with GMSH.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"df": {"columns": ["Temp", "Move"], "index": ["2023-02-13T11:00:00.000000Z", "2023-02-13T11:10:00.000000Z", "2023-02-13T11:20:00.000000Z"], "data": [[-0.3228149414, 109.6334991455], [-0.3199920654, 109.6382293701], [-0.3182678223, 109.6385650635]]}, "meta": {"Temp": {"name": "Temp", "unit": "\u00b0C", "sample_rate": 0.0016666666666666668, "coordinate": "32 U E455088 N5497785", "height": 104.105}, "Move": {"name": "Move", "unit": "mm", "sample_rate": 0.0016666666666666668, "coordinate": [1, 0.0, 0.0], "height": 99.3}}}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Sensor positions as JSON files. They can be empty if no measurement is required. Notice that the sintaxis depends on the type of sensor to be implemented (i.e. virtual sensor for data generation or for inference using probeye)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"columns": ["Temp", "Move"], "index": ["2023-02-13T11:00:00.000000Z", "2023-02-13T11:10:00.000000Z", "2023-02-13T11:20:00.000000Z"], "data": [[-0.3228149414, 109.6334991455], [-0.3199920654, 109.6382293701], [-0.3182678223, 109.6385650635]]}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"sensors": [
{
"id": "DisplacementSensor",
"type": "DisplacementSensor",
"sensor_file": "displacement_sensor",
"units": "meter",
"dimensionality": "[length]",
"where": [
1,
0.0,
0.0
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Sensor_2":{
"type": "DisplacementSensor",
"where": [[0,0,50]]
}



}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"disp_span_new_1":{
"measurand": "displacement",
"x": 0,
"y": 0,
"z": 25,
"std_model": "sigma"
},
"disp_span_new_2":{
"measurand": "displacement",
"x": 0,
"y": 0,
"z": 75,
"std_model": "sigma"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"disp_span":{
"measurand": "displacement",
"x": 0,
"y": 0,
"z": 50,
"std_model": "sigma"
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"sensors": [{"id": "DisplacementSensor", "type": "DisplacementSensor", "sensor_file": "displacement_sensor", "units": "meter", "dimensionality": "[length]", "where": [1, 0.0, 0.0]}]}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Settings files to govern the programm. They must be written in JSON format.
Loading

0 comments on commit d19cae7

Please sign in to comment.