Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions .github/workflows/Documenter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ jobs:
with:
path: OMFrontend.jl

# The docs step does `Pkg.develop(path = OMFrontend.jl)`, which makes Pkg
# inspect the package git tree. The test-only `test/3rdParty/Buildings`
# submodule is not fetched (submodules: false) and is not needed for docs,
# but its presence makes LibGit2 fail with "cannot get submodules without
# a working tree". Drop the reference so develop operates on a clean tree.
- name: Drop test-only submodule (not needed for docs)
shell: bash
run: |
rm -f OMFrontend.jl/.gitmodules
rm -rf OMFrontend.jl/test/3rdParty/Buildings

# OMFrontend.jl/Project.toml uses [sources] to point at sibling working
# copies. We mirror the CI workflow and lay the siblings out alongside the
# main checkout.
Expand Down Expand Up @@ -132,8 +143,13 @@ jobs:
"../OMRuntimeExternalC.jl",
"../SCode.jl",
]
Pkg.develop([Pkg.PackageSpec(path = p) for p in siblings])
Pkg.develop(Pkg.PackageSpec(path = pwd()))
# Develop OMFrontend together with the siblings in a single call.
# Developing the siblings first re-resolves the docs environment
# while OMFrontend is still a registry/git dependency, which makes
# Pkg clone it from GitHub and fail on its test submodule.
specs = [Pkg.PackageSpec(path = p) for p in siblings]
push!(specs, Pkg.PackageSpec(path = pwd()))
Pkg.develop(specs)
Pkg.instantiate()
'

Expand Down
9 changes: 7 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ jobs:
matrix:
# Listing every combination explicitly keeps the matrix predictable.
include:
# Coverage is only collected/uploaded on one runner.
- os: ubuntu-latest
arch: x64
version: '1.12'
coverage: true
- os: windows-latest
arch: x64
version: '1.12'
Expand Down Expand Up @@ -180,19 +182,22 @@ jobs:
# most expensive part of the build. Test is already in [deps] so
# no [extras] merging is needed.
# --code-coverage=user emits .cov files next to user source for the
# downstream julia-processcoverage step.
# downstream julia-processcoverage step. Only enabled on the coverage
# runner; the others run tests without the coverage overhead.
shell: bash
working-directory: OMFrontend.jl
run: |
julia --color=yes --code-coverage=user --project=. -e '
julia --color=yes ${{ matrix.coverage && '--code-coverage=user' || '' }} --project=. -e '
include("test/runtests.jl")
'

- uses: julia-actions/julia-processcoverage@v1
if: matrix.coverage
with:
directories: OMFrontend.jl/src

- name: Upload coverage to Codecov
if: matrix.coverage
uses: codecov/codecov-action@v4
with:
files: lcov.info
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "OMFrontend"
uuid = "b10394b5-f439-4073-a0c5-e09cb00cf46c"
authors = ["John Tinnerholm <johti17@liu.se>", "Martin Sjölund <martin.sjolund@liu.se>", "Adrian Pop <adrian.pop@liu.se>"]
version = "1.1.0"
version = "1.1.1"

[deps]
Absyn = "ce2f92e2-a952-11e9-0543-8b443f216f1d"
Expand Down
95 changes: 95 additions & 0 deletions docs/structural_mode_json.atd
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
(* ATD type definitions for flat Modelica model JSON export.
These types define the schema for both the hierarchy and flat model JSON
produced by `OMFrontend.exportJSON`. *)

(* -- Shared types -- *)

type variability = [
| Parameter
| Continuous
]

type visibility = [
| Public
| Protected
]

type variable = {
name: string;
type_name <json name="type">: string;
variability: variability;
visibility: visibility;
highest_differentiation_order: int;
?default_value <json name="default">: string option;
?attributes: string option;
}

type equation = {
id: string;
equation: string;
differentiation_order: int;
variables_used: string list;
}

(* -- Hierarchy JSON -- *)

type structural_class_summary = {
name: string;
highest_differentiation_order: int;
highest_differentiation_order_variables: string list;
}

type structural_component = {
name: string;
type_name <json name="type">: string;
n_variables: int;
n_parameters: int;
n_equations: int;
}

type hierarchy = {
model: string;
structural_classes: structural_class_summary list;
structural_components: structural_component list;
coupling_equations: string list;
~top_level_variables: string list;
}

(* -- Flat model JSON -- *)

type structural_class = {
name: string;
highest_differentiation_order: int;
highest_differentiation_order_variables: string list;
variables: variable list;
equations: equation list;
}

type parameter_override = {
parameter: string;
value: string;
}

type component = {
name: string;
class_name <json name="class">: string;
~parameter_overrides: parameter_override list;
}

type top_level_section = {
~variables: variable list;
~equations: equation list;
}

type var_equation_mapping = {
variable: string;
equation_ids: string list;
}

type flat_model = {
model: string;
structural_classes: structural_class list;
components: component list;
top_level: top_level_section;
variable_to_equations: var_equation_mapping list;
}
137 changes: 130 additions & 7 deletions src/Export/StructuralModeJSON.jl
Original file line number Diff line number Diff line change
Expand Up @@ -631,9 +631,126 @@ end

