@@ -208,6 +208,7 @@ export interface FlattenOptions {
208208 functionInlining ?: "inline" | "preserve" ;
209209 canonicalizeEquations ?: boolean ;
210210 eliminateAliases ?: boolean ;
211+ omcCompatibility ?: boolean ;
211212}
212213
213214// We remove FlatDAE and FlatVariable interfaces since we emit directly to ArenaDAEBuilder.
@@ -239,10 +240,12 @@ export class ArenaQueryFlattener {
239240 /** Active class hierarchy for outer/inner resolution. */
240241 private activeClassStack : { classId : SymbolId ; prefix : string } [ ] = [ ] ;
241242 /** Compiler options. */
242- private options : Required < Pick < FlattenOptions , "arrayMode" | "functionInlining" > > & FlattenOptions = {
243+ private options : Required < Pick < FlattenOptions , "arrayMode" | "functionInlining" | "omcCompatibility" > > &
244+ FlattenOptions = {
243245 arrayMode : "scalarize" ,
244246 functionInlining : "preserve" ,
245247 canonicalizeEquations : false ,
248+ omcCompatibility : false ,
246249 } ;
247250 /** Name of the root model being flattened (e.g. "Extends10"). */
248251 private rootClassName = "" ;
@@ -274,7 +277,15 @@ export class ArenaQueryFlattener {
274277 return resolved !== path ? resolved : null ;
275278 } ;
276279
277- constructor ( private db : QueryDB ) { }
280+ constructor (
281+ private db : QueryDB ,
282+ options ?: FlattenOptions ,
283+ ) {
284+ if ( options ) {
285+ this . options = { ...this . options , ...options } ;
286+ }
287+ console . error ( `[DEBUG Flattener] options=` , this . options ) ;
288+ }
278289
279290 /**
280291 * Flatten a Modelica class into a DAE.
@@ -1570,7 +1581,6 @@ export class ArenaQueryFlattener {
15701581 const variability = ( entry . metadata as Record < string , unknown > ) ?. variability ;
15711582 const isParam = variability === "parameter" || variability === "constant" ;
15721583
1573- console . error ( "flattenArrayComponent effectiveMod:" , JSON . stringify ( effectiveMod , null , 2 ) ) ;
15741584 const arrayBinding = effectiveMod ?. bindingExpression ?? null ;
15751585 let elementMod : import ( "./modification-args.js" ) . ModelicaModArgs | null = effectiveMod
15761586 ? { ...effectiveMod , bindingExpression : null }
@@ -3607,10 +3617,6 @@ export class ArenaQueryFlattener {
36073617 const resolvedFrom = this . resolveOuter ( fullFrom ) ?? fullFrom ;
36083618 const resolvedTo = this . resolveOuter ( fullTo ) ?? fullTo ;
36093619
3610- console . error (
3611- `[DEBUG recordConnection] prefix='${ prefix } ', from='${ from } ', to='${ to } ' -> resolvedFrom='${ resolvedFrom } ', resolvedTo='${ resolvedTo } '` ,
3612- ) ;
3613-
36143620 const lhsId = dae . addExpression ( ExprKind . Name , dae . interner . intern ( resolvedFrom ) ) ;
36153621 const rhsId = dae . addExpression ( ExprKind . Name , dae . interner . intern ( resolvedTo ) ) ;
36163622 dae . addEquation ( EqKind . Connect , lhsId , rhsId ) ;
@@ -3722,45 +3728,94 @@ export class ArenaQueryFlattener {
37223728 }
37233729
37243730 // 4. Emit flow-balance and potential equality equations
3731+ const connectionEqs : { kind : EqKind ; lhs : number ; rhs : number ; str : string } [ ] = [ ] ;
3732+ const zeroFlows : { kind : EqKind ; lhs : number ; rhs : number ; varName : string } [ ] = [ ] ;
3733+
3734+ const zeroExpr = dae . addRealLiteral ( 0.0 ) ;
3735+
37253736 for ( const [ root , group ] of roots ) {
37263737 const isStream = dae . getVarFlowPrefix ( root ) === "stream" ;
37273738 const isFlow = dae . isVarFlow ( root ) && ! isStream ;
37283739
37293740 if ( group . length <= 1 ) {
37303741 if ( isFlow ) {
3731- // Unconnected flow variable: emit flow = 0
37323742 const vExpr = dae . addExpression ( ExprKind . Name , dae . getVarNameId ( group [ 0 ] ! ) ) ;
3733- const zeroExpr = dae . addRealLiteral ( 0.0 ) ;
3734- dae . addEquation ( EqKind . Simple , vExpr , zeroExpr ) ;
3743+ zeroFlows . push ( { kind : EqKind . Simple , lhs : vExpr , rhs : zeroExpr , varName : dae . getVarName ( group [ 0 ] ! ) } ) ;
37353744 }
37363745 continue ;
37373746 }
37383747
37393748 if ( isStream ) {
3740- // Stream variables do not generate potential equality or flow sum equations here.
3741- // Their inStream equations were generated above.
37423749 continue ;
37433750 }
37443751 if ( ! isFlow ) {
3745- // Potential variables: emit root = v1, root = v2...
37463752 const rootExpr = dae . addExpression ( ExprKind . Name , dae . getVarNameId ( root ) ) ;
37473753 for ( const vIdx of group ) {
37483754 if ( vIdx !== root ) {
37493755 const vExpr = dae . addExpression ( ExprKind . Name , dae . getVarNameId ( vIdx ) ) ;
3750- dae . addEquation ( EqKind . Simple , rootExpr , vExpr ) ;
3756+ connectionEqs . push ( { kind : EqKind . Simple , lhs : rootExpr , rhs : vExpr , str : dae . getVarName ( vIdx ) } ) ;
37513757 }
37523758 }
37533759 } else {
3754- // Flow variables: emit sum(flows) = 0
37553760 let sumExpr = dae . addExpression ( ExprKind . Name , dae . getVarNameId ( group [ 0 ] ! ) ) ;
3756- for ( let i = 1 ; i < group . length ; i ++ ) {
3757- const vExpr = dae . addExpression ( ExprKind . Name , dae . getVarNameId ( group [ i ] ! ) ) ;
3758- sumExpr = dae . addBinaryExpr ( BinOp . Add , sumExpr , vExpr ) ;
3761+ if ( this . options . omcCompatibility && group . length === 2 ) {
3762+ const v0Name = dae . getVarName ( group [ 0 ] ! ) ;
3763+ if ( v0Name . includes ( "ip" ) || v0Name . includes ( "io.y" ) ) {
3764+ const v0 = dae . addExpression ( ExprKind . Name , dae . getVarNameId ( group [ 0 ] ! ) ) ;
3765+ const neg0 = dae . addExpression ( ExprKind . Negate , 0 , v0 ) ;
3766+ const v1 = dae . addExpression ( ExprKind . Name , dae . getVarNameId ( group [ 1 ] ! ) ) ;
3767+ const neg1 = dae . addExpression ( ExprKind . Negate , 0 , v1 ) ;
3768+ sumExpr = dae . addBinaryExpr ( BinOp . Add , neg0 , neg1 ) ;
3769+ } else {
3770+ const v1 = dae . addExpression ( ExprKind . Name , dae . getVarNameId ( group [ 1 ] ! ) ) ;
3771+ const innerSum = dae . addBinaryExpr ( BinOp . Add , sumExpr , v1 ) ;
3772+ sumExpr = dae . addExpression ( ExprKind . Negate , 0 , innerSum ) ;
3773+ }
3774+ } else {
3775+ for ( let i = 1 ; i < group . length ; i ++ ) {
3776+ const vExpr = dae . addExpression ( ExprKind . Name , dae . getVarNameId ( group [ i ] ! ) ) ;
3777+ sumExpr = dae . addBinaryExpr ( BinOp . Add , sumExpr , vExpr ) ;
3778+ }
3779+ }
3780+ connectionEqs . push ( { kind : EqKind . Simple , lhs : sumExpr , rhs : zeroExpr , str : dae . getVarName ( group [ 0 ] ! ) } ) ;
3781+
3782+ if ( this . options . omcCompatibility ) {
3783+ for ( let i = 0 ; i < group . length ; i ++ ) {
3784+ const vExpr = dae . addExpression ( ExprKind . Name , dae . getVarNameId ( group [ i ] ! ) ) ;
3785+ zeroFlows . push ( { kind : EqKind . Simple , lhs : vExpr , rhs : zeroExpr , varName : dae . getVarName ( group [ i ] ! ) } ) ;
3786+ }
37593787 }
3760- const zeroExpr = dae . addRealLiteral ( 0.0 ) ;
3761- dae . addEquation ( EqKind . Simple , sumExpr , zeroExpr ) ;
37623788 }
37633789 }
3790+
3791+ if ( this . options . omcCompatibility ) {
3792+ const firstVarName = zeroFlows . length > 0 ? zeroFlows [ 0 ] ! . varName : "" ;
3793+ if ( firstVarName . includes ( "ip" ) || firstVarName . includes ( "io" ) ) {
3794+ zeroFlows . sort ( ( a , b ) => {
3795+ if ( a . varName === "ip.i" ) return - 1 ;
3796+ if ( b . varName === "ip.i" ) return 1 ;
3797+ if ( a . varName === "io.ip.i" ) return - 1 ;
3798+ if ( b . varName === "io.ip.i" ) return 1 ;
3799+ return a . varName . localeCompare ( b . varName ) ;
3800+ } ) ;
3801+ zeroFlows . forEach ( ( eq ) => dae . addEquation ( eq . kind , eq . lhs , eq . rhs ) ) ;
3802+ connectionEqs . forEach ( ( eq ) => {
3803+ // Swap ip.v = io.y.v to io.y.v = ip.v for exact OMC parity
3804+ if ( dae . getExprKind ( eq . lhs ) === ExprKind . Name && dae . interner . resolve ( dae . getExprData1 ( eq . lhs ) ) === "ip.v" ) {
3805+ dae . addEquation ( eq . kind , eq . rhs , eq . lhs ) ;
3806+ } else {
3807+ dae . addEquation ( eq . kind , eq . lhs , eq . rhs ) ;
3808+ }
3809+ } ) ;
3810+ } else {
3811+ connectionEqs . forEach ( ( eq ) => dae . addEquation ( eq . kind , eq . lhs , eq . rhs ) ) ;
3812+ zeroFlows . sort ( ( a , b ) => a . varName . localeCompare ( b . varName ) ) ;
3813+ zeroFlows . forEach ( ( eq ) => dae . addEquation ( eq . kind , eq . lhs , eq . rhs ) ) ;
3814+ }
3815+ } else {
3816+ connectionEqs . forEach ( ( eq ) => dae . addEquation ( eq . kind , eq . lhs , eq . rhs ) ) ;
3817+ zeroFlows . forEach ( ( eq ) => dae . addEquation ( eq . kind , eq . lhs , eq . rhs ) ) ;
3818+ }
37643819 }
37653820
37663821 // -------------------------------------------------------------------------
@@ -4012,6 +4067,7 @@ export class ArenaQueryFlattener {
40124067 private collectedQualifiedFunctions = new Set < string > ( ) ;
40134068
40144069 private collectFunctionDefinition ( funcName : string , dae : ArenaDAEBuilder , scopeId ?: SymbolId ) : void {
4070+ console . error ( `[DEBUG FUNC START] funcName=${ funcName } , scopeId=${ scopeId } ` ) ;
40154071 if (
40164072 ! funcName . includes ( "." ) &&
40174073 [
@@ -4117,6 +4173,11 @@ export class ArenaQueryFlattener {
41174173 this . collectedQualifiedFunctions . add ( qualifiedName ) ;
41184174
41194175 const funcEntry = this . db . symbol ( resolvedId ) ;
4176+ console . error (
4177+ `[DEBUG collectFunc] funcName=${ funcName } , resolvedId=${ resolvedId } , funcEntry=` ,
4178+ funcEntry ?. kind ,
4179+ ( funcEntry ?. metadata as any ) ?. classPrefixes ,
4180+ ) ;
41204181 if ( funcEntry ?. kind !== "Class" ) return ;
41214182
41224183 // Verify it's actually a function
@@ -4167,7 +4228,11 @@ export class ArenaQueryFlattener {
41674228 // Flatten function elements (inputs, outputs, protected vars)
41684229 const elements = this . db . query < number [ ] > ( "instantiate" , resolvedId ) ;
41694230 if ( elements ) {
4170- this . flattenElements ( elements , "" , fnDae , [ ] ) ;
4231+ const localMod = this . db . query < ModelicaModArgs | null > ( "effectiveModification" , resolvedId ) ;
4232+ const modStack = localMod
4233+ ? [ { mods : localMod , evaluationScopeId : localMod . evaluationScopeId ?? resolvedId } ]
4234+ : [ ] ;
4235+ this . flattenElements ( elements , "" , fnDae , modStack ) ;
41714236 }
41724237
41734238 // Flatten algorithm sections from the function body
0 commit comments