diff --git a/.gitignore b/.gitignore index 824b241..e60b881 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ test_pybamm.py # Ignore the virtual environment folder venv/ +env/ # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/BatteryModelMapper/__init__.py b/BatteryModelMapper/__init__.py index 8102f69..6923ee9 100644 --- a/BatteryModelMapper/__init__.py +++ b/BatteryModelMapper/__init__.py @@ -3,3 +3,5 @@ from .json_validator import JSONValidator from .json_writer import JSONWriter from .parameter_mapper import ParameterMapper +from .jsonld_exporter import export_jsonld +from .preprocess_input import PreprocessInput diff --git a/BatteryModelMapper/json_loader.py b/BatteryModelMapper/json_loader.py index 02c4cc5..0ed1bf5 100644 --- a/BatteryModelMapper/json_loader.py +++ b/BatteryModelMapper/json_loader.py @@ -1,8 +1,21 @@ +from pathlib import Path +from urllib.parse import urlparse +import json import requests + class JSONLoader: @staticmethod - def load(json_url): - response = requests.get(json_url) - response.raise_for_status() - return response.json() + def load(source): + source = Path(source) + + if urlparse(str(source)).scheme in ("http", "https"): + # Read from URL + response = requests.get(str(source)) + response.raise_for_status() + return response.json() + elif source.is_file(): + # Read from file + return json.loads(source.read_text(encoding="utf-8")) + else: + raise ValueError(f"File does not exist: {source}") diff --git a/BatteryModelMapper/jsonld_exporter.py b/BatteryModelMapper/jsonld_exporter.py new file mode 100644 index 0000000..8f2db85 --- /dev/null +++ b/BatteryModelMapper/jsonld_exporter.py @@ -0,0 +1,219 @@ +import json +from typing import Any, Dict, List, Optional, Set +from rdflib import BNode, URIRef +from rdflib.namespace import RDF, RDFS, OWL, SKOS + + +def _is_number_like(v: Any) -> bool: + if isinstance(v, (int, float)) and not isinstance(v, bool): + return True + if isinstance(v, str): + s = v.strip() + if not s: + return False + try: + float(s) + return True + except ValueError: + return False + return False + + +def _get_modellib_hash(u: URIRef) -> str: + s = str(u) + return s.rsplit("#", 1)[-1].rsplit("/", 1)[-1] + + +def _curie(g, term: URIRef) -> str: + try: + return g.namespace_manager.normalizeUri(term) + except Exception: + return str(term) + + +def _first_literal_str(g, subj: URIRef, pred: URIRef) -> Optional[str]: + for o in g.objects(subj, pred): + return str(o) + return None + + +def _get_skos_prefLabel(g, term: URIRef) -> str: + return ( + _first_literal_str(g, term, SKOS.prefLabel) + or _first_literal_str(g, term, RDFS.label) + or _curie(g, term) + ) + + +def _find_any_predicate_by_localname(g, candidates: Set[str]) -> Optional[URIRef]: + for p in set(g.predicates()): + if _get_modellib_hash(p) in candidates: + return p + return None + + +def _get_value_from_path(data: Any, keys: List[Any]) -> Any: + cur = data + try: + for k in keys: + if isinstance(k, str): + k = k.strip() + if isinstance(cur, dict): + cur = cur[k] + elif isinstance(cur, list): + cur = cur[int(k)] + else: + return None + return cur + except (KeyError, IndexError, ValueError, TypeError): + return None + + +def _iter_restrictions(g, cls: URIRef): + for sc in g.objects(cls, RDFS.subClassOf): + if isinstance(sc, BNode) and (sc, RDF.type, OWL.Restriction) in g: + yield sc + for ec in g.objects(cls, OWL.equivalentClass): + if isinstance(ec, BNode) and (ec, RDF.type, OWL.Restriction) in g: + yield ec + + +def _find_missing_values(ontology_parser, input_data, input_type): + mapped_paths = set() + key = ontology_parser.key_map.get(input_type) + for s in ontology_parser.graph.subjects(): + for p, o in ontology_parser.graph.predicate_objects(s): + if p == key: + mapped_paths.add(tuple(ontology_parser.parse_key(str(o)))) + + def collect_json_paths(data, prefix=()): + paths = set() + if isinstance(data, dict): + for k, v in data.items(): + paths |= collect_json_paths(v, prefix + (k,)) + elif isinstance(data, list): + for i, v in enumerate(data): + paths |= collect_json_paths(v, prefix + (i,)) + else: + paths.add(prefix) + return paths + + input_paths = collect_json_paths(input_data) + missing = sorted(p for p in input_paths if p not in mapped_paths) + return input_paths, mapped_paths, missing + + +def _find_any_predicate_by_localname(g, candidates: Set[str]) -> Optional[URIRef]: + for p in set(g.predicates()): + if _get_modellib_hash(p) in candidates: + return p + return None + + +def _get_unit_for_subject(g, subject: URIRef) -> Optional[str]: + # Get the unit for a given subject if defined + unit_predicates = {"hasMeasurementUnit", "hasUnit", "unit"} + unit_pred = _find_any_predicate_by_localname(g, unit_predicates) + if unit_pred: + for unit in g.objects(subject, unit_pred): + return _curie(g, unit) + else: + # Assume SI units based on common property names + # breakpoint() + + # Get skos preflabel if exists + label = _get_skos_prefLabel(g, subject) + breakpoint() + + +def export_jsonld( + ontology_parser, + input_type: str, + input_data: Dict[str, Any], + output_path: str, + cell_id: str = "BattMo", + cell_type: str = "PouchCell", +): + g = ontology_parser.graph + input_key = ontology_parser.key_map.get(input_type) + if not input_key: + raise ValueError(f"Invalid input type: {input_type}") + + out = { + "@context": "https://w3id.org/emmo/domain/battery/context", + "@graph": { + "@id": cell_id, + "@type": cell_type, + "hasProperty": [], + }, + } + has_property = out["@graph"]["hasProperty"] + + for subject in set(g.subjects(input_key, None)): + path = None + for p, o in g.predicate_objects(subject): + if p == input_key: + path = ontology_parser.parse_key(str(o)) + break + if not path: + continue + value = _get_value_from_path(input_data, path) + if value is None: + continue + + prop_obj = { + "@type": _curie(g, subject), + "rdfs:label": _get_skos_prefLabel(g, subject), + } + + if _is_number_like(value): + prop_obj["hasNumericalPart"] = { + "@type": "Real", + "hasNumericalValue": float(value), + } + elif isinstance(value, str): + prop_obj["hasStringPart"] = { + "@type": "String", + "hasStringValue": value, + } + elif isinstance(value, (list, dict)): + if isinstance(value, dict) and "functionname" in value: + func_name = str(value["functionname"]) + prop_obj["hasStringPart"] = { + "@type": "String", + "hasStringValue": func_name, + } + else: + prop_obj["hasStringPart"] = { + "@type": "String", + "hasStringValue": str(value), + } + else: + prop_obj["hasStringPart"] = { + "@type": "String", + "hasStringValue": str(value), + } + has_property.append(prop_obj) + + # Add units + unit = _get_unit_for_subject(g, subject) + if unit: + prop_obj["emmo:hasMeasurementUnit"] = unit + + with open(output_path, "w", encoding="utf-8") as f: + json.dump(out, f, indent=2) + + # Find values not mapped + input_paths, mapped_paths, missing = _find_missing_values( + ontology_parser, input_data, input_type + ) + + print("Number of JSON leaf values:", len(input_paths)) + print("Number of mapped values:", len(mapped_paths)) + print("Missing values:", len(missing)) + print("These are missing values from the ontology mapping:") + for p in missing: + print(" ", ".".join(str(x) for x in p)) + print("Write these missing values to 'missing_values.json'") + with open("missing_values.json", "w", encoding="utf-8") as f: + json.dump([".".join(str(x) for x in p) for p in missing], f, indent=2) diff --git a/BatteryModelMapper/ontology_parser.py b/BatteryModelMapper/ontology_parser.py index f327bc1..cc47494 100644 --- a/BatteryModelMapper/ontology_parser.py +++ b/BatteryModelMapper/ontology_parser.py @@ -1,17 +1,41 @@ +from pathlib import Path +from urllib.parse import urlparse + import ast +import json import requests -from rdflib import Graph, URIRef + +from rdflib import Graph, URIRef, OWL +from rdflib.namespace import RDF + class OntologyParser: - def __init__(self, ontology_url): + def __init__(self, ontology_ref): self.graph = Graph() - response = requests.get(ontology_url) - response.raise_for_status() - self.graph.parse(data=response.text, format='ttl') + ontology_ref = Path(ontology_ref) + + if urlparse(str(ontology_ref)).scheme in ("http", "https"): + response = requests.get(ontology_ref) + response.raise_for_status() + response_text = response.text + elif ontology_ref.is_file(): + with open(ontology_ref, "r", encoding="utf-8") as f: + response_text = f.read().replace("\r\n", "\n") + else: + raise ValueError(f"File does not exist: {ontology_ref}") + + self.graph.parse(data=response_text, format="ttl") + self.key_map = { - 'bpx': URIRef("https://w3id.org/emmo/domain/battery-model-lithium-ion#bmli_0a5b99ee_995b_4899_a79b_925a4086da37"), - 'cidemod': URIRef("https://w3id.org/emmo/domain/battery-model-lithium-ion#bmli_1b718841_5d72_4071_bb71_fc4a754f5e30"), - 'battmo': URIRef("https://w3id.org/emmo/domain/battery-model-lithium-ion#bmli_2c718841_6d73_5082_bb81_gc5b754f6e40") # Placeholder URI + "bpx": URIRef( + "https://w3id.org/emmo/domain/battery-model-lithium-ion#bmli_0a5b99ee_995b_4899_a79b_925a4086da37" + ), + "cidemod": URIRef( + "https://w3id.org/emmo/domain/battery-model-lithium-ion#bmli_1b718841_5d72_4071_bb71_fc4a754f5e30" + ), + "battmo.m": URIRef( + "https://w3id.org/emmo/domain/battery-model-lithium-ion#bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14" + ), } def parse_key(self, key): @@ -25,7 +49,9 @@ def get_mappings(self, input_type, output_type): input_key = self.key_map.get(input_type) output_key = self.key_map.get(output_type) if not input_key or not output_key: - raise ValueError(f"Invalid input or output type: {input_type}, {output_type}") + raise ValueError( + f"Invalid input or output type: {input_type}, {output_type}" + ) mappings = {} for subject in self.graph.subjects(): diff --git a/BatteryModelMapper/parameter_mapper.py b/BatteryModelMapper/parameter_mapper.py index a3201fc..53013b4 100644 --- a/BatteryModelMapper/parameter_mapper.py +++ b/BatteryModelMapper/parameter_mapper.py @@ -1,6 +1,7 @@ import json import re + class ParameterMapper: def __init__(self, mappings, template, input_url, output_type, input_type): self.mappings = mappings @@ -17,8 +18,6 @@ def map_parameters(self, input_data): if value is not None: if isinstance(value, str): value = self.replace_variables(value) - if self.input_type == 'cidemod' and 'kinetic_constant' in input_key: - value = self.scale_kinetic_constant(value) self.set_value_from_path(output_data, output_key, value) self.remove_default_from_used(output_key) self.set_bpx_header(output_data) @@ -27,17 +26,10 @@ def map_parameters(self, input_data): def replace_variables(self, value): if isinstance(value, str): - value = re.sub(r'\bx_s\b', 'x', value) - value = re.sub(r'\bc_e\b', 'x', value) + value = re.sub(r"\bx_s\b", "x", value) + value = re.sub(r"\bc_e\b", "x", value) return value - def scale_kinetic_constant(self, value): - try: - return value * 1e6 - except TypeError: - print(f"Error scaling kinetic_constant value: {value}") - return value - def get_all_paths(self, data, path=""): paths = set() if isinstance(data, dict): @@ -66,7 +58,7 @@ def get_value_from_path(self, data, keys): return None return data except (KeyError, IndexError, ValueError, TypeError) as e: - print(f"Error accessing key {key} in path {keys}: {e}") + print(f"Warning: accessing key {key} in path {keys}: {e}") return None def set_value_from_path(self, data, keys, value): @@ -86,7 +78,9 @@ def set_value_from_path(self, data, keys, value): final_key = keys[-1] if isinstance(final_key, str): final_key = final_key.strip() - if isinstance(final_key, int) or (isinstance(final_key, str) and final_key.isdigit()): + if isinstance(final_key, int) or ( + isinstance(final_key, str) and final_key.isdigit() + ): final_key = int(final_key) data[final_key] = value print(f"Set value for path {keys}: {value}") @@ -107,9 +101,13 @@ def set_bpx_header(self, data): "BPX": 0.1, "Title": "An autoconverted parameter set using BatteryModelMapper", "Description": f"This data set was automatically generated from {self.input_url}. Please check carefully.", - "Model": "DFN" + "Model": "DFN", } data.pop("Validation", None) def remove_high_level_defaults(self): - self.defaults_used = {path for path in self.defaults_used if not any(k in path for k in ["Parameterisation", "Header"])} + self.defaults_used = { + path + for path in self.defaults_used + if not any(k in path for k in ["Parameterisation", "Header"]) + } diff --git a/BatteryModelMapper/preprocess_input.py b/BatteryModelMapper/preprocess_input.py new file mode 100644 index 0000000..2b26c87 --- /dev/null +++ b/BatteryModelMapper/preprocess_input.py @@ -0,0 +1,34 @@ +class PreprocessInput: + def __init__(self, input_type, input_data): + self.input_type = input_type + self.input_data = input_data + + def process(self): + if self.input_type == "cidemod": + return self._process_cidemod() + elif self.input_type == "battmo.m": + return self._process_battmo_m() + else: + raise ValueError(f"Unsupported input type: {self.input_type}") + + def _process_cidemod(self): + # Scale kinetic constant + for key, value in self.input_data.items(): + if "kinetic_constant" in key: + self.input_data[key] = value * 1e6 + return self.input_data + + def _process_battmo_m(self): + # Save NE and PE porosities computed from volume fractions + eldes = ["NegativeElectrode", "PositiveElectrode"] + for elde in eldes: + elde_data = self.input_data.get(elde) + co_data = elde_data.get("Coating") + vf = co_data.get("volumeFraction") + if vf is None: + raise ValueError(f"Missing volumeFraction data for {elde}") + else: + porosity = 1.0 - vf + self.input_data[elde]["Coating"]["porosity"] = porosity + + return self.input_data diff --git a/assets/battery-model-lithium-ion.ttl b/assets/battery-model-lithium-ion.ttl new file mode 100644 index 0000000..b33cd43 --- /dev/null +++ b/assets/battery-model-lithium-ion.ttl @@ -0,0 +1,484 @@ +@prefix : . +@prefix owl: . +@prefix rdf: . +@prefix xml: . +@prefix xsd: . +@prefix emmo: . +@prefix rdfs: . +@prefix skos: . +@prefix dcterms: . +@prefix annotations: . +@prefix electrochemistry: . +@base . + + rdf:type owl:Ontology ; + owl:versionIRI ; + owl:imports . + +################################################################# +# Annotation properties +################################################################# + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#bmli_0a5b99ee_995b_4899_a79b_925a4086da37 +:bmli_0a5b99ee_995b_4899_a79b_925a4086da37 rdf:type owl:AnnotationProperty ; + skos:prefLabel "bpxKey"@en ; + rdfs:subPropertyOf rdfs:label . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 +:bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 rdf:type owl:AnnotationProperty ; + skos:prefLabel "cidemodKey"@en ; + rdfs:subPropertyOf rdfs:label . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 +:bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 rdf:type owl:AnnotationProperty ; + skos:prefLabel "battmoKey"@en ; + rdfs:subPropertyOf rdfs:label . + + +################################################################# +# Classes +################################################################# + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_098f98dc_e015_4dbd_b358_a7ac3b3ecff3 +:modellib_098f98dc_e015_4dbd_b358_a7ac3b3ecff3 rdf:type owl:Class ; + rdfs:subClassOf emmo:EMMO_d5be1faf_0c56_4f5a_9b78_581e6dee949f ; + skos:prefLabel "InitialLithiumConcentrationInElectrolyte"@en ; + emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "the initial amount concentration of lithium in an electrolyte"@en ; + emmo:hasMeasurementUnit emmo:molPerM3 ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Electrolyte','Initial concentration [mol.m-3]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['electrolyte','initial_concentration','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['Electrolyte', 'species', 'nominalConcentration']"@en . + + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_0a1e73c5_e91b_4365_88d4_1e1f476bf776 +:modellib_0a1e73c5_e91b_4365_88d4_1e1f476bf776 rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_a5571263_f153_448f_84a3_cd18092cf8fa ; + skos:prefLabel "PositiveElectrodeActiveMaterialVolumetricSurfaceArea"@en ; + emmo:hasMeasurementUnit emmo:meterPowerNMinusOne ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Positive electrode','Surface area per unit volume [m-1]']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['PositiveElectrode','Coating','ActiveMaterial','Interface','volumetricSurfaceArea']"@en. + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_0e2f4fe6_570a_4d13_81e9_de1d4f9987af +:modellib_0e2f4fe6_570a_4d13_81e9_de1d4f9987af rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_9c657fdc_b9d3_4964_907c_f9a6e8c5f52b ; + skos:prefLabel "NegativeElectrodeActiveMaterialOpenCircuitVoltage"@en ; + emmo:hasMeasurementUnit emmo:volt ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Negative electrode','OCP [V]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['negative_electrode','active_materials',0,'OCP','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['NegativeElectrode','Coating','ActiveMaterial','Interface','openCircuitPotential']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_0ea21f71_d1bd_4714_a260_b991e6d4bcf7 +:modellib_0ea21f71_d1bd_4714_a260_b991e6d4bcf7 rdf:type owl:Class ; + rdfs:subClassOf emmo:EMMO_06448f64_8db6_4304_8b2c_e785dba82044 ; + skos:prefLabel "BatteryCellDensity"@en ; + emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "the overall density of a lithium-ion battery cell, calculated as the quotient of the total mass and the total volume"@en ; + emmo:hasMeasurementUnit emmo:kgPerM3 ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Cell','Density [kg.m-3]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['properties','density','value']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_1923575e_05b0_4b8b_8d58_0b2f2ba41c3e +:modellib_1923575e_05b0_4b8b_8d58_0b2f2ba41c3e rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_25dabdc2_68bf_4a38_8cbe_11be017358bc ; + skos:prefLabel "ElectrolyteConductivity"@en ; + emmo:hasMeasurementUnit emmo:siemensPerMeter ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Electrolyte','Conductivity [S.m-1]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['electrolyte','ionic_conductivity','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['Electrolyte','ionicConductivity']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_21da0fe9_9fb6_4840_a12f_fbcc1ba84fb3 +:modellib_21da0fe9_9fb6_4840_a12f_fbcc1ba84fb3 rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_f22bd1ec_faca_4335_92a5_a1687154c622 ; + skos:prefLabel "NegativeElectrodeLithiumStoichiometricCoefficientAtSOC0"@en ; + emmo:hasMeasurementUnit emmo:dimensionless ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Negative electrode','Minimum stoichiometry']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['negative_electrode','active_materials',0,'stoichiometry0','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['NegativeElectrode','Coating','ActiveMaterial','Interface','guestStoichiometry0']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_404126e0_cb1b_44e4_98dc_2474185767a1 +:modellib_404126e0_cb1b_44e4_98dc_2474185767a1 rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_0335e3f6_d1d8_4daa_8376_a9285f1bc9f1 ; + skos:prefLabel "PositiveElectrodeReactionRateConstant"@en ; + emmo:hasMeasurementUnit emmo:molPerM2PerS ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Positive electrode','Reaction rate constant [mol.m-2.s-1]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['positive_electrode','active_materials',0,'kinetic_constant','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['PositiveElectrode','Coating','ActiveMaterial','Interface','reactionRateConstant']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_43f77743_1af6_4a0f_9cc6_285c2a450549 +:modellib_43f77743_1af6_4a0f_9cc6_285c2a450549 rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_ce74d2dc_d496_4116_b2fb_3e83d88bc744 ; + skos:prefLabel "PositiveElectrodeElectronicConductivity"@en ; + emmo:hasMeasurementUnit emmo:siemensPerMeter ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Positive electrode','Conductivity [S.m-1]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['positive_electrode','electronic_conductivity','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['PositiveElectrode','Coating','electronicConductivity']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_47288277_4aed_447e_b659_0c975d031406 +:modellib_47288277_4aed_447e_b659_0c975d031406 rdf:type owl:Class ; + rdfs:subClassOf emmo:EMMO_43003c86_9d15_433b_9789_ee2940920656 ; + skos:prefLabel "SeparatorThickness"@en ; + emmo:hasMeasurementUnit emmo:meter ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Separator','Thickness [m]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['separator','thickness','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['Separator','thickness']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_4c274506_af5b_4ef1_8217_829ffd459f28 +:modellib_4c274506_af5b_4ef1_8217_829ffd459f28 rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_37b24a94_cae0_4d7a_9519_9f7692dec607 ; + skos:prefLabel "LithiumDiffusivityInElectrolyte"@en ; + emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "the diffusivity of lithium in an electrolyte"@en ; + emmo:hasMeasurementUnit emmo:meterPower2PerSecond ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Electrolyte','Diffusivity [m2.s-1]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['electrolyte','diffusion_constant','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['Electrolyte','diffusionCoefficient']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_4d69edda_d2fa_40b0_9c1e_52e08debf578 +:modellib_4d69edda_d2fa_40b0_9c1e_52e08debf578 rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_d7f8cab9_b035_4ecd_be63_292672572526 ; + skos:prefLabel "ActivationEnergyOfLithiumDiffusivityInPositiveElectrode"@en ; + emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "the activation energy barrier in an Arrhenius expression for the diffusivity of lithium in the positive electrode"@en ; + emmo:hasMeasurementUnit emmo:joulePerMol ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Positive electrode','Diffusivity activation energy [J.mol-1]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['positive_electrode','active_materials',0,'diffusion_constant','arrhenius','activation_energy']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['PositiveElectrode','Coating','ActiveMaterial','SolidDiffusion','activationEnergyOfDiffusion']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_50247e71_75fe_4986_959e_fd06c6be98db +:modellib_50247e71_75fe_4986_959e_fd06c6be98db rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_37b24a94_cae0_4d7a_9519_9f7692dec607 ; + skos:prefLabel "LithiumDiffusivityInNegativeElectrode"@en ; + emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "the diffusivity of lithium in the negative electrode"@en ; + emmo:hasMeasurementUnit emmo:meterPower2PerSecond ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Negative electrode','Diffusivity [m2.s-1]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['negative_electrode','active_materials',0,'diffusion_constant','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['NegativeElectrode','Coating','ActiveMaterial','SolidDiffusion','referenceDiffusionCoefficient']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_52ab4fdd_f945_4541_9ce6_cd6fd3a05861 +:modellib_52ab4fdd_f945_4541_9ce6_cd6fd3a05861 rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_9c657fdc_b9d3_4964_907c_f9a6e8c5f52b ; + skos:prefLabel "PositiveElectrodeActiveMaterialOpenCircuitVoltage"@en ; + emmo:hasMeasurementUnit emmo:volt ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Positive electrode','OCP [V]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['positive_electrode','active_materials',0,'OCP','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['PositiveElectrode','Coating','ActiveMaterial','Interface','openCircuitPotential']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_56b9cd1f_5397_4385_9292_30d93d9e7a05 +:modellib_56b9cd1f_5397_4385_9292_30d93d9e7a05 rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_d7f8cab9_b035_4ecd_be63_292672572526 ; + skos:prefLabel "PostiveElectrodeActivationEnergyOfReaction"@en ; + emmo:hasMeasurementUnit emmo:joulePerMol ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Positive electrode','Reaction rate constant activation energy [J.mol-1]']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['PositiveElectrode','Coating','ActiveMaterial','Interface','activationEnergyOfReaction']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_56de36fe_e8e1_486c_8d29_061ac8d28c13 + :modellib_56de36fe_e8e1_486c_8d29_061ac8d28c13 rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_d7f8cab9_b035_4ecd_be63_292672572526 ; + skos:prefLabel "ActivationEnergyOfLithiumDiffusivityInElectrolyte"@en ; + emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "the activation energy barrier in an Arrhenius expression for the diffusivity of lithium in an electrolyte"@en ; + emmo:hasMeasurementUnit emmo:joulePerMol ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Electrolyte','Diffusivity activation energy [J.mol-1]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['electrolyte','diffusion_constant','arrhenius','activation_energy']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_58400817_3282_46e5_942e_3a1538631403 +:modellib_58400817_3282_46e5_942e_3a1538631403 rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_b92e382f_5109_4f60_ab5e_c89d340419a9 ; + skos:prefLabel "PositiveElectrodeActiveMaterialParticleRadius"@en ; + emmo:hasMeasurementUnit emmo:meter ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Positive electrode','Particle radius [m]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['positive_electrode','active_materials',0,'particle_radius','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['PositiveElectrode','Coating','ActiveMaterial','SolidDiffusion','particleRadius']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_5cb403c4_4f28_46cb_81c4_21c5c47ef14a +:modellib_5cb403c4_4f28_46cb_81c4_21c5c47ef14a rdf:type owl:Class ; + rdfs:subClassOf emmo:EMMO_3a6578ac_aee0_43b9_9bc6_1eb208c8c9a9 ; + skos:prefLabel "NegativeElectrodeCoatingPorosity"@en ; + emmo:hasMeasurementUnit emmo:dimensionless ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Negative electrode','Porosity']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['negative_electrode','porosity','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['NegativeElectrode','Coating','porosity']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_62f5beeb_6d1e_442a_8048_3ebe08882964 +:modellib_62f5beeb_6d1e_442a_8048_3ebe08882964 rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_3b938708_e7e4_4ac0_a959_9c04306302e7 ; + skos:prefLabel "PositiveElectrodeCoatingThickness"@en ; + emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "thickness of the positive electrode coating"@en ; + emmo:hasMeasurementUnit emmo:meter ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Positive electrode','Thickness [m]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['positive_electrode','thickness','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['PositiveElectrode','Coating','thickness']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_7481c4c9_c247_4248_a045_a1077230acba +:modellib_7481c4c9_c247_4248_a045_a1077230acba rdf:type owl:Class ; + rdfs:subClassOf emmo:EMMO_3a6578ac_aee0_43b9_9bc6_1eb208c8c9a9 ; + skos:prefLabel "PositiveElectrodeCoatingPorosity"@en ; + emmo:hasMeasurementUnit emmo:dimensionless ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Positive electrode','Porosity']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['positive_electrode','porosity','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['PositiveElectrode','Coating','porosity']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_80920875_62ac_4e29_b970_ec4316e76aa5 +:modellib_80920875_62ac_4e29_b970_ec4316e76aa5 rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_f22bd1ec_faca_4335_92a5_a1687154c622 ; + skos:prefLabel "PositiveElectrodeLithiumStoichiometricCoefficientAtSOC0"@en ; + emmo:hasMeasurementUnit emmo:dimensionless ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Positive electrode','Minimum stoichiometry']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['positive_electrode','active_materials',0,'stoichiometry1','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['PositiveElectrode','Coating','ActiveMaterial','Interface','guestStoichiometry0']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_86af4487_33c1_4562_a00b_3a8252ffe378 +:modellib_86af4487_33c1_4562_a00b_3a8252ffe378 rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_d7f8cab9_b035_4ecd_be63_292672572526 ; + skos:prefLabel "ActivationEnergyOfLithiumDiffusivityInNegativeElectrode"@en ; + emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "the activation energy barrier in an Arrhenius expression for the diffusivity of lithium in the negative electrode"@en ; + emmo:hasMeasurementUnit emmo:joulePerMol ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Negative electrode','Diffusivity activation energy [J.mol-1]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['negative_electrode','active_materials',0,'diffusion_constant','arrhenius','activation_energy']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['NegativeElectrode','Coating','ActiveMaterial','SolidDiffusion','activationEnergyOfDiffusion']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_8c16cb12_41c1_43bd_9e7c_2eea7b06a1f0 +:modellib_8c16cb12_41c1_43bd_9e7c_2eea7b06a1f0 rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_d7f8cab9_b035_4ecd_be63_292672572526 ; + skos:prefLabel "ActivationEnergyOfElectrolyteConductivity"@en ; + emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "the activation energy barrier in an Arrhenius expression for the ionic conductivity of an electrolyte"@en ; + emmo:hasMeasurementUnit emmo:joulePerMol ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Electrolyte','Conductivity activation energy [J.mol-1]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['electrolyte','ionic_conductivity','arrhenius','activation_energy']"@en . + + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_8c336ae9_1818_4b08_a660_4bb83b28351f +:modellib_8c336ae9_1818_4b08_a660_4bb83b28351f rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_38ab058e_3912_48c2_a7eb_76d25d000820 ; + skos:prefLabel "NegativeElectrodeLithiumStoichiometricCoefficientAtSOC100"@en ; + emmo:hasMeasurementUnit emmo:dimensionless ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Negative electrode','Maximum stoichiometry']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['negative_electrode','active_materials',0,'stoichiometry1','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['NegativeElectrode','Coating','ActiveMaterial','Interface','guestStoichiometry100']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_99041897_5c08_40ed_9118_3e77e9b0e191 +:modellib_99041897_5c08_40ed_9118_3e77e9b0e191 rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_38ab058e_3912_48c2_a7eb_76d25d000820 ; + skos:prefLabel "PositiveElectrodeLithiumStoichiometricCoefficientAtSOC100"@en ; + emmo:hasMeasurementUnit emmo:dimensionless ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Positive electrode','Maximum stoichiometry']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['positive_electrode','active_materials',0,'stoichiometry0','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['PositiveElectrode','Coating','ActiveMaterial','Interface','guestStoichiometry100']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_9cba2158_26ba_4dd7_b082_ba66dbb960c7 +:modellib_9cba2158_26ba_4dd7_b082_ba66dbb960c7 rdf:type owl:Class ; + rdfs:subClassOf emmo:EMMO_b4f4ed28_d24c_4a00_9583_62ab839abeca ; + skos:prefLabel "BatteryCellLumpedSpecificHeatCapacity"@en ; + emmo:hasMeasurementUnit emmo:joulePerKelvinPerKilogram ; + emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "the lumped specific heat capacity of a battery cell"@en ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Cell','Specific heat capacity [J.K-1.kg-1]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['properties','specific_heat','value']"@en . + + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_9d558b56_d3b8_429a_a4e2_d2ffab895e42 +:modellib_9d558b56_d3b8_429a_a4e2_d2ffab895e42 rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_3bb5ae23_59fa_4bc7_9495_803eb6719f28 ; + skos:prefLabel "PositiveElectrodeEntropicChangeCoefficient"@en ; + emmo:hasMeasurementUnit emmo:voltPerKelvin ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Positive electrode','Entropic change coefficient [V.K-1]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['positive_electrode','active_materials',0,'entropy_coefficient','value']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_a4858e4d_dd3b_48ce_97ba_3eeb8571b633 +:modellib_a4858e4d_dd3b_48ce_97ba_3eeb8571b633 rdf:type owl:Class ; + rdfs:subClassOf emmo:EMMO_3a6578ac_aee0_43b9_9bc6_1eb208c8c9a9 ; + skos:prefLabel "SeparatorPorosity"@en ; + emmo:hasMeasurementUnit emmo:dimensionless ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Separator','Porosity']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['separator','porosity','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['Separator','porosity']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_b4184e46_c53c_47cc_9bfc_186fd77836a5 +:modellib_b4184e46_c53c_47cc_9bfc_186fd77836a5 rdf:type owl:Class ; + rdfs:subClassOf emmo:EMMO_f1a51559_aa3d_43a0_9327_918039f0dfed ; + skos:prefLabel "BatteryCellVolume"@en ; + emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "the total volume of a battery cell, determined by its external dimensions"@en ; + emmo:hasMeasurementUnit emmo:cubicMeter ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Cell','Volume [m3]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['properties','volume','value']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_be3da3e2_58a9_4e58_adc2_7336d312717c +:modellib_be3da3e2_58a9_4e58_adc2_7336d312717c rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_ce74d2dc_d496_4116_b2fb_3e83d88bc744 ; + skos:prefLabel "NegativeElectrodeElectronicConductivity"@en ; + emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "the electronic conductivity of the negative electrode"@en ; + emmo:hasMeasurementUnit emmo:siemensPerMeter ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Negative electrode','Conductivity [S.m-1]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['negative_electrode','electronic_conductivity','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['NegativeElectrode','Coating','electronicConductivity']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_bfe553c2_a63e_49b6_a209_0855dfc39724 +:modellib_bfe553c2_a63e_49b6_a209_0855dfc39724 rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_b92e382f_5109_4f60_ab5e_c89d340419a9 ; + skos:prefLabel "NegativeElectrodeActiveMaterialParticleRadius"@en ; + emmo:hasMeasurementUnit emmo:meter ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Negative electrode','Particle radius [m]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['negative_electrode','active_materials',0,'particle_radius','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['NegativeElectrode','Coating','ActiveMaterial','SolidDiffusion','particleRadius']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_c5dcb1a2_f2cf_421a_b8ae_47a88a61fce3 +:modellib_c5dcb1a2_f2cf_421a_b8ae_47a88a61fce3 rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_0335e3f6_d1d8_4daa_8376_a9285f1bc9f1 ; + skos:prefLabel "NegativeElectrodeReactionRateConstant"@en ; + emmo:hasMeasurementUnit emmo:molPerMeterPower2PerSecond ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Negative electrode','Reaction rate constant [mol.m-2.s-1]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['negative_electrode','active_materials',0,'kinetic_constant','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['NegativeElectrode','Coating','ActiveMaterial','Interface','reactionRateConstant']"@en . + + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_c5f9b91e_a770_4e9b_837e_fa2a76019111 + :modellib_c5f9b91e_a770_4e9b_837e_fa2a76019111 rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_a5571263_f153_448f_84a3_cd18092cf8fa ; + skos:prefLabel "NegativeElectrodeActiveMaterialVolumetricSurfaceArea"@en ; + emmo:hasMeasurementUnit emmo:meterPowerMinus1 ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Negative electrode','Surface area per unit volume [m-1]']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['NegativeElectrode','Coating','ActiveMaterial','Interface','volumetricSurfaceArea']"@en . + + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_c69a9d55_823f_4113_a305_ebc89dde7de3 + :modellib_c69a9d55_823f_4113_a305_ebc89dde7de3 rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_47287d09_6108_45ca_ac65_8b9451b1065e ; + skos:prefLabel "PositiveElectrodeMaximumLithiumConcentration"@en ; + emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "maximum concentration of lithium in the positive electrode"@en ; + emmo:hasMeasurementUnit emmo:molPerCubicMeter ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Positive electrode','Maximum concentration [mol.m-3]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['positive_electrode','active_materials',0,'maximum_concentration','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['PositiveElectrode','Coating','ActiveMaterial','Interface','saturationConcentration']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_ccde4e5f_ace4_41d1_b4d8_cbd63e6376e6 +:modellib_ccde4e5f_ace4_41d1_b4d8_cbd63e6376e6 rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_05cf26ef_782a_4f66_a196_7004dd973f8e ; + skos:prefLabel "BatteryCellSurfaceArea"@en ; + emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "the external surface area of a battery cell"@en ; + emmo::hasMeasurementUnit emmo:meterPower2 ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Parameterisation','Cell','External surface area [m2]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['properties','external_surface_area','value']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_cdc91ec0_9fc5_4551_bbd9_6824c2920124 +:modellib_cdc91ec0_9fc5_4551_bbd9_6824c2920124 rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_3b938708_e7e4_4ac0_a959_9c04306302e7 ; + skos:prefLabel "NegativeElectrodeCoatingThickness"@en ; + emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "thickness of the negative electrode coating"@en ; + emmo:hasMeasurementUnit emmo:meter ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Negative electrode','Thickness [m]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['negative_electrode','thickness','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['NegativeElectrode','Coating','thickness']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_e3e78df2_d568_4ab7_8c0d_d3a2ee3ae282 +:modellib_e3e78df2_d568_4ab7_8c0d_d3a2ee3ae282 rdf:type owl:Class ; + rdfs:subClassOf emmo:EMMO_d97b27cb_61a4_4568_a38b_4edd4f224acc ; + skos:prefLabel "LithiumTransportNumber"@en ; + emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "transport number of lithium ion in electrolyte"@en ; + emmo:hasMeasurementUnit emmo:dimensionless ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Electrolyte','Cation transference number']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['electrolyte','transference_number','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['Electrolyte','species', 'transferenceNumber']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_e59188bb_ce66_49f6_84aa_ecb98e76941e +:modellib_e59188bb_ce66_49f6_84aa_ecb98e76941e rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_37b24a94_cae0_4d7a_9519_9f7692dec607 ; + skos:prefLabel "LithiumDiffusivityInPositiveElectrode"@en ; + emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "the diffusivity of lithium in the positive electrode"@en ; + emmo:hasMeasurementUnit emmo:squareMeterPerSecond ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Positive electrode','Diffusivity [m2.s-1]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['positive_electrode','active_materials',0,'diffusion_constant','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['PositiveElectrode','Coating','ActiveMaterial','SolidDiffusion','referenceDiffusionCoefficient']"@en . + + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_e808a26a_5812_49e9_894c_b793c7fe0c38 + :modellib_e808a26a_5812_49e9_894c_b793c7fe0c38 rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_47287d09_6108_45ca_ac65_8b9451b1065e ; + skos:prefLabel "NegativeElectrodeMaximumLithiumConcentration"@en ; + emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "maximum concentration of lithium in the negative electrode"@en ; + emmo:hasMeasurementUnit emmo:molPerCubicMeter ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Negative electrode','Maximum concentration [mol.m-3]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['negative_electrode','active_materials',0,'maximum_concentration','value']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['NegativeElectrode','Coating','ActiveMaterial','Interface','saturationConcentration']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_eac57b09_5cc9_41d7_b2c8_40218d7fd47c +:modellib_eac57b09_5cc9_41d7_b2c8_40218d7fd47c rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_3bb5ae23_59fa_4bc7_9495_803eb6719f28 ; + skos:prefLabel "NegativeElectrodeEntropicChangeCoefficient"@en ; + emmo:hasMeasurementUnit emmo:voltPerKelvin ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Negative electrode','Entropic change coefficient [V.K-1]']"@en ; + :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['negative_electrode','active_materials',0,'entropy_coefficient','value']"@en . + + +### https://w3id.org/emmo/domain/battery-model-lithium-ion#modellib_fda9539d_5232_471c_8945_b9a8ec7247fe +:modellib_fda9539d_5232_471c_8945_b9a8ec7247fe rdf:type owl:Class ; + rdfs:subClassOf electrochemistry:electrochemistry_d7f8cab9_b035_4ecd_be63_292672572526 ; + skos:prefLabel "NegativeElectrodeActivationEnergyOfReaction"@en ; + emmo:hasMeasurementUnit emmo:joulePerMol ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Negative electrode','Reaction rate constant activation energy [J.mol-1]']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['NegativeElectrode','Coating','ActiveMaterial','Interface','activationEnergyOfReaction']"@en . + + + +################################################################# +# Annotations +################################################################# + +electrochemistry:electrochemistry_39a44af0_0e1a_4859_b550_bdabad64386e :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['simulation','initial_state','exterior_temperature','value']"@en ; +emmo:hasMeasurementUnit emmo:kelvin ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Cell','Ambient temperature [K]']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['ThermalModel','externalTemperature']"@en . + + +electrochemistry:electrochemistry_534dd59c_904c_45d9_8550_ae9d2eb6bbc9 :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['simulation','triggers','voltage','lower']"@en ; +emmo:hasMeasurementUnit emmo:volt ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Cell','Lower voltage cut-off [V]']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['Control','lowerCutoffVoltage']"@en . + + +electrochemistry:electrochemistry_9c9b80a4_a00b_4b91_8e17_3a7831f2bf2f :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['simulation','initial_state','initial_temperature','value']"@en ; +emmo:hasMeasurementUnit emmo:kelvin ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Cell','Initial temperature [K]']"@en ; + :bmli_e5e86474_8623_48ea_a1cf_502bdb10aa14 "['initT']"@en . + + +electrochemistry:electrochemistry_fa7790d6_07bb_4b0f_9965_55966828f5f3 :bmli_1b718841_5d72_4071_bb71_fc4a754f5e30 "['properties','area','value']"@en ; +emmo:hasMeasurementUnit emmo:meterPower2 ; + :bmli_0a5b99ee_995b_4899_a79b_925a4086da37 "['Parameterisation','Cell','Electrode area [m2]']"@en . + + +### Generated by the OWL API (version 4.5.9.2019-02-01T07:24:44Z) https://github.com/owlcs/owlapi diff --git a/main.py b/main.py new file mode 100644 index 0000000..8c16137 --- /dev/null +++ b/main.py @@ -0,0 +1,147 @@ +import json +import argparse +import BatteryModelMapper as bmm + + +def fix_battmo_porosity(label, input_path, input_data): + if ( + label == "PositiveElectrodeCoatingPorosity" + or label == "NegativeElectrodeCoatingPorosity" + ): + # Get the volume fraction of active material in the positive electrode coating + vf_path = list(input_path) + vf_path[-1] = "volumeFraction" + vf_value = ParameterMapper.get_value_from_path(input_data, vf_path) + if vf_value is None: + ValueError( + f"Could not find volumeFraction for porosity calculcation for {label}" + ) + else: + porosity = 1.0 - vf_value + return porosity + + +def run( + input_file, + input_type, + output_file, + output_type, + cell_id, + cell_type, + ontology_ref="assets/battery-model-lithium-ion.ttl", + template_ref="assets/bpx_template.json", +): + + # Initialize the OntologyParser + ontology_parser = bmm.OntologyParser(ontology_ref) + + # Load the input JSON file + input_data = bmm.JSONLoader.load(input_file) + # print("Input Data:", json.dumps(input_data, indent=4)) + + # Preprocessing + preprocessor = bmm.PreprocessInput(input_type, input_data) + input_data = preprocessor.process() + + if output_type == "jsonld": + + bmm.export_jsonld( + ontology_parser=ontology_parser, + input_type=input_type, + input_data=input_data, + output_path=output_file, + cell_id=cell_id, + cell_type=cell_type, + ) + + else: + + mappings = ontology_parser.get_mappings(input_type, output_type) + print( + "Mappings:", + json.dumps({str(k): str(v) for k, v in mappings.items()}, indent=4), + ) + + # Load the template JSON file + template_data = bmm.JSONLoader.load(template_ref) + template_data.pop( + "Validation", None + ) # Remove validation if it exists in the template + + # Map the parameters using the mappings from the ontology + parameter_mapper = bmm.ParameterMapper( + mappings, template_data, input_file, output_type, input_type + ) + output_data = parameter_mapper.map_parameters(input_data) + # defaults_used_data = list(parameter_mapper.defaults_used) + bmm.JSONWriter().write(output_data, output_file) + + +def input_parser(): + + parser = argparse.ArgumentParser(description="Battery Model Mapper CLI") + + types = ["bpx", "cidemod", "battmo.m", "jsonld"] + input_types = list(set(types) - {"jsonld"}) + output_types = types + + parser.add_argument("-input-file", required=True, help="Input filename") + parser.add_argument( + "-input-type", + required=True, + help=f"Input type string (must be one of {input_types})", + ) + parser.add_argument( + "-output-file", required=False, default="output.jsonld", help="Output filename" + ) + parser.add_argument( + "-output-type", + required=False, + default="jsonld", + help=f"Output type string (must be one of {output_types})", + ) + parser.add_argument( + "-cell-id", + required=False, + default="Cell ID", + help="Cell ID (eg BattMo) for JSON-LD output", + ) + parser.add_argument( + "-cell-type", + required=False, + default="Pouch", + help="Cell Type (eg Pouch) for JSON-LD output", + ) + parser.add_argument( + "-ontology-ref", + default="assets/battery-model-lithium-ion.ttl", + help="Ontology file path", + ) + parser.add_argument( + "-template-ref", default="assets/bpx_template.json", help="Template file path" + ) + return parser, input_types, output_types + + +if __name__ == "__main__": + + # Parse input + parser, input_types, output_types = input_parser() + args = parser.parse_args() + + # Check the input and output types + if args.input_type not in input_types: + raise ValueError(f"Invalid input type: {args.input_type}") + if args.output_type not in output_types: + raise ValueError(f"Invalid output type: {args.output_type}") + + run( + input_file=args.input_file, + input_type=args.input_type, + output_file=args.output_file, + output_type=args.output_type, + cell_id=args.cell_id, + cell_type=args.cell_type, + ontology_ref=args.ontology_ref, + template_ref=args.template_ref, + ) diff --git a/test.py b/test.py deleted file mode 100644 index 3086a40..0000000 --- a/test.py +++ /dev/null @@ -1,245 +0,0 @@ -import json -import ast -import requests -import pybamm -from rdflib import Graph, URIRef -from jsonschema import validate, ValidationError -import re - -class OntologyParser: - def __init__(self, ontology_url): - self.graph = Graph() - response = requests.get(ontology_url) - response.raise_for_status() - self.graph.parse(data=response.text, format='ttl') - self.key_map = { - 'bpx': URIRef("https://w3id.org/emmo/domain/battery-model-lithium-ion#bmli_0a5b99ee_995b_4899_a79b_925a4086da37"), - 'cidemod': URIRef("https://w3id.org/emmo/domain/battery-model-lithium-ion#bmli_1b718841_5d72_4071_bb71_fc4a754f5e30"), - 'battmo': URIRef("https://w3id.org/emmo/domain/battery-model-lithium-ion#bmli_2c718841_6d73_5082_bb81_gc5b754f6e40") # Placeholder URI - } - - def parse_key(self, key): - try: - return ast.literal_eval(key) - except (ValueError, SyntaxError) as e: - print(f"Error parsing key: {key} - {e}") - return [] - - def get_mappings(self, input_type, output_type): - input_key = self.key_map.get(input_type) - output_key = self.key_map.get(output_type) - if not input_key or not output_key: - raise ValueError(f"Invalid input or output type: {input_type}, {output_type}") - - mappings = {} - for subject in self.graph.subjects(): - input_value = None - output_value = None - for predicate, obj in self.graph.predicate_objects(subject): - if predicate == input_key: - input_value = self.parse_key(str(obj)) - elif predicate == output_key: - output_value = self.parse_key(str(obj)) - if input_value and output_value: - mappings[tuple(input_value)] = tuple(output_value) - print(f"Mapping added: {tuple(input_value)} -> {tuple(output_value)}") - return mappings - -class JSONLoader: - @staticmethod - def load(json_url): - response = requests.get(json_url) - response.raise_for_status() - return response.json() - -class JSONValidator: - @staticmethod - def validate(data, schema_url): - schema = JSONLoader.load(schema_url) - try: - validate(instance=data, schema=schema) - print("JSON is valid.") - except ValidationError as e: - print(f"JSON validation error: {e.message}") - raise - -class JSONWriter: - @staticmethod - def write(data, output_path): - with open(output_path, 'w') as file: - json.dump(data, file, indent=4) - -class ParameterMapper: - def __init__(self, mappings, template, input_url, output_type, input_type): - self.mappings = mappings - self.template = template - self.input_url = input_url - self.output_type = output_type - self.input_type = input_type - self.defaults_used = set(self.get_all_paths(template)) - - def map_parameters(self, input_data): - output_data = self.template.copy() - for input_key, output_key in self.mappings.items(): - value = self.get_value_from_path(input_data, input_key) - if value is not None: - if isinstance(value, str): - value = self.replace_variables(value) - if self.input_type == 'cidemod' and 'kinetic_constant' in input_key: - value = self.scale_kinetic_constant(value) - self.set_value_from_path(output_data, output_key, value) - self.remove_default_from_used(output_key) - self.set_bpx_header(output_data) - self.remove_high_level_defaults() - return output_data - - def replace_variables(self, value): - if isinstance(value, str): - value = re.sub(r'\bx_s\b', 'x', value) - value = re.sub(r'\bc_e\b', 'x', value) - return value - - def scale_kinetic_constant(self, value): - try: - return value * 1e6 - except TypeError: - print(f"Error scaling kinetic_constant value: {value}") - return value - - def get_all_paths(self, data, path=""): - paths = set() - if isinstance(data, dict): - for key, value in data.items(): - current_path = f"{path}.{key}" if path else key - paths.add(current_path) - paths.update(self.get_all_paths(value, current_path)) - elif isinstance(data, list): - for i, item in enumerate(data): - current_path = f"{path}[{i}]" - paths.add(current_path) - paths.update(self.get_all_paths(item, current_path)) - return paths - - def get_value_from_path(self, data, keys): - try: - for key in keys: - if isinstance(key, str): - key = key.strip() - if isinstance(data, dict): - data = data[key] - elif isinstance(data, list): - key = int(key) # Convert key to integer for list index - data = data[key] - else: - return None - return data - except (KeyError, IndexError, ValueError, TypeError) as e: - print(f"Error accessing key {key} in path {keys}: {e}") - return None - - def set_value_from_path(self, data, keys, value): - try: - for key in keys[:-1]: - if isinstance(key, str): - key = key.strip() - if isinstance(key, int) or key.isdigit(): - key = int(key) - while len(data) <= key: - data.append({}) - data = data[key] - else: - if key not in data: - data[key] = {} - data = data[key] - final_key = keys[-1] - if isinstance(final_key, str): - final_key = final_key.strip() - if isinstance(final_key, int) or (isinstance(final_key, str) and final_key.isdigit()): - final_key = int(final_key) - data[final_key] = value - print(f"Set value for path {keys}: {value}") - except (KeyError, IndexError, ValueError, TypeError) as e: - print(f"Error setting value for path {keys}: {e}") - - def remove_default_from_used(self, keys): - path = "Parameterisation" - for key in keys: - if isinstance(key, str): - path += f".{key.strip()}" - elif isinstance(key, int): - path += f"[{key}]" - self.defaults_used.discard(path) - - def set_bpx_header(self, data): - data["Header"] = { - "BPX": 0.1, - "Title": "An autoconverted parameter set using BatteryModelMapper", - "Description": f"This data set was automatically generated from {self.input_url}. Please check carefully.", - "Model": "DFN" - } - data.pop("Validation", None) - - def remove_high_level_defaults(self): - self.defaults_used = {path for path in self.defaults_used if not any(k in path for k in ["Parameterisation", "Header"])} - -if __name__ == "__main__": - ontology_url = 'https://w3id.org/emmo/domain/battery-model-lithium-ion/latest' - input_json_url = 'https://raw.githubusercontent.com/cidetec-energy-storage/cideMOD/main/data/data_Chen_2020/params_tuned_vOCPexpression.json' - output_json_path = 'converted_battery_parameters.json' - defaults_json_path = 'defaults_used.json' - template_url = 'https://raw.githubusercontent.com/BIG-MAP/ModelMapper/main/assets/bpx_template.json' - input_type = 'cidemod' - output_type = 'bpx' - - # Initialize the OntologyParser - ontology_parser = OntologyParser(ontology_url) - mappings = ontology_parser.get_mappings(input_type, output_type) - print("Mappings:", json.dumps({str(k): str(v) for k, v in mappings.items()}, indent=4)) - - # Load the input JSON file - input_data = JSONLoader.load(input_json_url) - print("Input Data:", json.dumps(input_data, indent=4)) - - # Load the template JSON file - template_data = JSONLoader.load(template_url) - template_data.pop("Validation", None) # Remove validation if it exists in the template - - # Map the parameters using the mappings from the ontology - parameter_mapper = ParameterMapper(mappings, template_data, input_json_url, output_type, input_type) - output_data = parameter_mapper.map_parameters(input_data) - defaults_used_data = list(parameter_mapper.defaults_used) - print("Output Data:", json.dumps(output_data, indent=4)) - - # Write the output JSON file - JSONWriter.write(output_data, output_json_path) - - # Write the defaults used JSON file - JSONWriter.write(defaults_used_data, defaults_json_path) - - # Load the DFN model - model = pybamm.lithium_ion.DFN() - - # Load the parameter values - parameter_values = pybamm.ParameterValues.create_from_bpx('converted_battery_parameters.json') - - # Define the experiment: Charge from SOC=0.01, then discharge - experiment = pybamm.Experiment([ - ("Charge at C/5 until 4.2 V", - "Hold at 4.2 V until 1 mA", - "Rest for 1 hour", - "Discharge at C/5 until 2.5 V") - ]) - - # Create the simulation with the experiment - sim = pybamm.Simulation(model, experiment=experiment, parameter_values=parameter_values) - - - # Define initial concentration in negative and positive electrodes - parameter_values["Initial concentration in negative electrode [mol.m-3]"] = 0.0279 * parameter_values["Maximum concentration in negative electrode [mol.m-3]"] - parameter_values["Initial concentration in positive electrode [mol.m-3]"] = 0.9084 * parameter_values["Maximum concentration in positive electrode [mol.m-3]"] - - # Solve the simulation - sim.solve() - - # Plot the results - sim.plot() diff --git a/test/test_against_original_modelmapper.py b/test/test_against_original_modelmapper.py new file mode 100644 index 0000000..f8ba325 --- /dev/null +++ b/test/test_against_original_modelmapper.py @@ -0,0 +1,37 @@ +import pytest + + +def test_bpx_to_cidemod_conversion(): + # Check that the conversion made by the updated modelmapper + # matches the original original + pass + + +def test_bpx_to_battmo_conversion(): + # Check that the conversion made by the updated modelmapper + # matches the original original + pass + + +def test_cidemod_to_battmo_conversion(): + # Check that the conversion made by the updated modelmapper + # matches the original original + pass + + +def test_cidemod_to_bpx_conversion(): + # Check that the conversion made by the updated modelmapper + # matches the original original + pass + + +def test_battmo_to_bpx_conversion(): + # Check that the conversion made by the updated modelmapper + # matches the original original + pass + + +def test_battmo_to_cidemod_conversion(): + # Check that the conversion made by the updated modelmapper + # matches the original original + pass diff --git a/test/test_validation.py b/test/test_validation.py new file mode 100644 index 0000000..f46019e --- /dev/null +++ b/test/test_validation.py @@ -0,0 +1,16 @@ +import pytestx + + +def test_validate_bpx(): + # Check that the generated bpx files run in pybamm + pass + + +def test_validate_cidemod(): + # Check that the generated cidemod files run in cidemod + pass + + +def test_validate_battmo(): + # Check that the generated battmo files run in battmo + pass