# === Public API ===

#= Canonical OCaml ATD schema for the exported JSON. Single source of truth;
docs/structural_mode_json.atd is generated from this. =#
const ATD_SCHEMA = """
(* ATD type definitions for flat Modelica model JSON export.
These types define the schema for both the hierarchy and flat model JSON
produced by `OMFrontend.exportJSON`. *)

(* -- Shared types -- *)

type variability = [
| Parameter
| Continuous
]

type visibility = [
| Public
| Protected
]

type variable = {
name: string;
type_name <json name="type">: string;
variability: variability;
visibility: visibility;
highest_differentiation_order: int;
?default_value <json name="default">: string option;
?attributes: string option;
}

type equation = {
id: string;
equation: string;
differentiation_order: int;
variables_used: string list;
}

(* -- Hierarchy JSON -- *)

type structural_class_summary = {
name: string;
highest_differentiation_order: int;
highest_differentiation_order_variables: string list;
}

type structural_component = {
name: string;
type_name <json name="type">: string;
n_variables: int;
n_parameters: int;
n_equations: int;
}

type hierarchy = {
model: string;
structural_classes: structural_class_summary list;
structural_components: structural_component list;
coupling_equations: string list;
~top_level_variables: string list;
}

(* -- Flat model JSON -- *)

type structural_class = {
name: string;
highest_differentiation_order: int;
highest_differentiation_order_variables: string list;
variables: variable list;
equations: equation list;
}

type parameter_override = {
parameter: string;
value: string;
}

type component = {
name: string;
class_name <json name="class">: string;
~parameter_overrides: parameter_override list;
}

type top_level_section = {
~variables: variable list;
~equations: equation list;
}

type var_equation_mapping = {
variable: string;
equation_ids: string list;
}

type flat_model = {
model: string;
structural_classes: structural_class list;
components: component list;
top_level: top_level_section;
variable_to_equations: var_equation_mapping list;
}
"""
exportFlatModelJSON(FM; output_dir=".", base_name, hierarchy=true, flat=true,
class_mapping=nothing) -> NamedTuple

"""
exportATD(; output_dir=".", base_name="structural_mode_json") -> String

Write the canonical OCaml ATD schema (`ATD_SCHEMA`) for the exported JSON to
`<output_dir>/<base_name>.atd` and return the written path. The schema is
model-independent.
"""
function exportATD(; output_dir::AbstractString = ".",
base_name::AbstractString = "structural_mode_json")
mkpath(output_dir)
path = joinpath(output_dir, base_name * ".atd")
open(path, "w") do io
write(io, ATD_SCHEMA)
end
return path
end

"""
exportJSON(FM; output_dir=".", base_name, hierarchy=true, flat=true,
class_mapping=nothing, atd=false) -> NamedTuple

Walk a FlatModel and write `<base_name>_hierarchy.json` and/or `<base_name>_flat.json`.
Structural-mode components (in `FM.structuralSubmodels`) are deduplicated into
Expand All @@ -643,22 +760,25 @@ class templates with per-instance parameter overrides.
to class name (e.g. `Dict("p1" => "Pendulum")`). When not provided, the class
name defaults to the uppercase-first form of the first instance in each fingerprint group.

Returns a `NamedTuple{(:hierarchy_path, :flat_path)}`. Either entry is the
written file path, or `nothing` when the corresponding keyword was `false`.
Returns a `NamedTuple{(:hierarchy_path, :flat_path, :atd_path)}`. Each entry is the
written file path, or `nothing` when the corresponding keyword was `false`. Pass
`atd=true` to also write `<base_name>.atd` (the schema in `ATD_SCHEMA`).
"""
function exportFlatModelJSON(fmOrTuple;
function exportJSON(fmOrTuple;
output_dir::AbstractString = ".",
base_name::AbstractString = "",
hierarchy::Bool = true,
flat::Bool = true,
class_mapping::Union{Dict{String,String}, Nothing} = nothing)
class_mapping::Union{Dict{String,String}, Nothing} = nothing,
atd::Bool = false)
fm = fmOrTuple isa Frontend.FLAT_MODEL ? fmOrTuple : first(fmOrTuple)
isempty(base_name) && (base_name = fm.name)
mkpath(output_dir)
submodels = _collectStructuralSubmodels(fm)
mapping = _buildClassMapping(submodels, class_mapping)
hierPath = nothing
flatPath = nothing
atdPath = nothing
if hierarchy
h = _buildHierarchy(fm, submodels, mapping)
hierPath = joinpath(output_dir, base_name * "_hierarchy.json")
Expand All @@ -673,7 +793,10 @@ function exportFlatModelJSON(fmOrTuple;
JSON.print(io, _toDict(f), 2)
end
end
return (hierarchy_path = hierPath, flat_path = flatPath)
if atd
atdPath = exportATD(output_dir = output_dir, base_name = base_name)
end
return (hierarchy_path = hierPath, flat_path = flatPath, atd_path = atdPath)
end

end # module StructuralModeJSON
Loading
Loading