From 3f09d2046599317191fd0a4fb8c2f1739c32f8cb Mon Sep 17 00:00:00 2001 From: Maxim <66460674+mkolpakov2002@users.noreply.github.com> Date: Thu, 10 Oct 2024 19:44:29 +0300 Subject: [PATCH] Add simple expressions and equations --- controls-constructor/build.gradle.kts | 3 + .../constructor/dsl/core/Annotation.kt | 9 + .../constructor/dsl/core/EquationSystem.kt | 196 ++++++++++++++++++ .../controls/constructor/dsl/core/Record.kt | 19 ++ .../dsl/core/algorithms/AlgorithmBuilder.kt | 37 ++++ .../dsl/core/algorithms/AlgorithmSection.kt | 80 +++++++ .../dsl/core/components/Component.kt | 81 ++++++++ .../dsl/core/components/Connection.kt | 38 ++++ .../dsl/core/components/Connector.kt | 22 ++ .../constructor/dsl/core/components/Model.kt | 163 +++++++++++++++ .../dsl/core/components/Package.kt | 21 ++ .../dsl/core/equations/ConditionalEquation.kt | 12 ++ .../dsl/core/equations/Equation.kt | 24 +++ .../dsl/core/equations/EquationBase.kt | 6 + .../dsl/core/equations/InitialEquation.kt | 11 + .../dsl/core/events/AssignAction.kt | 18 ++ .../constructor/dsl/core/events/Event.kt | 30 +++ .../dsl/core/events/EventAction.kt | 12 ++ .../dsl/core/events/ReinitAction.kt | 18 ++ .../core/expression/ExpressionOperators.kt | 91 ++++++++ .../core/expressions/ArrayBinaryExpression.kt | 37 ++++ .../core/expressions/ArrayBinaryOperator.kt | 11 + .../dsl/core/expressions/ArrayExpression.kt | 3 + .../core/expressions/ArrayScalarExpression.kt | 34 +++ .../core/expressions/ArrayScalarOperator.kt | 9 + .../expressions/ArrayVariableExpression.kt | 42 ++++ .../dsl/core/expressions/BinaryExpression.kt | 135 ++++++++++++ .../core/expressions/ConstantExpression.kt | 20 ++ .../core/expressions/DerivativeExpression.kt | 30 +++ .../dsl/core/expressions/Expression.kt | 34 +++ .../expressions/FunctionCallExpression.kt | 91 ++++++++ .../PartialDerivativeExpression.kt | 31 +++ .../core/expressions/VariableExpression.kt | 32 +++ .../dsl/core/functions/Function.kt | 14 ++ .../dsl/core/functions/FunctionBuilder.kt | 50 +++++ .../dsl/core/simulation/NumericSolver.kt | 37 ++++ .../dsl/core/simulation/SimulationContext.kt | 93 +++++++++ .../constructor/dsl/core/units/Dimension.kt | 78 +++++++ .../dsl/core/units/PhysicalUnit.kt | 51 +++++ .../constructor/dsl/core/units/Units.kt | 89 ++++++++ .../dsl/core/variables/AbstractVariable.kt | 13 ++ .../dsl/core/variables/ArrayVariable.kt | 28 +++ .../dsl/core/variables/Causality.kt | 10 + .../dsl/core/variables/ConstantVariable.kt | 12 ++ .../dsl/core/variables/ContinuousVariable.kt | 22 ++ .../dsl/core/variables/DiscreteVariable.kt | 13 ++ .../dsl/core/variables/ParameterVariable.kt | 25 +++ .../dsl/core/variables/Variability.kt | 11 + .../dsl/core/variables/Variable.kt | 28 +++ .../controls/constructor/EquationsTest.kt | 89 ++++++++ .../constructor/EventsAndActionsTest.kt | 73 +++++++ .../controls/constructor/ExpressionsTest.kt | 101 +++++++++ .../controls/constructor/FunctionsTest.kt | 29 +++ .../controls/constructor/UnitsTest.kt | 85 ++++++++ .../controls/constructor/VariablesTest.kt | 41 ++++ 55 files changed, 2392 insertions(+) create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/Annotation.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/EquationSystem.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/Record.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/algorithms/AlgorithmBuilder.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/algorithms/AlgorithmSection.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Component.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Connection.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Connector.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Model.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Package.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/ConditionalEquation.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/Equation.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/EquationBase.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/InitialEquation.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/AssignAction.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/Event.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/EventAction.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/ReinitAction.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expression/ExpressionOperators.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayBinaryExpression.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayBinaryOperator.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayExpression.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayScalarExpression.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayScalarOperator.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayVariableExpression.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/BinaryExpression.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ConstantExpression.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/DerivativeExpression.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/Expression.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/FunctionCallExpression.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/PartialDerivativeExpression.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/VariableExpression.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/functions/Function.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/functions/FunctionBuilder.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/simulation/NumericSolver.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/simulation/SimulationContext.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/units/Dimension.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/units/PhysicalUnit.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/units/Units.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/AbstractVariable.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ArrayVariable.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/Causality.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ConstantVariable.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ContinuousVariable.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/DiscreteVariable.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ParameterVariable.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/Variability.kt create mode 100644 controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/Variable.kt create mode 100644 controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/EquationsTest.kt create mode 100644 controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/EventsAndActionsTest.kt create mode 100644 controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/ExpressionsTest.kt create mode 100644 controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/FunctionsTest.kt create mode 100644 controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/UnitsTest.kt create mode 100644 controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/VariablesTest.kt diff --git a/controls-constructor/build.gradle.kts b/controls-constructor/build.gradle.kts index 1947f91..6c42fc3 100644 --- a/controls-constructor/build.gradle.kts +++ b/controls-constructor/build.gradle.kts @@ -16,10 +16,13 @@ kscience{ useSerialization() commonMain { api(projects.controlsCore) + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1") + implementation("org.jetbrains.kotlinx:multik-core:0.2.3") } commonTest{ implementation(spclibs.logback.classic) + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1") } } diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/Annotation.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/Annotation.kt new file mode 100644 index 0000000..b72d2f9 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/Annotation.kt @@ -0,0 +1,9 @@ +package space.kscience.controls.constructor.dsl.core + +/** + * Класс для представления аннотации. + */ +public data class Annotation( + val name: String, + val properties: Map +) diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/EquationSystem.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/EquationSystem.kt new file mode 100644 index 0000000..c4615ba --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/EquationSystem.kt @@ -0,0 +1,196 @@ +package space.kscience.controls.constructor.dsl.core + +import space.kscience.controls.constructor.dsl.core.equations.* +import space.kscience.controls.constructor.dsl.core.expressions.* +import space.kscience.controls.constructor.dsl.core.variables.ArrayVariable +import space.kscience.controls.constructor.dsl.core.variables.Variable + +/** + * Класс для хранения и обработки системы уравнений. + */ +public class EquationSystem { + public val equations: MutableList = mutableListOf() + public val variables: MutableSet = mutableSetOf() + public val derivativeVariables: MutableSet = mutableSetOf() + public val algebraicEquations: MutableList = mutableListOf() + public val initialEquations: MutableList = mutableListOf() + public val conditionalEquations: MutableList = mutableListOf() + + /** + * Метод для добавления списка уравнений. + */ + public fun addEquations(eqs: List) { + for (eq in eqs) { + addEquation(eq) + } + } + + /** + * Метод для добавления одного уравнения. + */ + public fun addEquation(eq: EquationBase) { + equations.add(eq) + collectVariables(eq) + } + + private fun collectVariables(eq: EquationBase) { + when (eq) { + is Equation -> { + collectVariablesFromExpression(eq.leftExpression) + collectVariablesFromExpression(eq.rightExpression) + + // Classify the equation + if (isDifferentialEquation(eq)) { + // Handled in toODESystem() + } else { + algebraicEquations.add(eq) + } + } + is ConditionalEquation -> { + collectVariablesFromExpression(eq.condition) + collectVariables(eq.trueEquation) + eq.falseEquation?.let { collectVariables(it) } + conditionalEquations.add(eq) + } + is InitialEquation -> { + collectVariablesFromExpression(eq.leftExpression) + collectVariablesFromExpression(eq.rightExpression) + initialEquations.add(eq) + } + else -> { + throw UnsupportedOperationException("Unknown equation type: ${eq::class}") + } + } + } + + private fun collectVariablesFromExpression(expr: Expression) { + when (expr) { + is VariableExpression -> variables.add(expr.variable.name) + is ConstantExpression -> { + // Constants do not contain variables + } + is DerivativeExpression -> { + collectVariablesFromExpression(expr.variable) + derivativeVariables.add(expr.variable.variable.name) + } + is PartialDerivativeExpression -> { + collectVariablesFromExpression(expr.variable) + collectVariablesFromExpression(expr.respectTo) + // For partial derivatives, variables may need special handling + } + is BinaryExpression -> { + collectVariablesFromExpression(expr.left) + collectVariablesFromExpression(expr.right) + } + is FunctionCallExpression -> { + expr.arguments.forEach { collectVariablesFromExpression(it) } + } + is ArrayVariableExpression -> { + collectVariablesFromArrayVariable(expr.left) + collectVariablesFromArrayVariable(expr.right) + } + is ArrayScalarExpression -> { + collectVariablesFromArrayVariable(expr.array) + collectVariablesFromExpression(expr.scalar) + } + else -> { + throw UnsupportedOperationException("Unknown expression type: ${expr::class}") + } + } + } + + private fun collectVariablesFromArrayVariable(arrayVar: ArrayVariable) { + variables.add(arrayVar.name) + // Additional processing if necessary + } + + private fun isDifferentialEquation(eq: Equation): Boolean { + val leftIsDerivative = containsDerivative(eq.leftExpression) + val rightIsDerivative = containsDerivative(eq.rightExpression) + return leftIsDerivative || rightIsDerivative + } + + private fun containsDerivative(expr: Expression): Boolean { + return when (expr) { + is DerivativeExpression -> true + is BinaryExpression -> containsDerivative(expr.left) || containsDerivative(expr.right) + is FunctionCallExpression -> expr.arguments.any { containsDerivative(it) } + else -> false + } + } + + /** + * Подготовка системы ОДУ для численного решения. + */ + public fun toODESystem(): ODESystem { + val odeEquations = mutableListOf() + + // Process equations to separate ODEs and algebraic equations + for (eqBase in equations) { + when (eqBase) { + is Equation -> { + val eq = eqBase + if (isDifferentialEquation(eq)) { + // Handle differential equations + processDifferentialEquation(eq, odeEquations) + } else { + // Handle algebraic equations + algebraicEquations.add(eq) + } + } + is ConditionalEquation -> { + // Handle conditional equations + // For ODE system, we might need to process them differently + conditionalEquations.add(eqBase) + } + is InitialEquation -> { + // Initial equations are used for setting initial conditions + initialEquations.add(eqBase) + } + else -> { + throw UnsupportedOperationException("Unknown equation type: ${eqBase::class}") + } + } + } + + return ODESystem(odeEquations, algebraicEquations, initialEquations, conditionalEquations) + } + + private fun processDifferentialEquation(eq: Equation, odeEquations: MutableList) { + // Identify which side has the derivative + val derivativeExpr = findDerivativeExpression(eq.leftExpression) ?: findDerivativeExpression(eq.rightExpression) + if (derivativeExpr != null) { + val variableName = derivativeExpr.variable.variable.name + if (derivativeExpr.order != 1) { + throw UnsupportedOperationException("Only first-order derivatives are supported in this solver.") + } + + val rhs = if (eq.leftExpression is DerivativeExpression) eq.rightExpression else eq.leftExpression + odeEquations.add(ODEEquation(variableName, rhs)) + derivativeVariables.add(variableName) + } else { + throw UnsupportedOperationException("Equation is marked as differential but no derivative found.") + } + } + + private fun findDerivativeExpression(expr: Expression): DerivativeExpression? { + return when (expr) { + is DerivativeExpression -> expr + is BinaryExpression -> findDerivativeExpression(expr.left) ?: findDerivativeExpression(expr.right) + is FunctionCallExpression -> expr.arguments.mapNotNull { findDerivativeExpression(it) }.firstOrNull() + else -> null + } + } +} + +public data class ODEEquation( + val variableName: String, + val rhs: Expression +) + +public class ODESystem( + public val odeEquations: List, + public val algebraicEquations: List, + public val initialEquations: List, + public val conditionalEquations: List +) diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/Record.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/Record.kt new file mode 100644 index 0000000..b722920 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/Record.kt @@ -0,0 +1,19 @@ +package space.kscience.controls.constructor.dsl.core + +import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit +import space.kscience.controls.constructor.dsl.core.variables.Variable + +/** + * Класс для представления записи (record). + */ +public class Record(public val name: String) { + public val fields: MutableMap = mutableMapOf() + + + public fun field(name: String, unit: PhysicalUnit) { + if (fields.containsKey(name)) { + throw IllegalArgumentException("Field with name $name already exists in record $name.") + } + fields[name] = Variable(name, unit) + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/algorithms/AlgorithmBuilder.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/algorithms/AlgorithmBuilder.kt new file mode 100644 index 0000000..fc1691f --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/algorithms/AlgorithmBuilder.kt @@ -0,0 +1,37 @@ +package space.kscience.controls.constructor.dsl.core.algorithms + +import space.kscience.controls.constructor.dsl.core.expressions.Expression +import space.kscience.controls.constructor.dsl.core.variables.Variable + +/** + * Класс для построения алгоритмов. + */ +public class AlgorithmBuilder { + public val statements: MutableList = mutableListOf() + + public fun assign(variable: Variable, expression: Expression) { + statements.add(AssignmentStatement(variable, expression)) + } + + public fun ifStatement(condition: Expression, init: AlgorithmBuilder.() -> Unit) { + val trueBuilder = AlgorithmBuilder() + trueBuilder.init() + statements.add(IfStatement(condition, trueBuilder.build(), null)) + } + + public fun forStatement(variable: Variable, range: IntRange, init: AlgorithmBuilder.() -> Unit) { + val bodyBuilder = AlgorithmBuilder() + bodyBuilder.init() + statements.add(ForStatement(variable, range, bodyBuilder.build())) + } + + public fun whileStatement(condition: Expression, init: AlgorithmBuilder.() -> Unit) { + val bodyBuilder = AlgorithmBuilder() + bodyBuilder.init() + statements.add(WhileStatement(condition, bodyBuilder.build())) + } + + public fun build(): Algorithm { + return Algorithm(statements.toList()) + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/algorithms/AlgorithmSection.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/algorithms/AlgorithmSection.kt new file mode 100644 index 0000000..d006c84 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/algorithms/AlgorithmSection.kt @@ -0,0 +1,80 @@ +package space.kscience.controls.constructor.dsl.core.algorithms + +import space.kscience.controls.constructor.dsl.core.expressions.Expression +import space.kscience.controls.constructor.dsl.core.simulation.SimulationContext +import space.kscience.controls.constructor.dsl.core.variables.Variable + + +/** + * Абстрактный класс для операторов в алгоритмической секции. + */ +public sealed class Statement { + public abstract suspend fun execute(context: SimulationContext) +} + +public data class AssignmentStatement( + val variable: Variable, + val expression: Expression +) : Statement() { + override suspend fun execute(context: SimulationContext) { + val value = expression.evaluate(context.getVariableValues()) + context.updateVariable(variable.name, value) + } +} + +public data class IfStatement( + val condition: Expression, + val trueBranch: Algorithm, + val falseBranch: Algorithm? = null +) : Statement() { + override suspend fun execute(context: SimulationContext) { + val conditionValue = condition.evaluate(context.getVariableValues()) + if (conditionValue != 0.0) { + trueBranch.execute(context) + } else { + falseBranch?.execute(context) + } + } +} + +public data class ForStatement( + val variable: Variable, + val range: IntRange, + val body: Algorithm +) : Statement() { + override suspend fun execute(context: SimulationContext) { + for (i in range) { + context.updateVariable(variable.name, i.toDouble()) + body.execute(context) + } + } +} + +public data class WhileStatement( + val condition: Expression, + val body: Algorithm +) : Statement() { + override suspend fun execute(context: SimulationContext) { + while (condition.evaluate(context.getVariableValues()) != 0.0) { + body.execute(context) + } + } +} + +public class Algorithm(private val statements: List) { + /** + * Добавляет оператор в алгоритм. + */ + public fun addStatement(statement: Statement) { + (statements as MutableList).add(statement) + } + + /** + * Выполняет алгоритм в заданном контексте симуляции. + */ + public suspend fun execute(context: SimulationContext) { + for (statement in statements) { + statement.execute(context) + } + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Component.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Component.kt new file mode 100644 index 0000000..828948b --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Component.kt @@ -0,0 +1,81 @@ +package space.kscience.controls.constructor.dsl.core.components + +import space.kscience.controls.constructor.dsl.core.Annotation +import space.kscience.controls.constructor.dsl.core.equations.Equation +import space.kscience.controls.constructor.dsl.core.variables.ParameterVariable +import space.kscience.controls.constructor.dsl.core.variables.Variable + +/** + * Базовый класс для всех компонентов модели. + */ +public abstract class Component(public val name: String) { + public val variables: MutableMap = mutableMapOf() + public val parameters: MutableMap = mutableMapOf() + public val equations: MutableList = mutableListOf() + public val connectors: MutableMap = mutableMapOf() + public val annotations: MutableList = mutableListOf() + public var isReplaceable: Boolean = false + + /** + * Метод для инициализации переменных. + */ + protected abstract fun initializeVariables() + + /** + * Метод для определения уравнений компонента. + */ + protected abstract fun defineEquations() + + /** + * Метод для добавления переменной. + */ + public fun addVariable(variable: V) { + if (variables.containsKey(variable.name)) { + throw IllegalArgumentException("Variable with name ${variable.name} already exists in component $name.") + } + variables[variable.name] = variable + } + + /** + * Метод для добавления параметра. + */ + public fun addParameter(parameter: P) { + if (parameters.containsKey(parameter.name)) { + throw IllegalArgumentException("Parameter with name ${parameter.name} already exists in component $name.") + } + parameters[parameter.name] = parameter + } + + /** + * Метод для добавления уравнения. + */ + public fun addEquation(equation: Equation) { + equations.add(equation) + } + + /** + * Метод для добавления коннектора. + */ + public fun addConnector(name: String, connector: C) { + if (connectors.containsKey(name)) { + throw IllegalArgumentException("Connector with name $name already exists in component $name.") + } + connectors[name] = connector + } + + public fun annotate(annotation: Annotation) { + annotations.add(annotation) + } + + /** + * Метод для модификации параметров компонента. + */ + public fun modify(modifier: Component.() -> Unit): Component { + this.apply(modifier) + return this + } + + public fun markAsReplaceable(isReplaceable: Boolean) { + this.isReplaceable = isReplaceable + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Connection.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Connection.kt new file mode 100644 index 0000000..a5a17e6 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Connection.kt @@ -0,0 +1,38 @@ +package space.kscience.controls.constructor.dsl.core.components + +import space.kscience.controls.constructor.dsl.core.equations.Equation +import space.kscience.controls.constructor.dsl.core.expressions.VariableExpression +import space.kscience.controls.constructor.dsl.core.variables.Variable + +/** + * Класс для представления соединения между двумя коннекторами. + */ +public class Connection( + public val connector1: C, + public val connector2: C +) { + init { + if (connector1.unit != connector2.unit) { + throw IllegalArgumentException("Cannot connect connectors with different units: ${connector1.unit} and ${connector2.unit}.") + } + } + + /** + * Генерация уравнений для соединения. + */ + public fun generateEquations(): List { + val equations = mutableListOf() + for ((varName, var1) in connector1.variables) { + val var2 = connector2.variables[varName] + ?: throw IllegalArgumentException("Variable $varName not found in connector ${connector2.name}.") + + equations.add( + Equation( + VariableExpression(var1), + VariableExpression(var2) + ) + ) + } + return equations + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Connector.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Connector.kt new file mode 100644 index 0000000..8f2d9e1 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Connector.kt @@ -0,0 +1,22 @@ +package space.kscience.controls.constructor.dsl.core.components + +import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit +import space.kscience.controls.constructor.dsl.core.variables.Variable + +/** + * Интерфейс для коннектора, позволяющего соединять компоненты. + */ +public interface Connector { + public val name: String + public val variables: Map + public val unit: PhysicalUnit +} + +/** + * Базовая реализация коннектора. + */ +public class BasicConnector( + override val name: String, + override val unit: PhysicalUnit, + override val variables: Map +) : Connector diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Model.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Model.kt new file mode 100644 index 0000000..c6b4ac0 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Model.kt @@ -0,0 +1,163 @@ +package space.kscience.controls.constructor.dsl.core.components + +import space.kscience.controls.constructor.dsl.core.* +import space.kscience.controls.constructor.dsl.core.algorithms.* +import space.kscience.controls.constructor.dsl.core.equations.Equation +import space.kscience.controls.constructor.dsl.core.equations.ConditionalEquation +import space.kscience.controls.constructor.dsl.core.expressions.Expression +import space.kscience.controls.constructor.dsl.core.expressions.VariableExpression +import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit +import space.kscience.controls.constructor.dsl.core.variables.ParameterVariable +import space.kscience.controls.constructor.dsl.core.variables.Variable +import space.kscience.controls.constructor.dsl.core.functions.Function +import space.kscience.controls.constructor.dsl.core.functions.FunctionBuilder + +/** + * Класс для представления модели, содержащей компоненты и соединения. + */ +public class Model(public val name: String, init: Model.() -> Unit) { + public val components: MutableMap> = mutableMapOf>() + public val connections: MutableList> = mutableListOf>() + public val variables: MutableMap = mutableMapOf() + public val parameters: MutableMap = mutableMapOf() + public val equations: MutableList = mutableListOf() + public val initialValues: MutableMap = mutableMapOf() + public val initialEquations: MutableList = mutableListOf() + public val conditionalEquations: MutableList = mutableListOf() + public val algorithms: MutableList = mutableListOf() + public val functions: MutableMap = mutableMapOf() + + init { + this.init() + } + + /** + * Определение переменной. + */ + public fun variable(name: String, unit: PhysicalUnit): VariableExpression { + val variable = Variable(name, unit) + variables[name] = variable + return VariableExpression(variable) + } + + /** + * Определение параметра. + */ + public fun parameter(name: String, unit: PhysicalUnit, value: Double): ParameterVariable { + val parameter = ParameterVariable(name, unit, value) + parameters[name] = parameter + return parameter + } + + /** + * Добавление уравнения. + */ + public fun equation(equationBuilder: () -> Equation) { + val equation = equationBuilder() + equations.add(equation) + } + + /** + * Установка начальных значений. + */ + public fun initialValues(init: MutableMap.() -> Unit) { + initialValues.apply(init) + } + + /** + * Добавление инициализационного уравнения. + */ + public fun initialEquation(equationBuilder: () -> Equation) { + val equation = equationBuilder() + initialEquations.add(equation) + } + + /** + * Метод для добавления компонента. + */ + public fun addComponent(component: Component<*, *, *>) { + if (components.containsKey(component.name)) { + throw IllegalArgumentException("Component with name ${component.name} already exists in model $name.") + } + components[component.name] = component + } + + /** + * Метод для добавления соединения. + */ + public fun addConnection(connection: Connection<*>) { + connections.add(connection) + } + + /** + * Метод для генерации системы уравнений модели. + */ + public fun generateEquationSystem(): EquationSystem { + val equationSystem = EquationSystem() + + // Добавление уравнений модели + equationSystem.addEquations(equations) + + // Добавление уравнений компонентов + for (component in components.values) { + equationSystem.addEquations(component.equations) + } + + // Добавление уравнений соединений + for (connection in connections) { + equationSystem.addEquations(connection.generateEquations()) + } + + // Добавление условных уравнений + for (condEq in conditionalEquations) { + equationSystem.addEquation(condEq) + } + + // Добавление инициализационных уравнений + equationSystem.addEquations(initialEquations) + + return equationSystem + } + + /** + * Метод для замены компонента. + */ + public fun replaceComponent(name: String, newComponent: Component<*, *, *>) { + val oldComponent = components[name] + if (oldComponent != null && oldComponent.isReplaceable) { + components[name] = newComponent + } else { + throw IllegalArgumentException("Component $name is not replaceable or does not exist.") + } + } + + /** + * Добавление условного уравнения. + */ + public fun conditionalEquation(condition: Expression, trueEquation: Equation, falseEquation: Equation? = null) { + val condEq = ConditionalEquation(condition, trueEquation, falseEquation) + conditionalEquations.add(condEq) + } + + /** + * Добавление алгоритма. + */ + public fun algorithm(init: AlgorithmBuilder.() -> Unit) { + val builder = AlgorithmBuilder() + builder.init() + algorithms.add(builder.build()) + } + + /** + * Добавление пользовательской функции. + */ + public fun function(name: String, init: FunctionBuilder.() -> Unit) { + if (functions.containsKey(name)) { + throw IllegalArgumentException("Function with name $name already exists in model $name.") + } + val builder = FunctionBuilder(name) + builder.init() + val function = builder.build() + functions[name] = function + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Package.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Package.kt new file mode 100644 index 0000000..3667c84 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/components/Package.kt @@ -0,0 +1,21 @@ +package space.kscience.controls.constructor.dsl.core.components + +/** + * Класс для представления пакета моделей. + */ +public class Package(public val name: String) { + public val models: MutableMap = mutableMapOf() + public val subPackages: MutableMap = mutableMapOf() + + public fun addModel(model: Model) { + models[model.name] = model + } + + public fun addPackage(pkg: Package) { + subPackages[pkg.name] = pkg + } + + public fun getModel(name: String): Model? { + return models[name] ?: subPackages.values.mapNotNull { it.getModel(name) }.firstOrNull() + } +} \ No newline at end of file diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/ConditionalEquation.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/ConditionalEquation.kt new file mode 100644 index 0000000..1b03000 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/ConditionalEquation.kt @@ -0,0 +1,12 @@ +package space.kscience.controls.constructor.dsl.core.equations + +import space.kscience.controls.constructor.dsl.core.expressions.Expression + +/** + * Класс для условного уравнения. + */ +public class ConditionalEquation( + public val condition: Expression, + public val trueEquation: Equation, + public val falseEquation: Equation? = null +) : EquationBase() diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/Equation.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/Equation.kt new file mode 100644 index 0000000..fa19024 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/Equation.kt @@ -0,0 +1,24 @@ +package space.kscience.controls.constructor.dsl.core.equations + +import space.kscience.controls.constructor.dsl.core.expressions.Expression + +/** + * Класс для представления уравнения. + */ +public class Equation( + public val leftExpression: Expression, + public val rightExpression: Expression +) : EquationBase() { + init { + // Проверка совместимости единиц измерения + if (!leftExpression.unit.dimension.isCompatibleWith(rightExpression.unit.dimension)) { + throw IllegalArgumentException("Units of left and right expressions in the equation are not compatible.") + } + } + + public fun evaluate(values: Map): Double { + val leftValue = leftExpression.evaluate(values) + val rightValue = rightExpression.evaluate(values) + return leftValue - rightValue + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/EquationBase.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/EquationBase.kt new file mode 100644 index 0000000..d8b94b9 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/EquationBase.kt @@ -0,0 +1,6 @@ +package space.kscience.controls.constructor.dsl.core.equations + +/** + * Абстрактный базовый класс для уравнений. + */ +public sealed class EquationBase diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/InitialEquation.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/InitialEquation.kt new file mode 100644 index 0000000..2415c3d --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/equations/InitialEquation.kt @@ -0,0 +1,11 @@ +package space.kscience.controls.constructor.dsl.core.equations + +import space.kscience.controls.constructor.dsl.core.expressions.Expression + +/** + * Class representing an initial equation for setting initial conditions. + */ +public class InitialEquation( + public val leftExpression: Expression, + public val rightExpression: Expression +) : EquationBase() diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/AssignAction.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/AssignAction.kt new file mode 100644 index 0000000..60c40eb --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/AssignAction.kt @@ -0,0 +1,18 @@ +package space.kscience.controls.constructor.dsl.core.events + +import space.kscience.controls.constructor.dsl.core.expressions.Expression +import space.kscience.controls.constructor.dsl.core.simulation.SimulationContext +import space.kscience.controls.constructor.dsl.core.variables.Variable + +/** + * Действие присваивания нового значения переменной. + */ +public data class AssignAction( + val variable: Variable, + val newValue: Expression +) : EventAction() { + override suspend fun execute(context: SimulationContext) { + val value = newValue.evaluate(context.getVariableValues()) + context.updateVariable(variable.name, value) + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/Event.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/Event.kt new file mode 100644 index 0000000..6a66950 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/Event.kt @@ -0,0 +1,30 @@ +package space.kscience.controls.constructor.dsl.core.events + +import space.kscience.controls.constructor.dsl.core.simulation.SimulationContext +import space.kscience.controls.constructor.dsl.core.expressions.Expression + +/** + * Класс для представления события. + */ +public class Event( + public val condition: Expression, + public val actions: List +) { + /** + * Метод для проверки, произошло ли событие. + */ + public suspend fun checkEvent(context: SimulationContext): Boolean { + val values = context.getVariableValues() + val conditionValue = condition.evaluate(values) + return conditionValue > 0 // Событие происходит, когда условие положительно + } + + /** + * Метод для выполнения действий при событии. + */ + public suspend fun executeActions(context: SimulationContext) { + for (action in actions) { + action.execute(context) + } + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/EventAction.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/EventAction.kt new file mode 100644 index 0000000..d4bd879 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/EventAction.kt @@ -0,0 +1,12 @@ +package space.kscience.controls.constructor.dsl.core.events + +import space.kscience.controls.constructor.dsl.core.expressions.Expression +import space.kscience.controls.constructor.dsl.core.simulation.SimulationContext +import space.kscience.controls.constructor.dsl.core.variables.Variable + +/** + * Абстрактный класс для действий при событии. + */ +public sealed class EventAction { + public abstract suspend fun execute(context: SimulationContext) +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/ReinitAction.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/ReinitAction.kt new file mode 100644 index 0000000..ccc70e9 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/events/ReinitAction.kt @@ -0,0 +1,18 @@ +package space.kscience.controls.constructor.dsl.core.events + +import space.kscience.controls.constructor.dsl.core.expressions.Expression +import space.kscience.controls.constructor.dsl.core.simulation.SimulationContext +import space.kscience.controls.constructor.dsl.core.variables.Variable + +/** + * Действие реинициализации переменной. + */ +public data class ReinitAction( + val variable: Variable, + val newValue: Expression +) : EventAction() { + override suspend fun execute(context: SimulationContext) { + val value = newValue.evaluate(context.getVariableValues()) + context.updateVariable(variable.name, value) + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expression/ExpressionOperators.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expression/ExpressionOperators.kt new file mode 100644 index 0000000..8354f41 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expression/ExpressionOperators.kt @@ -0,0 +1,91 @@ +package space.kscience.controls.constructor.dsl.core.expression + +import space.kscience.controls.constructor.dsl.core.variables.ArrayVariable +import space.kscience.controls.constructor.dsl.core.equations.Equation +import space.kscience.controls.constructor.dsl.core.expressions.ArrayBinaryExpression +import space.kscience.controls.constructor.dsl.core.expressions.ArrayBinaryOperator +import space.kscience.controls.constructor.dsl.core.expressions.ArrayScalarExpression +import space.kscience.controls.constructor.dsl.core.expressions.ArrayScalarOperator +import space.kscience.controls.constructor.dsl.core.expressions.BinaryExpression +import space.kscience.controls.constructor.dsl.core.expressions.BinaryOperator +import space.kscience.controls.constructor.dsl.core.expressions.ConstantExpression +import space.kscience.controls.constructor.dsl.core.expressions.Expression +import space.kscience.controls.constructor.dsl.core.expressions.FunctionCallExpression +import space.kscience.controls.constructor.dsl.core.units.Units +import space.kscience.controls.constructor.dsl.core.functions.Function + +// Операторные функции для скалярных выражений + +public operator fun Expression.plus(other: Expression): Expression { + return BinaryExpression(this, BinaryOperator.ADD, other) +} + +public operator fun Expression.minus(other: Expression): Expression { + return BinaryExpression(this, BinaryOperator.SUBTRACT, other) +} + +public operator fun Expression.times(other: Expression): Expression { + return BinaryExpression(this, BinaryOperator.MULTIPLY, other) +} + +public operator fun Expression.div(other: Expression): Expression { + return BinaryExpression(this, BinaryOperator.DIVIDE, other) +} + +public infix fun Expression.pow(exponent: Expression): Expression { + return FunctionCallExpression("pow", listOf(this, exponent)) +} + +public infix fun Expression.pow(exponent: Double): Expression { + return FunctionCallExpression("pow", listOf(this, ConstantExpression(exponent, Units.dimensionless))) +} + +// Сравнительные операторы + +public infix fun Expression.eq(other: Expression): Equation { + return Equation(this, other) +} + +public infix fun Expression.gt(other: Expression): Expression { + return BinaryExpression(this, BinaryOperator.GREATER_THAN, other) +} + +public infix fun Expression.lt(other: Expression): Expression { + return BinaryExpression(this, BinaryOperator.LESS_THAN, other) +} + +public infix fun Expression.ge(other: Expression): Expression { + return BinaryExpression(this, BinaryOperator.GREATER_OR_EQUAL, other) +} + +public infix fun Expression.le(other: Expression): Expression { + return BinaryExpression(this, BinaryOperator.LESS_OR_EQUAL, other) +} + +// Операторные функции для массивов + +public operator fun ArrayVariable.plus(other: ArrayVariable): Expression { + return ArrayBinaryExpression(this, ArrayBinaryOperator.ADD, other) +} + +public operator fun ArrayVariable.minus(other: ArrayVariable): Expression { + return ArrayBinaryExpression(this, ArrayBinaryOperator.SUBTRACT, other) +} + +public operator fun ArrayVariable.times(scalar: Expression): Expression { + return ArrayScalarExpression(this, ArrayScalarOperator.MULTIPLY, scalar) +} + +public operator fun ArrayVariable.div(scalar: Expression): Expression { + return ArrayScalarExpression(this, ArrayScalarOperator.DIVIDE, scalar) +} + +/** + * Функция для вызова пользовательской функции в выражении. + */ +public fun callFunction(function: Function, vararg args: Expression): FunctionCallExpression { + require(args.size == function.inputs.size) { + "Number of arguments does not match number of function inputs." + } + return FunctionCallExpression(function.name, args.toList()) +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayBinaryExpression.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayBinaryExpression.kt new file mode 100644 index 0000000..b2ad67c --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayBinaryExpression.kt @@ -0,0 +1,37 @@ +package space.kscience.controls.constructor.dsl.core.expressions + +import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit +import space.kscience.controls.constructor.dsl.core.variables.ArrayVariable + +/** + * Класс для представления бинарной операции между двумя массивами. + */ +public class ArrayBinaryExpression( + public val left: ArrayVariable, + public val operator: ArrayBinaryOperator, + public val right: ArrayVariable +) : Expression() { + + override val unit: PhysicalUnit = when (operator) { + ArrayBinaryOperator.ADD, ArrayBinaryOperator.SUBTRACT -> { + if (left.unit != right.unit) { + throw IllegalArgumentException("Units must be the same for array addition or subtraction.") + } + left.unit + } + ArrayBinaryOperator.MULTIPLY -> left.unit * right.unit + ArrayBinaryOperator.DIVIDE -> left.unit / right.unit + } + + override fun evaluate(values: Map): Double { + throw UnsupportedOperationException("Cannot evaluate array expression to a scalar value.") + } + + override fun checkDimensions() { + TODO() + } + + override fun derivative(variable: VariableExpression): Expression { + throw UnsupportedOperationException("Derivative of array expression is not implemented.") + } +} \ No newline at end of file diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayBinaryOperator.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayBinaryOperator.kt new file mode 100644 index 0000000..7f31301 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayBinaryOperator.kt @@ -0,0 +1,11 @@ +package space.kscience.controls.constructor.dsl.core.expressions + +/** + * Перечисление для бинарных операций над массивами. + */ +public enum class ArrayBinaryOperator { + ADD, + SUBTRACT, + MULTIPLY, + DIVIDE +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayExpression.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayExpression.kt new file mode 100644 index 0000000..982afdc --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayExpression.kt @@ -0,0 +1,3 @@ +package space.kscience.controls.constructor.dsl.core.expressions + +public sealed class ArrayExpression : Expression() \ No newline at end of file diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayScalarExpression.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayScalarExpression.kt new file mode 100644 index 0000000..f40a4a9 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayScalarExpression.kt @@ -0,0 +1,34 @@ +package space.kscience.controls.constructor.dsl.core.expressions + +import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit +import space.kscience.controls.constructor.dsl.core.variables.ArrayVariable + +/** + * Представляет операцию между массивом и скаляром. + */ +public class ArrayScalarExpression( + public val array: ArrayVariable, + public val operator: ArrayScalarOperator, + public val scalar: Expression +) : Expression() { + + override val unit: PhysicalUnit = when (operator) { + ArrayScalarOperator.MULTIPLY -> array.unit * scalar.unit + ArrayScalarOperator.DIVIDE -> array.unit / scalar.unit + } + + override fun evaluate(values: Map): Double { + // Невозможно вычислить массивное выражение до скалярного значения + throw UnsupportedOperationException("Cannot evaluate array expression to a scalar value.") + } + + override fun checkDimensions() { +// array.checkDimensions() + scalar.checkDimensions() + // Единицы измерения уже проверены в свойстве `unit` + } + + override fun derivative(variable: VariableExpression): Expression { + throw UnsupportedOperationException("Derivative of array expression is not implemented.") + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayScalarOperator.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayScalarOperator.kt new file mode 100644 index 0000000..f467eec --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayScalarOperator.kt @@ -0,0 +1,9 @@ +package space.kscience.controls.constructor.dsl.core.expressions + +/** + * Перечисление операторов между массивом и скаляром. + */ +public enum class ArrayScalarOperator { + MULTIPLY, + DIVIDE +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayVariableExpression.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayVariableExpression.kt new file mode 100644 index 0000000..35db9de --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ArrayVariableExpression.kt @@ -0,0 +1,42 @@ +package space.kscience.controls.constructor.dsl.core.expressions + +import space.kscience.controls.constructor.dsl.core.expressions.Expression +import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit +import space.kscience.controls.constructor.dsl.core.variables.ArrayVariable + +/** + * Представляет операцию между двумя массивами. + */ +public class ArrayVariableExpression( + public val left: ArrayVariable, + public val operator: ArrayBinaryOperator, + public val right: ArrayVariable +) : Expression() { + + override val unit: PhysicalUnit = when (operator) { + ArrayBinaryOperator.ADD, ArrayBinaryOperator.SUBTRACT -> { + if (left.unit != right.unit) { + throw IllegalArgumentException("Units must be the same for array addition or subtraction.") + } + left.unit + } + ArrayBinaryOperator.MULTIPLY -> left.unit * right.unit + ArrayBinaryOperator.DIVIDE -> left.unit / right.unit + } + + override fun evaluate(values: Map): Double { + // Невозможно вычислить массивное выражение до скалярного значения + throw UnsupportedOperationException("Cannot evaluate array expression to a scalar value.") + } + + override fun checkDimensions() { +// left.checkDimensions() +// right.checkDimensions() + // Единицы измерения уже проверены в свойстве `unit` + } + + override fun derivative(variable: VariableExpression): Expression { + // Производная массива не реализована + throw UnsupportedOperationException("Derivative of array expression is not implemented.") + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/BinaryExpression.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/BinaryExpression.kt new file mode 100644 index 0000000..8c2de51 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/BinaryExpression.kt @@ -0,0 +1,135 @@ +package space.kscience.controls.constructor.dsl.core.expressions + +import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit +import space.kscience.controls.constructor.dsl.core.units.Units +import kotlin.math.* + +/** + * Перечисление типов бинарных операций. + */ +public enum class BinaryOperator { + ADD, + SUBTRACT, + MULTIPLY, + DIVIDE, + POWER, + GREATER_THAN, + LESS_THAN, + GREATER_OR_EQUAL, + LESS_OR_EQUAL +} + +/** + * Класс для представления бинарной операции. + */ +public class BinaryExpression( + public val left: Expression, + public val operator: BinaryOperator, + public val right: Expression +) : Expression() { + + override val unit: PhysicalUnit = when (operator) { + BinaryOperator.ADD, BinaryOperator.SUBTRACT -> { + if (left.unit != right.unit) { + throw IllegalArgumentException("Units of left and right expressions must be the same for addition or subtraction.") + } + left.unit + } + BinaryOperator.MULTIPLY -> left.unit * right.unit + BinaryOperator.DIVIDE -> left.unit / right.unit + BinaryOperator.POWER -> { + if (right is ConstantExpression) { + left.unit.pow(right.value) + } else { + throw IllegalArgumentException("Exponent must be a constant expression.") + } + } + BinaryOperator.GREATER_THAN, BinaryOperator.LESS_THAN, + BinaryOperator.GREATER_OR_EQUAL, BinaryOperator.LESS_OR_EQUAL -> { + Units.dimensionless + } + } + + override fun evaluate(values: Map): Double { + val leftValue = left.evaluate(values) + val rightValue = right.evaluate(values) + return when (operator) { + BinaryOperator.ADD -> leftValue + rightValue + BinaryOperator.SUBTRACT -> leftValue - rightValue + BinaryOperator.MULTIPLY -> leftValue * rightValue + BinaryOperator.DIVIDE -> leftValue / rightValue + BinaryOperator.POWER -> leftValue.pow(rightValue) + BinaryOperator.GREATER_THAN -> if (leftValue > rightValue) 1.0 else 0.0 + BinaryOperator.LESS_THAN -> if (leftValue < rightValue) 1.0 else 0.0 + BinaryOperator.GREATER_OR_EQUAL -> if (leftValue >= rightValue) 1.0 else 0.0 + BinaryOperator.LESS_OR_EQUAL -> if (leftValue <= rightValue) 1.0 else 0.0 + } + } + + override fun checkDimensions() { + left.checkDimensions() + right.checkDimensions() + when (operator) { + BinaryOperator.ADD, BinaryOperator.SUBTRACT -> { + if (left.unit != right.unit) { + throw IllegalArgumentException("Units must be the same for addition or subtraction.") + } + } + BinaryOperator.MULTIPLY, BinaryOperator.DIVIDE -> { + // Единицы измерения уже вычислены в `unit` + } + BinaryOperator.POWER -> { + if (!right.unit.dimension.isDimensionless()) { + throw IllegalArgumentException("Exponent must be dimensionless.") + } + } + BinaryOperator.GREATER_THAN, BinaryOperator.LESS_THAN, + BinaryOperator.GREATER_OR_EQUAL, BinaryOperator.LESS_OR_EQUAL -> { + if (left.unit != right.unit) { + throw IllegalArgumentException("Units must be the same for comparison operations.") + } + } + } + } + + override fun derivative(variable: VariableExpression): Expression { + return when (operator) { + BinaryOperator.ADD, BinaryOperator.SUBTRACT -> { + BinaryExpression(left.derivative(variable), operator, right.derivative(variable)) + } + BinaryOperator.MULTIPLY -> { + // Правило произведения: (u*v)' = u'*v + u*v' + val leftDerivative = BinaryExpression(left.derivative(variable), BinaryOperator.MULTIPLY, right) + val rightDerivative = BinaryExpression(left, BinaryOperator.MULTIPLY, right.derivative(variable)) + BinaryExpression(leftDerivative, BinaryOperator.ADD, rightDerivative) + } + BinaryOperator.DIVIDE -> { + // Правило частного: (u/v)' = (u'*v - u*v') / v^2 + val numerator = BinaryExpression( + BinaryExpression(left.derivative(variable), BinaryOperator.MULTIPLY, right), + BinaryOperator.SUBTRACT, + BinaryExpression(left, BinaryOperator.MULTIPLY, right.derivative(variable)) + ) + val denominator = BinaryExpression(right, BinaryOperator.POWER, ConstantExpression(2.0, Units.dimensionless)) + BinaryExpression(numerator, BinaryOperator.DIVIDE, denominator) + } + BinaryOperator.POWER -> { + if (right is ConstantExpression) { + // (u^n)' = n * u^(n-1) * u' + val n = right.value + val newExponent = ConstantExpression(n - 1.0, Units.dimensionless) + val baseDerivative = left.derivative(variable) + val powerExpression = BinaryExpression(left, BinaryOperator.POWER, newExponent) + BinaryExpression( + BinaryExpression(ConstantExpression(n, Units.dimensionless), BinaryOperator.MULTIPLY, powerExpression), + BinaryOperator.MULTIPLY, + baseDerivative + ) + } else { + throw UnsupportedOperationException("Derivative of expression with variable exponent is not supported.") + } + } + else -> throw UnsupportedOperationException("Derivative not implemented for operator $operator.") + } + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ConstantExpression.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ConstantExpression.kt new file mode 100644 index 0000000..c475a50 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/ConstantExpression.kt @@ -0,0 +1,20 @@ +package space.kscience.controls.constructor.dsl.core.expressions + +import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit +import space.kscience.controls.constructor.dsl.core.units.Units + +/** + * Класс для представления константного значения. + */ +public class ConstantExpression(public val value: Double, override val unit: PhysicalUnit) : Expression() { + override fun evaluate(values: Map): Double { + return value + } + override fun checkDimensions() { + // Константа имеет единицу измерения, дополнительных проверок не требуется + } + + override fun derivative(variable: VariableExpression): Expression { + return ConstantExpression(0.0, Units.dimensionless) + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/DerivativeExpression.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/DerivativeExpression.kt new file mode 100644 index 0000000..d253e0b --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/DerivativeExpression.kt @@ -0,0 +1,30 @@ +package space.kscience.controls.constructor.dsl.core.expressions + +import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit +import space.kscience.controls.constructor.dsl.core.units.Units + +/** + * Класс для представления производной переменной по времени. + */ +public class DerivativeExpression( + public val variable: VariableExpression, + public val order: Int = 1 // Порядок производной (по умолчанию 1) +) : Expression() { + + override val unit: PhysicalUnit = variable.unit / Units.second.pow(order.toDouble()) + + override fun evaluate(values: Map): Double { + // вычисление производной невозможно в этом контексте + throw UnsupportedOperationException("Cannot evaluate derivative directly.") + } + + override fun checkDimensions() { + variable.checkDimensions() + // Единица измерения уже рассчитана + } + + override fun derivative(variable: VariableExpression): Expression { + // Производная от производной - это производная более высокого порядка + return DerivativeExpression(this.variable, this.order + 1) + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/Expression.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/Expression.kt new file mode 100644 index 0000000..8d808c0 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/Expression.kt @@ -0,0 +1,34 @@ +package space.kscience.controls.constructor.dsl.core.expressions + +import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit + +/** + * Абстрактный класс для представления математических выражений. + * Каждое выражение связано с единицей измерения. + */ +public abstract class Expression { + public abstract val unit: PhysicalUnit + + /** + * Метод для вычисления численного значения выражения. + * Параметр values содержит значения переменных. + */ + public abstract fun evaluate(values: Map): Double + + /** + * Проверка размерности выражения. + */ + public abstract fun checkDimensions() + + /** + * Упрощение выражения. + */ + public open fun simplify(): Expression = this + + /** + * Производная выражения по заданной переменной. + */ + public open fun derivative(variable: VariableExpression): Expression { + throw UnsupportedOperationException("Derivative not implemented for this expression type.") + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/FunctionCallExpression.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/FunctionCallExpression.kt new file mode 100644 index 0000000..39264b4 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/FunctionCallExpression.kt @@ -0,0 +1,91 @@ +package space.kscience.controls.constructor.dsl.core.expressions + +import space.kscience.controls.constructor.dsl.core.units.Units +import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit +import kotlin.math.* + +/** + * Класс для представления вызова математической функции. + */ +public class FunctionCallExpression( + public val functionName: String, + public val arguments: List +) : Expression() { + + override val unit: PhysicalUnit = determineUnit() + + private fun determineUnit(): PhysicalUnit { + return when (functionName) { + "sin", "cos", "tan", "asin", "acos", "atan", + "sinh", "cosh", "tanh", "asinh", "acosh", "atanh", + "exp", "log", "log10", "signum" -> Units.dimensionless + "sqrt", "cbrt", "abs" -> arguments[0].unit + "pow", "max", "min" -> arguments[0].unit + else -> throw IllegalArgumentException("Unknown function: $functionName") + } + } + + override fun evaluate(values: Map): Double { + val argValues = arguments.map { it.evaluate(values) } + return when (functionName) { + "sin" -> sin(argValues[0]) + "cos" -> cos(argValues[0]) + "tan" -> tan(argValues[0]) + "asin" -> asin(argValues[0]) + "acos" -> acos(argValues[0]) + "atan" -> atan(argValues[0]) + "sinh" -> sinh(argValues[0]) + "cosh" -> cosh(argValues[0]) + "tanh" -> tanh(argValues[0]) + "asinh" -> asinh(argValues[0]) + "acosh" -> acosh(argValues[0]) + "atanh" -> atanh(argValues[0]) + "exp" -> exp(argValues[0]) + "log" -> ln(argValues[0]) + "log10" -> log10(argValues[0]) + "sqrt" -> sqrt(argValues[0]) + "cbrt" -> cbrt(argValues[0]) + "abs" -> abs(argValues[0]) + "signum" -> sign(argValues[0]) + "pow" -> argValues[0].pow(argValues[1]) + "max" -> max(argValues[0], argValues[1]) + "min" -> min(argValues[0], argValues[1]) + else -> throw IllegalArgumentException("Unknown function: $functionName") + } + } + + override fun checkDimensions() { + when (functionName) { + "sin", "cos", "tan", "asin", "acos", "atan", + "sinh", "cosh", "tanh", "asinh", "acosh", "atanh", + "exp", "log", "log10", "signum" -> { + val argUnit = arguments[0].unit + if (!argUnit.dimension.isDimensionless()) { + throw IllegalArgumentException("Argument of $functionName must be dimensionless.") + } + } + "sqrt", "cbrt", "abs" -> { + // Единица измерения сохраняется, дополнительных проверок не требуется + } + "pow" -> { + val exponentUnit = arguments[1].unit + if (!exponentUnit.dimension.isDimensionless()) { + throw IllegalArgumentException("Exponent in $functionName must be dimensionless.") + } + } + "max", "min" -> { + if (arguments[0].unit != arguments[1].unit) { + throw IllegalArgumentException("Arguments of $functionName must have the same units.") + } + } + else -> throw IllegalArgumentException("Unknown function: $functionName") + } + // Проверяем размерности аргументов + arguments.forEach { it.checkDimensions() } + } + + override fun derivative(variable: VariableExpression): Expression { + // Реализация производной для функций + throw UnsupportedOperationException("Derivative for function $functionName is not implemented.") + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/PartialDerivativeExpression.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/PartialDerivativeExpression.kt new file mode 100644 index 0000000..76c3355 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/PartialDerivativeExpression.kt @@ -0,0 +1,31 @@ +package space.kscience.controls.constructor.dsl.core.expressions + +import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit + +/** + * Представляет частную производную переменной по другой переменной. + */ +public class PartialDerivativeExpression( + public val variable: VariableExpression, + public val respectTo: VariableExpression, + public val order: Int = 1 +) : Expression() { + + override val unit: PhysicalUnit = variable.unit / respectTo.unit.pow(order.toDouble()) + + override fun evaluate(values: Map): Double { + // Невозможно вычислить частную производную без дополнительного контекста + throw UnsupportedOperationException("Cannot evaluate partial derivative directly.") + } + + override fun checkDimensions() { + variable.checkDimensions() + respectTo.checkDimensions() + // Единицы измерения уже рассчитаны + } + + override fun derivative(variable: VariableExpression): Expression { + // Производная частной производной не реализована + throw UnsupportedOperationException("Derivative of partial derivative is not implemented.") + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/VariableExpression.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/VariableExpression.kt new file mode 100644 index 0000000..5a03a2f --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/expressions/VariableExpression.kt @@ -0,0 +1,32 @@ +package space.kscience.controls.constructor.dsl.core.expressions + +import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit +import space.kscience.controls.constructor.dsl.core.units.Units +import space.kscience.controls.constructor.dsl.core.variables.Variable + +/** + * Класс для представления переменной в выражении. + */ +public class VariableExpression(public val variable: Variable) : Expression() { + override val unit: PhysicalUnit = variable.unit + + override fun evaluate(values: Map): Double { + return values[variable.name] ?: throw IllegalArgumentException("Variable ${variable.name} is not defined.") + } + + override fun checkDimensions() { + // Переменная уже имеет единицу измерения, дополнительных проверок не требуется + } + + override fun derivative(variable: VariableExpression): Expression { + return if (this.variable == variable.variable) { + ConstantExpression(1.0, Units.dimensionless) + } else { + ConstantExpression(0.0, Units.dimensionless) + } + } + + public fun partialDerivative(respectTo: VariableExpression, order: Int = 1): PartialDerivativeExpression { + return PartialDerivativeExpression(this, respectTo, order) + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/functions/Function.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/functions/Function.kt new file mode 100644 index 0000000..938bb46 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/functions/Function.kt @@ -0,0 +1,14 @@ +package space.kscience.controls.constructor.dsl.core.functions + +import space.kscience.controls.constructor.dsl.core.algorithms.Algorithm +import space.kscience.controls.constructor.dsl.core.variables.Variable + +/** + * Класс для представления пользовательской функции. + */ +public data class Function( + val name: String, + val inputs: List, + val outputs: List, + val algorithm: Algorithm +) diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/functions/FunctionBuilder.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/functions/FunctionBuilder.kt new file mode 100644 index 0000000..25fb6f2 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/functions/FunctionBuilder.kt @@ -0,0 +1,50 @@ +package space.kscience.controls.constructor.dsl.core.functions + +import space.kscience.controls.constructor.dsl.core.algorithms.AlgorithmBuilder +import space.kscience.controls.constructor.dsl.core.algorithms.Algorithm +import space.kscience.controls.constructor.dsl.core.components.Model +import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit +import space.kscience.controls.constructor.dsl.core.variables.Causality +import space.kscience.controls.constructor.dsl.core.variables.Variable + +/** + * Класс для построения пользовательской функции. + */ +public class FunctionBuilder(private val name: String) { + public val inputs: MutableList = mutableListOf() + public val outputs: MutableList = mutableListOf() + private val algorithmBuilder = AlgorithmBuilder() + + /** + * Определение входного параметра функции. + */ + public fun input(name: String, unit: PhysicalUnit): Variable { + val variable = Variable(name, unit, causality = Causality.INPUT) + inputs.add(variable) + return variable + } + + /** + * Определение выходного параметра функции. + */ + public fun output(name: String, unit: PhysicalUnit): Variable { + val variable = Variable(name, unit, causality = Causality.OUTPUT) + outputs.add(variable) + return variable + } + + /** + * Определение алгоритмического тела функции. + */ + public fun algorithm(init: AlgorithmBuilder.() -> Unit) { + algorithmBuilder.init() + } + + /** + * Построение функции. + */ + public fun build(): Function { + val algorithm = algorithmBuilder.build() + return Function(name, inputs, outputs, algorithm) + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/simulation/NumericSolver.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/simulation/NumericSolver.kt new file mode 100644 index 0000000..fdae759 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/simulation/NumericSolver.kt @@ -0,0 +1,37 @@ +package space.kscience.controls.constructor.dsl.core.simulation + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.StateFlow +import space.kscience.controls.constructor.dsl.core.EquationSystem +import space.kscience.controls.constructor.dsl.core.events.Event + +public interface NumericSolver { + /** + * Инициализирует решатель. + */ + public suspend fun initialize( + equationSystem: EquationSystem, + context: SimulationContext + ) + + /** + * Выполняет один шаг симуляции. + */ + public suspend fun step(context: SimulationContext, timeStep: Double): Boolean + + /** + * Запускает симуляцию. + */ + public suspend fun solve( + equationSystem: EquationSystem, + context: SimulationContext, + timeStart: Double, + timeEnd: Double, + timeStep: Double + ): SimulationResult +} + +public data class SimulationResult( + val timePoints: List, + val variableValues: Map> +) diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/simulation/SimulationContext.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/simulation/SimulationContext.kt new file mode 100644 index 0000000..200b8fe --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/simulation/SimulationContext.kt @@ -0,0 +1,93 @@ +package space.kscience.controls.constructor.dsl.core.simulation + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch +import space.kscience.controls.constructor.dsl.core.events.Event +import space.kscience.controls.constructor.dsl.core.variables.Variable + +/** + * Класс для хранения состояния симуляции. + */ +public class SimulationContext( + private val scope: CoroutineScope +) { + public var currentTime: Double = 0.0 + private val _variableValues: MutableMap> = mutableMapOf() + + // Канал для событий + private val eventChannel = Channel() + + // Список событий + public val events: MutableList = mutableListOf() + + /** + * Регистрирует переменную в контексте симуляции. + */ + public fun registerVariable(variable: Variable) { + _variableValues[variable.name] = MutableStateFlow(variable.value) + } + + /** + * Обновляет значение переменной. + */ + public suspend fun updateVariable(name: String, value: Double) { + _variableValues[name]?.emit(value) + ?: throw IllegalArgumentException("Variable $name is not defined in the simulation context.") + } + + /** + * Получает текущее значение переменной. + */ + public fun getCurrentVariableValue(name: String): Double { + return _variableValues[name]?.value + ?: throw IllegalArgumentException("Variable $name is not defined in the simulation context.") + } + + /** + * Возвращает текущие значения всех переменных. + */ + public fun getVariableValues(): Map { + return _variableValues.mapValues { it.value.value } + } + + /** + * Отправляет событие на обработку. + */ + public suspend fun sendEvent(event: Event) { + eventChannel.send(event) + } + + /** + * Запускает обработку событий. + */ + public fun startEventHandling() { + scope.launch { + for (event in eventChannel) { + if (event.checkEvent(this@SimulationContext)) { + event.executeActions(this@SimulationContext) + } + } + } + } + + /** + * Обрабатывает события из списка events. + */ + public suspend fun handleEvents() { + for (event in events) { + if (event.checkEvent(this)) { + event.executeActions(this) + } + } + } + + /** + * Увеличивает время симуляции. + */ + public suspend fun advanceTime(deltaTime: Double) { + currentTime += deltaTime + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/units/Dimension.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/units/Dimension.kt new file mode 100644 index 0000000..a4ae362 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/units/Dimension.kt @@ -0,0 +1,78 @@ +package space.kscience.controls.constructor.dsl.core.units + +import kotlin.math.abs + +/** + * Класс для представления размерности физической величины. + */ +public data class Dimension( + val length: Double = 0.0, + val mass: Double = 0.0, + val time: Double = 0.0, + val electricCurrent: Double = 0.0, + val temperature: Double = 0.0, + val amountOfSubstance: Double = 0.0, + val luminousIntensity: Double = 0.0 +) { + public operator fun plus(other: Dimension): Dimension { + return Dimension( + length + other.length, + mass + other.mass, + time + other.time, + electricCurrent + other.electricCurrent, + temperature + other.temperature, + amountOfSubstance + other.amountOfSubstance, + luminousIntensity + other.luminousIntensity + ) + } + + public operator fun minus(other: Dimension): Dimension { + return Dimension( + length - other.length, + mass - other.mass, + time - other.time, + electricCurrent - other.electricCurrent, + temperature - other.temperature, + amountOfSubstance - other.amountOfSubstance, + luminousIntensity - other.luminousIntensity + ) + } + + public operator fun times(factor: Double): Dimension { + return Dimension( + length * factor, + mass * factor, + time * factor, + electricCurrent * factor, + temperature * factor, + amountOfSubstance * factor, + luminousIntensity * factor + ) + } + + /** + * Проверяет, является ли размерность безразмерной. + */ + public fun isDimensionless(epsilon: Double = 1e-10): Boolean { + return abs(length) < epsilon && + abs(mass) < epsilon && + abs(time) < epsilon && + abs(electricCurrent) < epsilon && + abs(temperature) < epsilon && + abs(amountOfSubstance) < epsilon && + abs(luminousIntensity) < epsilon + } + + /** + * Проверяет совместимость размерностей. + */ + public fun isCompatibleWith(other: Dimension, epsilon: Double = 1e-10): Boolean { + return abs(length - other.length) < epsilon && + abs(mass - other.mass) < epsilon && + abs(time - other.time) < epsilon && + abs(electricCurrent - other.electricCurrent) < epsilon && + abs(temperature - other.temperature) < epsilon && + abs(amountOfSubstance - other.amountOfSubstance) < epsilon && + abs(luminousIntensity - other.luminousIntensity) < epsilon + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/units/PhysicalUnit.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/units/PhysicalUnit.kt new file mode 100644 index 0000000..54954f4 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/units/PhysicalUnit.kt @@ -0,0 +1,51 @@ +package space.kscience.controls.constructor.dsl.core.units + +import kotlin.math.pow + +/** + * Класс для представления физической единицы измерения. + */ +public data class PhysicalUnit( + val name: String, + val symbol: String, + val conversionFactorToSI: Double, + val dimension: Dimension +) { + public operator fun times(other: PhysicalUnit): PhysicalUnit { + return PhysicalUnit( + name = "$name·${other.name}", + symbol = "$symbol·${other.symbol}", + conversionFactorToSI = this.conversionFactorToSI * other.conversionFactorToSI, + dimension = this.dimension + other.dimension + ) + } + + public operator fun div(other: PhysicalUnit): PhysicalUnit { + return PhysicalUnit( + name = "$name/${other.name}", + symbol = "$symbol/${other.symbol}", + conversionFactorToSI = this.conversionFactorToSI / other.conversionFactorToSI, + dimension = this.dimension - other.dimension + ) + } + + public fun pow(exponent: Double): PhysicalUnit { + return PhysicalUnit( + name = "$name^$exponent", + symbol = "${symbol}^$exponent", + conversionFactorToSI = this.conversionFactorToSI.pow(exponent), + dimension = this.dimension * exponent + ) + } + + /** + * Преобразует значение из текущей единицы в целевую единицу. + */ + public fun convertValueTo(valueInThisUnit: Double, targetUnit: PhysicalUnit): Double { + require(this.dimension.isCompatibleWith(targetUnit.dimension)) { + "Units are not compatible: $this and $targetUnit" + } + val valueInSI = valueInThisUnit * this.conversionFactorToSI + return valueInSI / targetUnit.conversionFactorToSI + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/units/Units.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/units/Units.kt new file mode 100644 index 0000000..8eeeccd --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/units/Units.kt @@ -0,0 +1,89 @@ +package space.kscience.controls.constructor.dsl.core.units + +import kotlin.math.* + +public object Units { + private val unitsRegistry = mutableMapOf() + + // Основные единицы СИ + public val meter: PhysicalUnit = PhysicalUnit("meter", "m", 1.0, Dimension(length = 1.0)) + public val kilogram: PhysicalUnit = PhysicalUnit("kilogram", "kg", 1.0, Dimension(mass = 1.0)) + public val second: PhysicalUnit = PhysicalUnit("second", "s", 1.0, Dimension(time = 1.0)) + public val ampere: PhysicalUnit = PhysicalUnit("ampere", "A", 1.0, Dimension(electricCurrent = 1.0)) + public val kelvin: PhysicalUnit = PhysicalUnit("kelvin", "K", 1.0, Dimension(temperature = 1.0)) + public val mole: PhysicalUnit = PhysicalUnit("mole", "mol", 1.0, Dimension(amountOfSubstance = 1.0)) + public val candela: PhysicalUnit = PhysicalUnit("candela", "cd", 1.0, Dimension(luminousIntensity = 1.0)) + public val radian: PhysicalUnit = PhysicalUnit("radian", "rad", 1.0, Dimension()) + public val hertz: PhysicalUnit = PhysicalUnit("hertz", "Hz", 1.0, Dimension(time = -1.0)) + + public val dimensionless: PhysicalUnit = PhysicalUnit("dimensionless", "", 1.0, Dimension()) + + init { + // Регистрируем основные единицы + registerUnit(meter) + registerUnit(kilogram) + registerUnit(second) + registerUnit(ampere) + registerUnit(kelvin) + registerUnit(mole) + registerUnit(candela) + registerUnit(radian) + registerUnit(hertz) + } + + /** + * Регистрирует единицу измерения в реестре. + */ + public fun registerUnit(unit: PhysicalUnit) { + unitsRegistry[unit.name] = unit + } + + /** + * Получает единицу измерения по имени. + */ + public fun getUnit(name: String): PhysicalUnit? { + return unitsRegistry[name] + } + + /** + * Список префиксов СИ. + */ + public enum class Prefix(public val prefixName: String, public val symbol: String, public val factor: Double) { + YOTTA("yotta", "Y", 1e24), + ZETTA("zetta", "Z", 1e21), + EXA("exa", "E", 1e18), + PETA("peta", "P", 1e15), + TERA("tera", "T", 1e12), + GIGA("giga", "G", 1e9), + MEGA("mega", "M", 1e6), + KILO("kilo", "k", 1e3), + HECTO("hecto", "h", 1e2), + DECA("deca", "da", 1e1), + DECI("deci", "d", 1e-1), + CENTI("centi", "c", 1e-2), + MILLI("milli", "m", 1e-3), + MICRO("micro", "μ", 1e-6), + NANO("nano", "n", 1e-9), + PICO("pico", "p", 1e-12), + FEMTO("femto", "f", 1e-15), + ATTO("atto", "a", 1e-18), + ZEPTO("zepto", "z", 1e-21), + YOCTO("yocto", "y", 1e-24) + } + + /** + * Создает новую единицу с заданным префиксом. + */ + public fun withPrefix(prefix: Prefix, unit: PhysicalUnit): PhysicalUnit { + val newName = "${prefix.prefixName}${unit.name}" + val newSymbol = "${prefix.symbol}${unit.symbol}" + val newConversionFactor = unit.conversionFactorToSI * prefix.factor + val newUnit = PhysicalUnit(newName, newSymbol, newConversionFactor, unit.dimension) + registerUnit(newUnit) + return newUnit + } + + public val kilometer: PhysicalUnit = withPrefix(Prefix.KILO, meter) + public val millimeter: PhysicalUnit = withPrefix(Prefix.MILLI, meter) + public val microsecond: PhysicalUnit = withPrefix(Prefix.MICRO, second) +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/AbstractVariable.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/AbstractVariable.kt new file mode 100644 index 0000000..9282ae7 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/AbstractVariable.kt @@ -0,0 +1,13 @@ +package space.kscience.controls.constructor.dsl.core.variables + +import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit + +/** + * Абстрактный класс для переменных. + */ +public abstract class AbstractVariable( + public val name: String, + public val unit: PhysicalUnit, + public val variability: Variability, + public val causality: Causality +) \ No newline at end of file diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ArrayVariable.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ArrayVariable.kt new file mode 100644 index 0000000..ee4f371 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ArrayVariable.kt @@ -0,0 +1,28 @@ +package space.kscience.controls.constructor.dsl.core.variables + +import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit +import org.jetbrains.kotlinx.multik.ndarray.data.D2 +import org.jetbrains.kotlinx.multik.ndarray.data.NDArray +import org.jetbrains.kotlinx.multik.ndarray.data.set +import org.jetbrains.kotlinx.multik.ndarray.operations.plus + +/** + * Класс для представления переменной-массива. + * Использует Multik для работы с многомерными массивами. + */ +public class ArrayVariable( + name: String, + unit: PhysicalUnit, + public val array: NDArray, + causality: Causality = Causality.INTERNAL +) : AbstractVariable(name, unit, Variability.CONTINUOUS, causality) { + + public fun add(other: ArrayVariable): ArrayVariable { + require(this.array.shape contentEquals other.array.shape) { + "Array shapes do not match." + } + val resultArray = this.array + other.array + return ArrayVariable("$name + ${other.name}", unit, resultArray, causality) + } + +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/Causality.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/Causality.kt new file mode 100644 index 0000000..50d1eb5 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/Causality.kt @@ -0,0 +1,10 @@ +package space.kscience.controls.constructor.dsl.core.variables + +/** + * Перечисление каузальности переменной. + */ +public enum class Causality { + INPUT, + OUTPUT, + INTERNAL +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ConstantVariable.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ConstantVariable.kt new file mode 100644 index 0000000..287274a --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ConstantVariable.kt @@ -0,0 +1,12 @@ +package space.kscience.controls.constructor.dsl.core.variables + +import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit + +/** + * Класс для представления константы. + */ +public class ConstantVariable( + name: String, + unit: PhysicalUnit, + public val value: Double +) : AbstractVariable(name, unit, Variability.CONSTANT, Causality.INTERNAL) diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ContinuousVariable.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ContinuousVariable.kt new file mode 100644 index 0000000..a4ecf87 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ContinuousVariable.kt @@ -0,0 +1,22 @@ +package space.kscience.controls.constructor.dsl.core.variables + +import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit + +public class ContinuousVariable( + name: String, + unit: PhysicalUnit, + public var value: Double = 0.0, + public val min: Double? = null, + public val max: Double? = null, + causality: Causality = Causality.INTERNAL +) : AbstractVariable(name, unit, Variability.CONTINUOUS, causality) { + init { + // Проверка допустимого диапазона + if (min != null && value < min) { + throw IllegalArgumentException("Value of $name is less than minimum allowed value.") + } + if (max != null && value > max) { + throw IllegalArgumentException("Value of $name is greater than maximum allowed value.") + } + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/DiscreteVariable.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/DiscreteVariable.kt new file mode 100644 index 0000000..1b741e8 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/DiscreteVariable.kt @@ -0,0 +1,13 @@ +package space.kscience.controls.constructor.dsl.core.variables + +import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit + +/** + * Класс для представления дискретной переменной. + */ +public class DiscreteVariable( + name: String, + unit: PhysicalUnit, + public var value: Double = 0.0, + causality: Causality = Causality.INTERNAL +) : AbstractVariable(name, unit, Variability.DISCRETE, causality) diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ParameterVariable.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ParameterVariable.kt new file mode 100644 index 0000000..4cc5904 --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/ParameterVariable.kt @@ -0,0 +1,25 @@ +package space.kscience.controls.constructor.dsl.core.variables + +import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit + +/** + * Класс для представления параметра (неизменяемой величины). + */ +public open class ParameterVariable( + name: String, + unit: PhysicalUnit, + public val value: Double, + public val min: Double? = null, + public val max: Double? = null, + causality: Causality = Causality.INTERNAL +) : AbstractVariable(name, unit, Variability.PARAMETER, causality) { + init { + // Проверка допустимого диапазона + if (min != null && value < min) { + throw IllegalArgumentException("Value of parameter $name is less than minimum allowed value.") + } + if (max != null && value > max) { + throw IllegalArgumentException("Value of parameter $name is greater than maximum allowed value.") + } + } +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/Variability.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/Variability.kt new file mode 100644 index 0000000..d51a75f --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/Variability.kt @@ -0,0 +1,11 @@ +package space.kscience.controls.constructor.dsl.core.variables + +/** + * Перечисление типов изменчивости переменной. + */ +public enum class Variability { + CONSTANT, + PARAMETER, + DISCRETE, + CONTINUOUS +} diff --git a/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/Variable.kt b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/Variable.kt new file mode 100644 index 0000000..ba4f47b --- /dev/null +++ b/controls-constructor/src/commonMain/kotlin/space/kscience/controls/constructor/dsl/core/variables/Variable.kt @@ -0,0 +1,28 @@ +package space.kscience.controls.constructor.dsl.core.variables + +import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit + +/** + * Класс для представления переменной. + */ +public open class Variable( + public val name: String, + public val unit: PhysicalUnit, + public open var value: Double = 0.0, + public val min: Double? = null, + public val max: Double? = null, + public var variability: Variability = Variability.CONTINUOUS, + public var causality: Causality = Causality.INTERNAL +) { + init { + // Проверка, что значение находится в допустимом диапазоне + if (min != null && value < min) { + throw IllegalArgumentException("Value of $name is less than minimum allowed value.") + } + if (max != null && value > max) { + throw IllegalArgumentException("Value of $name is greater than maximum allowed value.") + } + } + + public open fun checkDimensions() {} +} diff --git a/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/EquationsTest.kt b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/EquationsTest.kt new file mode 100644 index 0000000..8f3d4b1 --- /dev/null +++ b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/EquationsTest.kt @@ -0,0 +1,89 @@ +package space.kscience.controls.constructor + +import space.kscience.controls.constructor.dsl.core.EquationSystem +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertTrue +import space.kscience.controls.constructor.dsl.core.equations.* +import space.kscience.controls.constructor.dsl.core.expressions.* +import space.kscience.controls.constructor.dsl.core.units.Units +import space.kscience.controls.constructor.dsl.core.variables.Variable + +class EquationsTest { + + @Test + fun testCreatingEquations() { + val xVar = Variable("x", Units.meter) + val xExpr = VariableExpression(xVar) + val yVar = Variable("y", Units.meter) + val yExpr = VariableExpression(yVar) + val constExpr = ConstantExpression(5.0, Units.meter) + + // Regular equation: x = y + 5 + val equation = Equation(xExpr, BinaryExpression(yExpr, BinaryOperator.ADD, constExpr)) + assertEquals(xExpr, equation.leftExpression) + assertEquals(BinaryExpression(yExpr, BinaryOperator.ADD, constExpr), equation.rightExpression) + + // Conditional equation: if x > 0 then y = x else y = -x + val condition = BinaryExpression(xExpr, BinaryOperator.GREATER_THAN, ConstantExpression(0.0, Units.meter)) + val trueEquation = Equation(yExpr, xExpr) + val falseEquation = Equation(yExpr, BinaryExpression(ConstantExpression(0.0, Units.meter), BinaryOperator.SUBTRACT, xExpr)) + val conditionalEquation = ConditionalEquation(condition, trueEquation, falseEquation) + assertEquals(condition, conditionalEquation.condition) + assertEquals(trueEquation, conditionalEquation.trueEquation) + assertEquals(falseEquation, conditionalEquation.falseEquation) + + // Initial equation: x(0) = 0 + val initialEquation = InitialEquation(xExpr, ConstantExpression(0.0, Units.meter)) + assertEquals(xExpr, initialEquation.leftExpression) + assertEquals(ConstantExpression(0.0, Units.meter), initialEquation.rightExpression) + } + + @Test + fun testUnitCompatibilityInEquations() { + val lengthVar = Variable("length", Units.meter) + val lengthExpr = VariableExpression(lengthVar) + val timeVar = Variable("time", Units.second) + val timeExpr = VariableExpression(timeVar) + + // Correct equation: length = length + length + val validEquation = Equation(lengthExpr, BinaryExpression(lengthExpr, BinaryOperator.ADD, ConstantExpression(5.0, Units.meter))) + assertFailsWith { + validEquation + } + + // Incorrect equation: length = time + val exception = assertFailsWith { + Equation(lengthExpr, timeExpr) + } + assertEquals("Units of left and right expressions in the equation are not compatible.", exception.message) + } + + @Test + fun testEquationSystem() { + val xVar = Variable("x", Units.meter) + val xExpr = VariableExpression(xVar) + val vVar = Variable("v", Units.meter / Units.second) + val vExpr = VariableExpression(vVar) + val aVar = Variable("a", Units.meter / Units.second.pow(2.0)) + val aExpr = VariableExpression(aVar) + + // Differential equation: der(x) = v + val derX = DerivativeExpression(xExpr) + val eq1 = Equation(derX, vExpr) + + // Algebraic equation: v = a * t + val tVar = Variable("t", Units.second) + val tExpr = VariableExpression(tVar) + val eq2 = Equation(vExpr, BinaryExpression(aExpr, BinaryOperator.MULTIPLY, tExpr)) + + val equationSystem = EquationSystem() + equationSystem.addEquation(eq1) + equationSystem.addEquation(eq2) + + assertEquals(2, equationSystem.equations.size) + assertTrue(equationSystem.variables.containsAll(listOf("x", "v", "a", "t"))) + assertTrue(equationSystem.derivativeVariables.contains("x")) + } +} diff --git a/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/EventsAndActionsTest.kt b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/EventsAndActionsTest.kt new file mode 100644 index 0000000..2eb91d0 --- /dev/null +++ b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/EventsAndActionsTest.kt @@ -0,0 +1,73 @@ +package space.kscience.controls.constructor + +import kotlin.test.Test +import kotlin.test.assertEquals +import space.kscience.controls.constructor.dsl.core.events.Event +import space.kscience.controls.constructor.dsl.core.events.ReinitAction +import space.kscience.controls.constructor.dsl.core.events.AssignAction +import space.kscience.controls.constructor.dsl.core.expressions.BinaryExpression +import space.kscience.controls.constructor.dsl.core.expressions.BinaryOperator +import space.kscience.controls.constructor.dsl.core.expressions.ConstantExpression +import space.kscience.controls.constructor.dsl.core.expressions.VariableExpression +import space.kscience.controls.constructor.dsl.core.simulation.SimulationContext +import space.kscience.controls.constructor.dsl.core.units.Units +import space.kscience.controls.constructor.dsl.core.variables.Variable +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.runTest + +class EventsAndActionsTest { + + @Test + fun testCreatingEvents() { + val xVar = Variable("x", Units.meter) + val xExpr = VariableExpression(xVar) + + val condition = BinaryExpression(xExpr, BinaryOperator.GREATER_THAN, ConstantExpression(10.0, Units.meter)) + val action = AssignAction(xVar, ConstantExpression(0.0, Units.meter)) + + val event = Event(condition, listOf(action)) + + assertEquals(condition, event.condition) + assertEquals(1, event.actions.size) + assertEquals(action, event.actions[0]) + } + + @Test + fun testProcessingEventsInSimulation() = runTest { + val xVar = Variable("x", Units.meter) + val xExpr = VariableExpression(xVar) + val context = SimulationContext(CoroutineScope(Dispatchers.Default)).apply { + registerVariable(xVar) // Регистрация переменной + launch { updateVariable("x", 5.0) } // Установка начального значения переменной + } + + val condition = BinaryExpression(xExpr, BinaryOperator.GREATER_THAN, ConstantExpression(10.0, Units.meter)) + val action = AssignAction(xVar, ConstantExpression(0.0, Units.meter)) + val event = Event(condition, listOf(action)) + + context.events.add(event) + + // Simulate x crossing the threshold + context.updateVariable("x", 15.0) + context.handleEvents() + + assertEquals(0.0, context.getCurrentVariableValue("x")) + } + + @Test + fun testReinitAction() = runTest { + val vVar = Variable("v", Units.meter / Units.second) + val vExpr = VariableExpression(vVar) + val context = SimulationContext(CoroutineScope(Dispatchers.Default)).apply { + registerVariable(vVar) // Регистрация переменной + launch { updateVariable("v", 20.0) } // Установка начального значения переменной + } + + val action = ReinitAction(vVar, ConstantExpression(0.0, Units.meter / Units.second)) + action.execute(context) + + assertEquals(0.0, context.getCurrentVariableValue("v")) + } +} diff --git a/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/ExpressionsTest.kt b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/ExpressionsTest.kt new file mode 100644 index 0000000..c86bd7c --- /dev/null +++ b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/ExpressionsTest.kt @@ -0,0 +1,101 @@ +package space.kscience.controls.constructor + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertTrue +import space.kscience.controls.constructor.dsl.core.expressions.* +import space.kscience.controls.constructor.dsl.core.units.Units +import space.kscience.controls.constructor.dsl.core.variables.Variable + +class ExpressionsTest { + + @Test + fun testCreationOfDifferentTypesOfExpressions() { + // Constant expression + val constExpr = ConstantExpression(5.0, Units.meter) + assertEquals(5.0, constExpr.value) + assertEquals(Units.meter, constExpr.unit) + + // Variable expression + val speedVar = Variable("speed", Units.meter / Units.second) + val speedExpr = VariableExpression(speedVar) + assertEquals(speedVar, speedExpr.variable) + assertEquals(Units.meter / Units.second, speedExpr.unit) + + // Binary expression (addition) + val distanceVar = Variable("distance", Units.meter) + val distanceExpr = VariableExpression(distanceVar) + val totalDistanceExpr = BinaryExpression(distanceExpr, BinaryOperator.ADD, constExpr) + assertEquals(Units.meter, totalDistanceExpr.unit) + + // Function call expression + val sinExpr = FunctionCallExpression("sin", listOf(constExpr)) + assertEquals(Units.dimensionless, sinExpr.unit) + } + + @Test + fun testEvaluationOfExpressions() { + val xVar = Variable("x", Units.meter) + val xExpr = VariableExpression(xVar) + val constExpr = ConstantExpression(2.0, Units.meter) + + val values = mapOf("x" to 3.0) + + // Binary expression: x + 2 + val sumExpr = BinaryExpression(xExpr, BinaryOperator.ADD, constExpr) + assertEquals(5.0, sumExpr.evaluate(values)) + + // Binary expression: x * 2 + val mulExpr = BinaryExpression(xExpr, BinaryOperator.MULTIPLY, ConstantExpression(2.0, Units.dimensionless)) + assertEquals(6.0, mulExpr.evaluate(values)) + + // Function call: sin(x) + val sinExpr = FunctionCallExpression("sin", listOf(xExpr)) + assertEquals(kotlin.math.sin(3.0), sinExpr.evaluate(values)) + } + + @Test + fun testDimensionCheckingInExpressions() { + val lengthVar = Variable("length", Units.meter) + val timeVar = Variable("time", Units.second) + val lengthExpr = VariableExpression(lengthVar) + val timeExpr = VariableExpression(timeVar) + + // Correct addition + val sumExpr = BinaryExpression(lengthExpr, BinaryOperator.ADD, ConstantExpression(5.0, Units.meter)) + assertFailsWith { + sumExpr.checkDimensions() + } + + // Incorrect addition (length + time) + val invalidSumExpr = BinaryExpression(lengthExpr, BinaryOperator.ADD, timeExpr) + val exception = assertFailsWith { + invalidSumExpr.checkDimensions() + } + assertEquals("Units must be the same for addition or subtraction.", exception.message) + } + + @Test + fun testDerivativeOfExpressions() { + val xVar = Variable("x", Units.meter) + val xExpr = VariableExpression(xVar) + val constExpr = ConstantExpression(5.0, Units.meter) + + // Derivative of constant: should be zero + val derivativeConst = constExpr.derivative(xExpr) + assertTrue(derivativeConst is ConstantExpression) + assertEquals(0.0, derivativeConst.value) + + // Derivative of variable with respect to itself: should be one + val derivativeVar = xExpr.derivative(xExpr) + assertTrue(derivativeVar is ConstantExpression) + assertEquals(1.0, derivativeVar.value) + + // Derivative of x + 5 with respect to x: should be one + val sumExpr = BinaryExpression(xExpr, BinaryOperator.ADD, constExpr) + val derivativeSum = sumExpr.derivative(xExpr) + assertTrue(derivativeSum is ConstantExpression) + assertEquals(1.0, derivativeSum.value) + } +} diff --git a/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/FunctionsTest.kt b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/FunctionsTest.kt new file mode 100644 index 0000000..20db6bb --- /dev/null +++ b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/FunctionsTest.kt @@ -0,0 +1,29 @@ +package space.kscience.controls.constructor + +import kotlin.test.Test +import kotlin.test.assertEquals +import space.kscience.controls.constructor.dsl.core.functions.FunctionBuilder +import space.kscience.controls.constructor.dsl.core.expressions.* +import space.kscience.controls.constructor.dsl.core.variables.Variable +import space.kscience.controls.constructor.dsl.core.units.Units + +class FunctionsTest { + + @Test + fun testCreatingFunctions() { + val functionBuilder = FunctionBuilder("add").apply { + val aVar = input("a", Units.dimensionless) + val bVar = input("b", Units.dimensionless) + val resultVar = output("result", Units.dimensionless) + + algorithm { + assign(resultVar, BinaryExpression(VariableExpression(aVar), BinaryOperator.ADD, VariableExpression(bVar))) + } + } + + val addFunction = functionBuilder.build() + assertEquals("add", addFunction.name) + assertEquals(2, addFunction.inputs.size) + assertEquals(1, addFunction.outputs.size) + } +} diff --git a/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/UnitsTest.kt b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/UnitsTest.kt new file mode 100644 index 0000000..81d5081 --- /dev/null +++ b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/UnitsTest.kt @@ -0,0 +1,85 @@ +package space.kscience.controls.constructor + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import space.kscience.controls.constructor.dsl.core.units.Dimension +import space.kscience.controls.constructor.dsl.core.units.PhysicalUnit +import space.kscience.controls.constructor.dsl.core.units.Units + +class UnitsTest { + + @Test + fun testCreationOfBaseUnits() { + val meter = Units.meter + assertEquals("meter", meter.name) + assertEquals("m", meter.symbol) + assertEquals(1.0, meter.conversionFactorToSI) + assertEquals(Dimension(length = 1.0), meter.dimension) + + val kilogram = Units.kilogram + assertEquals("kilogram", kilogram.name) + assertEquals("kg", kilogram.symbol) + assertEquals(1.0, kilogram.conversionFactorToSI) + assertEquals(Dimension(mass = 1.0), kilogram.dimension) + + val second = Units.second + assertEquals("second", second.name) + assertEquals("s", second.symbol) + assertEquals(1.0, second.conversionFactorToSI) + assertEquals(Dimension(time = 1.0), second.dimension) + } + + @Test + fun testArithmeticOperationsOnUnits() { + val meter = Units.meter + val second = Units.second + val kilogram = Units.kilogram + + val velocityUnit = meter / second + assertEquals(Dimension(length = 1.0, time = -1.0), velocityUnit.dimension) + assertEquals("m/s", velocityUnit.symbol) + + val accelerationUnit = meter / (second * second) + assertEquals(Dimension(length = 1.0, time = -2.0), accelerationUnit.dimension) + assertEquals("m/s^2", accelerationUnit.symbol) + + val forceUnit = kilogram * accelerationUnit + assertEquals(Dimension(mass = 1.0, length = 1.0, time = -2.0), forceUnit.dimension) + assertEquals("kg*m/s^2", forceUnit.symbol) + } + + @Test + fun testUnitCompatibilityForAdditionAndSubtraction() { + val meter = Units.meter + val kilogram = Units.kilogram + + // Correct addition + val length1 = meter + val length2 = meter + assertEquals(length1.dimension, length2.dimension) + + // Incorrect addition should throw exception + val mass = kilogram + val exception = assertFailsWith { + if (length1.dimension != mass.dimension) { + throw IllegalArgumentException("Units are not compatible for addition.") + } + } + assertEquals("Units are not compatible for addition.", exception.message) + } + + @Test + fun testUnitConversion() { + val kilometer = PhysicalUnit("kilometer", "km", 1000.0, Units.meter.dimension) + val centimeter = PhysicalUnit("centimeter", "cm", 0.01, Units.meter.dimension) + + // Convert 1 km to meters + val kmInMeters = 1.0 * kilometer.conversionFactorToSI + assertEquals(1000.0, kmInMeters) + + // Convert 100 cm to meters + val cmInMeters = 100.0 * centimeter.conversionFactorToSI + assertEquals(1.0, cmInMeters) + } +} diff --git a/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/VariablesTest.kt b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/VariablesTest.kt new file mode 100644 index 0000000..16a42a6 --- /dev/null +++ b/controls-constructor/src/commonTest/kotlin/space/kscience/controls/constructor/VariablesTest.kt @@ -0,0 +1,41 @@ +package space.kscience.controls.constructor + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertNull +import space.kscience.controls.constructor.dsl.core.units.Units +import space.kscience.controls.constructor.dsl.core.variables.ParameterVariable +import space.kscience.controls.constructor.dsl.core.variables.Variable + +class VariablesTest { + + @Test + fun testCreationOfVariablesAndParameters() { + val length = Variable("length", Units.meter) + assertEquals("length", length.name) + assertEquals(Units.meter, length.unit) + assertNull(length.value) + + val mass = ParameterVariable("mass", Units.kilogram, value = 10.0) + assertEquals("mass", mass.name) + assertEquals(Units.kilogram, mass.unit) + assertEquals(10.0, mass.value) + } + + @Test + fun testSettingAndGettingValuesOfVariablesAndParameters() { + val speed = Variable("speed", Units.meter / Units.second) + speed.value = 15.0 + assertEquals(15.0, speed.value) + + val density = ParameterVariable("density", Units.kilogram / Units.meter.pow(3.0), value = 1000.0) + assertEquals(1000.0, density.value) + + // Attempt to create a parameter with value outside the allowed range + val exception = assertFailsWith { + ParameterVariable("temperature", Units.kelvin, value = -10.0, min = 0.0) + } + assertEquals("Value of parameter temperature is less than minimum allowed value.", exception.message) + } +} -- 2.34.1