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
18 changes: 0 additions & 18 deletions Sources/PoieticFlows/Components/Chart.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,21 +62,3 @@ public struct ChartComponent: Component {
///
public let series: [ObjectSnapshot]
}

extension ChartComponent: InspectableComponent {
public static let attributeKeys: [String] = ["name"]
public func attribute(forKey key: String) -> Variant? {
switch key {
case "name": name.map { Variant($0) }
default: nil
}
}

public static let toManyDesignReferenceKeys: [String] = ["series"]
public func designReferences(forKey key: String) -> [DesignEntityID] {
switch key {
case "series": series.map {$0.objectID}
default: []
}
}
}
6 changes: 3 additions & 3 deletions Sources/PoieticFlows/Components/SimulationPlan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ public struct SimulationPlan {
flows: [BoundFlow] = [],
// charts: [Chart] = [],
valueBindings: [CompiledControlBinding] = [],
simulationParameters: SimulationSettings? = nil) {
settings: SimulationSettings) {
self.simulationObjects = simulationObjects
self.stateVariables = stateVariables
self.builtins = builtins
self.stocks = stocks
self.flows = flows
// self.charts = charts
self.valueBindings = valueBindings
self.simulationSettings = simulationParameters
self.simulationSettings = settings
}

/// List of objects that are considered in the computation computed, ordered by computational
Expand Down Expand Up @@ -106,7 +106,7 @@ public struct SimulationPlan {
///
/// See ``SimulationSettings`` for more information.
///
public let simulationSettings: SimulationSettings?
public let simulationSettings: SimulationSettings

/// Get index into a list of computed variables for an object with given ID.
///
Expand Down
1 change: 0 additions & 1 deletion Sources/PoieticFlows/Metamodel/Traits.swift
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,6 @@ extension Trait {
optional: true,
abstract: "Solver type name"
),
// TODO: Add stop_time or final_time
// TODO: Support date/time
// TODO: Add Solver type
]
Expand Down
23 changes: 4 additions & 19 deletions Sources/PoieticFlows/Simulation/Scenario.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,16 @@ public struct SimulationSettings: Component {
let timeDelta = object["time_delta", default: 0.0]
let solverType = object["solver_type", default: "euler"]

if let endTime: Double = object["end_time"] {
if let steps: Int = object["steps"], steps >= 0 {
self.init(initialTime: initialTime,
timeDelta: timeDelta,
endTime: endTime,
steps: UInt(steps),
solverType: solverType)
}
else if let steps: Int = object["steps"], steps >= 0 {
else if let endTime: Double = object["end_time"] {
self.init(initialTime: initialTime,
timeDelta: timeDelta,
steps: UInt(steps),
endTime: endTime,
solverType: solverType)
}
else {
Expand All @@ -102,21 +102,6 @@ public struct SimulationSettings: Component {
}
}

extension SimulationSettings: InspectableComponent {
public static let attributeKeys: [String] = [
"initial_time", "time_delta", "end_time", "steps", "solver_type",
]
public func attribute(forKey key: String) -> Variant? {
switch key {
case "initial_time": Variant(self.initialTime)
case "time_delta": Variant(self.timeDelta)
case "ent_time": Variant(self.endTime)
case "steps": Variant(Int(exactly: self.steps) ?? 0)
case "solver_type": Variant(self.solverType)
default: nil
}
}
}
/// Initial values of simulation variables.
///
/// - SeeAlso: ``SimulationSettings``.
Expand Down
3 changes: 2 additions & 1 deletion Sources/PoieticFlows/Simulation/SimulationResult.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public struct SimulationResult: Component {
self.states.append(state)
}

#if false // This was used for Godot backend
/// Get numeric time series for a state variable at given index.
///
/// - Precondition: Variable at given index in all states is convertible to a float.
Expand All @@ -60,5 +61,5 @@ public struct SimulationResult: Component {

// func numericTimeSeries(index:Int, convert: (Int, Variant) -> Double) -> RegularTimeSeries
// func numericTimeSeries(index:Int, notConvertibleDefault: Double) -> RegularTimeSeries

#endif // false
}
95 changes: 53 additions & 42 deletions Sources/PoieticFlows/Systems/ComputationOrderSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,53 @@ public struct ComputationOrderSystem: System {
guard let frame = world.frame
else { return }

guard let snapshots = orderedSnapshots(world: world, frame: frame) else {
return
}

var stocks: [ObjectID] = []
var flows: [ObjectID] = []

// Determine simulation role: stock, flow, aux
// Note: See also filter in orderedSnapshots
for object in snapshots {
guard let entity = world.entity(object.objectID) else { continue /* Error? */ }
let role: SimulationObject.Role

// TODO: Should we use Trait.Stock?
if object.type === ObjectType.Stock {
role = .stock
stocks.append(object.objectID)
}
else if object.type === ObjectType.FlowRate {
role = .flow
flows.append(object.objectID)
}
else if object.type.hasTrait(Trait.Auxiliary) {
role = .auxiliary
}
else {
throw InternalSystemError(self,
message: "Unknown simulation object role for object type: \(object.type.name)",
context: .object(object.objectID))
}
let comp = SimulationRoleComponent(role: role)
entity.setComponent(comp)
}

let orderComponent = SimulationOrderComponent(
objects: snapshots,
stocks: stocks,
flows: flows
)

world.setSingleton(orderComponent)
}

func orderedSnapshots(world: World, frame: DesignFrame) -> [ObjectSnapshot]? {
// TODO: Replace with SimulationObject trait once we have it (there are practical reasons we don't yet)
// TODO: Should we use Trait.Stock? (also below)
// Note: See roles below
// TODO: Should we use Trait.Stock?
// Note: See also roles in update() method
let unordered: [ObjectSnapshot] = frame.filter {
($0.type === ObjectType.Stock
|| $0.type === ObjectType.FlowRate
Expand All @@ -46,6 +90,7 @@ public struct ComputationOrderSystem: System {
var nodes: Set<ObjectID> = Set()

for edge in cycleEdges {
guard let entity = world.entity(edge.id) else { continue }
nodes.insert(edge.origin)
nodes.insert(edge.target)
let issue = Issue(
Expand All @@ -54,57 +99,23 @@ public struct ComputationOrderSystem: System {
system: self,
error: ModelError.computationCycle,
)
world.appendIssue(issue, for: edge.id)
entity.appendIssue(issue)
}
for node in nodes {
guard let entity = world.entity(node) else { continue }
let issue = Issue(
identifier: "computation_cycle",
severity: .error,
system: self,
error: ModelError.computationCycle,
)
world.appendIssue(issue, for: node)
entity.appendIssue(issue)
}
return
return nil
}
let snapshots = ordered.compactMap { frame[$0] }

var stocks: [ObjectID] = []
var flows: [ObjectID] = []

// Determine simulation role: stock, flow, aux
// Note: See filter at the beginning of the method
for object in snapshots {
let role: SimulationObject.Role

// TODO: Should we use Trait.Stock?
if object.type === ObjectType.Stock {
role = .stock
stocks.append(object.objectID)
}
else if object.type === ObjectType.FlowRate {
role = .flow
flows.append(object.objectID)
}
else if object.type.hasTrait(Trait.Auxiliary) {
role = .auxiliary
}
else {
throw InternalSystemError(self,
message: "Unknown simulation object role for object type: \(object.type.name)",
context: .object(object.objectID))
}
let comp = SimulationRoleComponent(role: role)
world.setComponent(comp, for: object.objectID)
}

let orderComponent = SimulationOrderComponent(
objects: snapshots,
stocks: stocks,
flows: flows
)

world.setSingleton(orderComponent)
let snapshots = ordered.compactMap { frame[$0] }
return snapshots
}

}
Expand Down
33 changes: 17 additions & 16 deletions Sources/PoieticFlows/Systems/NameResolutionSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ public struct NameResolutionSystem: System {
var nameLookup: [String:ObjectID] = [:]

for object in order.objects {
guard let name = object.name else { continue }
guard let name = object.name,
let entity = world.entity(object.objectID)
else { continue }
let trimmedName = name.trimmingCharacters(in: .whitespacesAndNewlines)
if trimmedName.isEmpty {
let issue = Issue(
Expand All @@ -42,36 +44,35 @@ public struct NameResolutionSystem: System {
system: self,
error: ModelError.emptyName,
)
world.appendIssue(issue, for: object.objectID)
entity.appendIssue(issue)
continue
}
namedObjects[trimmedName, default: []].append(object.objectID)
}

// 2. Find duplicates
for (name, ids) in namedObjects where ids.count >= 1 {
guard ids.count == 1 else {
for (name, ids) in namedObjects {
if ids.count == 1 {
let onlyID = ids[0]
nameLookup[name] = onlyID
guard let entity = world.entity(onlyID) else { continue }
let comp = SimulationObjectNameComponent(name: name)
entity.setComponent(comp)
}
else if ids.count > 1 {
let issue = Issue(
identifier: "duplicate_name",
severity: .error,
system: self,
error: ModelError.duplicateName(name),
)
// TODO: Add related nodes
for id in ids {
world.appendIssue(issue, for: id)
for entity in world.query(ids) {
entity.appendIssue(issue)
}
continue
}
nameLookup[name] = ids[0]
let comp = SimulationObjectNameComponent(name: name)
world.setComponent(comp, for: ids[0])
}

let component = SimulationNameLookupComponent(
namedObjects: nameLookup
)
let component = SimulationNameLookupComponent(namedObjects: nameLookup)
world.setSingleton(component)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ public struct ParameterConnectionProposalSystem: System {
var toRemove: [ObjectID] = []
var toAdd: [ParameterProposal.EdgeProposal] = []

for (entityID, resolution) in world.query(ResolvedParametersComponent.self) {
guard let objectID = world.entityToObject(entityID)
for (entity, resolution) in world.query(ResolvedParametersComponent.self) {
guard let objectID = entity.objectID
else { continue }
if let selection, !selection.contains(objectID) { continue }

Expand Down
18 changes: 10 additions & 8 deletions Sources/PoieticFlows/Systems/ParameterResolutionSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ public struct ParameterResolutionSystem: System {
public func resolveFormulas(_ world: World, frame: DesignFrame) throws (InternalSystemError) {
let builtinNames = BuiltinVariable.allNames

for (entityID, exprComponent) in world.query(ParsedExpressionComponent.self) {
guard let objectID = world.entityToObject(entityID) else { continue }
for (entity, exprComponent) in world.query(ParsedExpressionComponent.self) {
guard let objectID = entity.objectID else { continue }
let requiredParams = exprComponent.variables.subtracting(builtinNames)
let incomingParams = frame.incoming(objectID).filter {
$0.object.type === ObjectType.Parameter
Expand Down Expand Up @@ -106,7 +106,7 @@ public struct ParameterResolutionSystem: System {
error: ModelError.unknownParameter(name),
details: ["name": Variant(name)]
)
world.appendIssue(issue, for: objectID)
entity.appendIssue(issue)
}

for edge in unused {
Expand All @@ -118,15 +118,15 @@ public struct ParameterResolutionSystem: System {
error: ModelError.unusedInput(name),
details: ["name": Variant(name)]
)
world.appendIssue(issue, for: objectID)
entity.appendIssue(issue)
}

let paramComponent = ResolvedParametersComponent(
incoming: connected,
missing: Array(missing),
unused: unused.map { $0.id }
)
world.setComponent(paramComponent, for: entityID)
entity.setComponent(paramComponent)
}
}
/// Resolve connections of single-parameter auxiliaries such as graphical function,
Expand All @@ -137,6 +137,8 @@ public struct ParameterResolutionSystem: System {
public func resolveAuxiliaries(_ world: World, frame: DesignFrame, type: ObjectType)
throws (InternalSystemError) {
for object in frame.filter(type: type) {
guard let entity = world.entity(object.objectID) else { continue }

let incomingParams = frame.incoming(object.objectID).filter {
$0.object.type === ObjectType.Parameter
}
Expand All @@ -149,7 +151,7 @@ public struct ParameterResolutionSystem: System {
system: self,
error: ModelError.missingRequiredParameter,
)
world.appendIssue(issue, for: object.objectID)
entity.appendIssue(issue)

component = ResolvedParametersComponent(
missingUnnamed: 1
Expand All @@ -162,7 +164,7 @@ public struct ParameterResolutionSystem: System {
system: self,
error: ModelError.tooManyParameters,
)
world.appendIssue(issue, for: object.objectID)
entity.appendIssue(issue)

component = ResolvedParametersComponent(
unused: incomingParams.map { $0.origin }
Expand All @@ -174,7 +176,7 @@ public struct ParameterResolutionSystem: System {
)
}

world.setComponent(component, for: object.objectID)
entity.setComponent(component)


}
Expand Down
Loading
Loading