// NetLink.java
//
//
// Copyright Björkman Elteknik och Data AB 2000
//
//
// History:
// 0.0 Created by Henrik Björkman 2001-01-07
//     (using code from fluxa.java)
// 0.3 Added NetLinLink. Henrik 2001-01-08
// 0.8 Many changes. Henrik & KE 2001-01-16
// 0.9 More changes. Henrik & KE 2001-01-17
// 0.10 Corrected eternal loop. 2001-01-18
// 0.11 More changes. Henrik & KE 2001-01-19
// 0.12 Node given by name instead of number. 2001-02-08
// 0.13 Corrected some errors in interconnections. 2001-02-13

import java.io.*;
import se.beod.henrik.filter.*;
//import java.lang.math;
import netlink_package.*;
import flux_package.*;

// nodes in each branch arranged in rising order number from the node 0 source


public class NetLink
{
  static final int maxNodes=1000;

  final static double PI=3.14159;
  static double FHz=0; // Frequency Hz
  static double startUkV=0;//kV  startUkV should be actual voltage at source och ref for relative voltage link data may be for a nominal voltage
  static double dp0=0;
  static double Q30=0;

  static int nTypes=0;
  static NetLinkType linkType[]=new NetLinkType[100];
  
  static int nNodes=0;
  static NetLinkNode node[]=new NetLinkNode[maxNodes];
  
  static int nLinks=0;
  static NetLinkLink link[]=new NetLinkLink[100];
  
  static double U[]=new double[maxNodes]; 
  static double V[]=new double[maxNodes]; 
  static double IA[]=new double[maxNodes];
  static double Imax[]=new double[maxNodes];  
  static double U2[]=new double[maxNodes];
  static double V2[]=new double[maxNodes];
  
  static double P1[]=new double[maxNodes]; //accumulated load
  static double Q1[]=new double[maxNodes];
  static double P2[]=new double[maxNodes]; //losses
  static double Q2[]=new double[maxNodes];
  static double P3[]=new double[maxNodes]; //not in use but could be voltage dependent losses like insulation losses
  static double Q3[]=new double[maxNodes]; //capacitive generation
  static double dP[]=new double[maxNodes];   //load added to even out voltage and phase difference between linked nodes
  static double dQ[]=new double[maxNodes];   
  //static double dUdP[]=new double[maxNodes];
  //static double dVdP[]=new double[maxNodes];
  //static double dUdQ[]=new double[maxNodes];
  //static double dVdQ[]=new double[maxNodes];
  static double Uold[]=new double[maxNodes];
  static double Vold[]=new double[maxNodes];
  
  static void error(String str)
  {
    System.err.println("NetLink: " + str);
    System.exit(0);
  }
  
  static void debug(String str)
  {
    System.err.println("NetLink: " + str);
  }
  
  static void print(String str)
  {
    System.out.print(str);
  }
  
  static void println(String str)
  {
    System.out.println(str);
  }
  
  static String readln()
  {
    String str=null;
    
    try 
    {
      BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
      str=in.readLine();
    }
    catch (IOException e) {error(e.toString());}
    
    return str;
  }
    
  
  static double sqr(double a)
  {
    return(a*a);
  }
  
  static double ATN(double a)
  {
    return Math.atan(a);
  }
  
  static String fi(int i, int nChar)
  {
    String str=""+i;
    
    while (str.length()<nChar)
    {
      str=" "+str;
    }
    
    return str;
  }

  static String fw(String inStr, int nChar)
  {
    String str=inStr;

    while (str.length()<nChar)
    {
      str=str+" ";
    }
    
    return str;
  }

  
  static String fd(double a, int nChar, int nDec)
  {
    String str;
    long d;    
    boolean neg=false;
    int f=1;

    if (a>0xFFFFFFFF)
    {
      // to large.
      return fw(""+a,nChar);      
    }
    
    for(int i=0;i<nDec;i++)
    {
      f*=10;
    }
    
    if (a<0)
    {
      a=-a;
      neg=true;
    }
    
    a=a*f;
    d=Math.round(a);
    str=""+d%f;                 
    while (str.length()<nDec) 
    {
      str="0"+str;
    }
    
    str=""+d/f+"."+str;
    
    if (neg)
    {
      str="-"+str;
    }
    
    while (str.length()<nChar)
    {
      str=" "+str;
    }
    return str;
  }
  
  static NetLinkType findLinkType(String name)
  {
    int i=0;
    while (linkType[i]!=null)
    {
      if (linkType[i].typeName.equals(name)) return(linkType[i]);
      i++;
    }
    error("link type "+name+" not found");
    return null;
  }


  static int findNodeNr(String name)
  {
    int i=0;
    while (node[i]!=null)
    {
      if (node[i].nodeId.equals(name)) return(i);
      i++;
    }
    error("node "+name+" not found");
    return -1;
  }

  static NetLinkNode findNode(String name)
  {
    int i=0;
    while (node[i]!=null)
    {
      if (node[i].nodeId.equals(name)) return(node[i]);
      i++;
    }

    if (!name.equals("-"))
    {
      error("node "+name+" not found");
    }

    return null;
  }


  
  /** Read data from file. */
  static void readData(String file_name)
  {
    PrecompilerReader in=null;
    
    try
    {
      in=new PrecompilerReader(new FileReader(file_name));
    }
    catch (IOException e) {error(""+e);}
    
    try
    {
      for(;;)
      {
        String str=in.readWord();        
        if (str==null) break;        
        if (str.length()==0) {;}
        else if (str.equals("FHz"))
        {
          FHz=in.readDouble();
        }
        
        else if (str.equals("startUkV"))
        {
        startUkV=in.readDouble();
        }
    
        else if (str.equals("dp0"))
        {
          dp0=in.readDouble();dP[0]=0;
        }

        else if (str.equals("nodes"))
        {
          in.readAndSkipWord("{");
          for(;;)
          {          
            node[nNodes]=NetLinkNode.readData(in);
            if (node[nNodes]==null) break;
            nNodes++;
            str=in.readWord();
            if (!str.equals(",")) break;
          }
        }  
        else if (str.equals("types"))
        {
          in.readAndSkipWord("{");
          for(;;)
          {          
            linkType[nTypes]=NetLinkType.readData(in);
            if (linkType[nTypes]==null) break;
            nTypes++;
            str=in.readWord();
            if (!str.equals(",")) break;
          }
        }
        else if (str.equals("links"))
        {
          in.readAndSkipWord("{");
          for(;;)
          {          
            link[nLinks]=NetLinkLink.readData(in);
            if (link[nLinks]==null) break;
            nLinks++;
            str=in.readWord();
            if (!str.equals(",")) break;
          }
        }
        else
        {
          error(in.getInfo()+" unknown data: \""+str+"\"");
        }
      }
      in.close();
    }
    //catch (IOException e) {error(in.getInfo()+" "+e); }
    catch (NumberFormatException e) {error(in.getInfo()+" "+e);}
  }
  
  
  // 100 rem summing load capacitive generation and line losses from leaves to root
  static void calculate()
  { 
    debug("Calculating");
    
    int i; // node 0 is supplied from node - which does not exist  0 was earlier used for total sum  
    int j; // inner counter
    //int k;//linkType number 
    int iter=0;
    //int cTYP;
        
    double startV=0; // voltage angle at source 
    double U5=startUkV;//U5 is used to find and to compare lowest voltage with last value
    double U6; //voltage difference from previous voltage
    double U7; //ABS(U7)
    double U8=0.0001; //starting value for approval of low enough change 
             
    
    for (j=0;j<nNodes;j++) 
    { 
      U[j]=startUkV; // start value in every node
    }
    
    while (U8>1E-8) 
    {
        
      if (iter>100)
      {
        print("iteration limit passed="+iter);break;
      }
      debug("iteration "+iter);  
          
      
      for (i=0;i<nNodes;i++) 
      {
        P1[i]=0;
        Q1[i]=0;
        P2[i]=0;
        Q2[i]=0;
        Q3[i]=0;
      }
      
      // for every node sum load and losses starting at the leaf ends which do not feed other nodes
      
      for (i=nNodes-1;i>=0;i--)
      {
        for (j=nNodes-1;j>=0;j--) 
        {
          if (node[j].fedFrom.equals(node[i].nodeId))
          {
            NetLinkType feedingType=findLinkType(node[j].linkType);// search nodes j that have i as feeding node to sum up load and losses
            if (feedingType==null) {error("No type "+node[j].linkType);}
            P1[i]=P1[i]+P1[j]+P2[j];
            Q1[i]=Q1[i]+Q1[j]+Q2[j]-node[j].kmN*PI*FHz*feedingType.c*0.000001*U[j]*U[j];//half line capacitive generation  
            Q3[j]=node[j].kmN*PI*FHz*feedingType.c*0.000001*U[i]*U[i];Q3[i]=Q3[i]+Q3[j];//half line capacitive generation    node[j]from is the only feeder to j
            Imax[j]=feedingType.imax;
          }
        }
        
        //add own load at node i        
        P1[i]=P1[i]+node[i].mw;
        Q1[i]=Q1[i]+node[i].mvar-Q3[i];
        
        // Calculate losses up to the node´s feeding node where they will be added 
        NetLinkType feedingConductorType=findLinkType(node[i].linkType);// search nodes j that have i as feeding node to sum up load and losses
        V2[i]=ATN(node[i].kmN*(feedingConductorType.x*FHz/50*P1[i]-feedingConductorType.r*Q1[i])/U[i]/U[i]);//voltage angle change in the uniq feeder
        U2[i]=U[i]-(U[i]-node[i].kmN*(feedingConductorType.r*P1[i]+feedingConductorType.x*FHz/50*Q1[i])/U[i])/Math.cos(V2[i]);
        P2[i]=node[i].kmN*feedingConductorType.r*(P1[i]*P1[i]+Q1[i]*Q1[i])/U[i]/U[i];
        Q2[i]=node[i].kmN*feedingConductorType.x*FHz/50*(P1[i]*P1[i]+Q1[i]*Q1[i])/U[i]/U[i];
      }
      
      debug("");
      U[0]=startUkV;V[0]=startV;//calculate actual voltage from root to leaf  Link impedances are at nominal voltage
      for (i=0;i<nNodes;i++)
      {
        for (j=0;j<nNodes;j++)
        {
          if (node[i].nodeId.equals(node[j].fedFrom))
          { 
            U[j]=U[i]-U2[j];
            V[j]=V[i]-V2[j];// add"DC  DC side Vdiff=0 Q=Q2=0 x=0 Rdcfas=rdctot*Uac*Uac/Udc/Udc   
            IA[j]=Math.sqrt((P1[j]*P1[j]+Q1[j]*Q1[j])/3)/U[j]*1000;
            
          }
        }
      }
      
      
        
      U6=U[nNodes-1]-U5;
      U7=Math.abs(U6);
      U8=U7/startUkV;
      
      
      if ((dp0)>0)    // to interconnect indata selected nodes        
      {               
        if (iter<1)  // indata dp0 used      
        {
          for (i=0;i<nLinks;i++)
          {
            dP[i]=dp0;dQ[i]=dp0;
          }
        }
        
        if (iter%4==0)  // save old angle  add dP which influences dV most
        {
          for (i=0;i<nLinks;i++)
          {
            int to=findNodeNr(link[i].to);
            int from=findNodeNr(link[i].from);
            Vold[to]=V[to];
            Vold[from]=V[from]; 
            node[to].mw=node[to].mw+dP[i];       // add dP to calculate dV/dP and dP[i] later     
            node[from].mw=node[from].mw-dP[i];       
          }
        }        

        if (iter%4==1)  //calculate dV/dP and add dQ which influences dU most
        {
          for (i=0;i<nLinks;i++)
          {
            int to=findNodeNr(link[i].to);
            int from=findNodeNr(link[i].from);
            dP[i]=-(V[from]-V[to])*dP[i]/(V[from]-V[to]-(Vold[from]-Vold[to]));
            Uold[to]=U[to];
            Uold[from]=U[from];
            node[to].mvar=node[to].mvar+dQ[i];    // add dQ to calculate dU/dP and dQ[i] later
            node[from].mvar=node[from].mvar-dQ[i];
          }
        }
     
        if (iter%4==2)  // old voltage saved
        {    
          for (i=0;i<nLinks;i++)
          {
            int to=findNodeNr(link[i].to);
            int from=findNodeNr(link[i].from);
            dQ[i]=-(U[from]-U[to])*dQ[i]/(U[from]-U[to]-(Uold[from]-Uold[to])); // provade andra tecken
          }
        }

        if (iter%4==3) //  dP and dQ  this iteration may be  omitted
        {
          for (i=0;i<nLinks;i++)
          {
            int to=findNodeNr(link[i].to);
            int from=findNodeNr(link[i].from); 
          }
        }
      }    
      
                          //if (U8>1E-8)                      
      { 
        U5=U[nNodes-1];       
      }     

      iter++;
    }      
  }  
  /** Read calibration data from file. */
  static void writeData()                
  {
    int i;
    int j;  
    debug("Writing results");    
    println("dp0 "+dp0);
    println("startUkV "+startUkV);    
    println("links{");
    for (i=0;i<nLinks;i++)
    {
      if (i!=0) 
      {
        print(",");
      }
      println(" "+link[i]);
    }

    println("}");  
    println("types{"); 
    for (i=0;i<nTypes;i++)
    {
      if (i!=0) 
      {  
        print(",");
      }
      println(" "+linkType[i]);
    }

    println("}");        
    println("nodes{");
    for (i=0;i<nNodes;i++)
    {
    if (i!=0)  
      { 
        print(",");
      }

      println(" "+node[i]);
    }

    println("}");
    println("result");
    println(" "+"Station              Load                 Voltage");
    println(" "+"                    MW        Mvar       kV        deg");    
    for (i=0;i<nNodes;i++)
    {
      println(" "+fw(node[i].nodeId,16)+" "+fd(node[i].mw,8,3)+" "+fd(node[i].mvar,8,3)+" "+fd(U[i],8,3)+" "+fd(V[i]*180/PI,8,3)+"");//*rad 296
    } 

    println("    "+"Station      Feeder         Link               Losses          Current");
    println(" "+"                from       type        kmN      kW       kVar    A     %of max"); 
    for (i=0;i<nNodes;i++)
    {
      NetLinkType feedingType=findLinkType(node[i].linkType);
      println("  "+fw(node[i].nodeId,12)+" "+fw(node[i].fedFrom,10)+" "+fw(node[i].linkType,13)+" "+fd(node[i].kmN,5,1)+" "+fd(P2[i]*1000,8,3)+" "+fd(Q2[i]*1000,8,3)+" "+fd(IA[i],8,3)+fd(IA[i]/Imax[i]*100,8,3));
      Q30=Q30+Q3[i];
    }
    println("");
    println("   Total line Capacitive generation "+fd(Q30*1000,6,0)+" kvar");

     println("dp0 "+dp0+"dP[i] "+dP[0]); //to check that this part is ok
  }
    
  
  public static void main(String args[]) 
  {
    if (args.length < 1)
    {
      println("Usage: java NetLink <in file>");
      println("test < "+fd(665.5656,6,2)+" "+fd(215,6,2)+" "+fd(-54.4,6,2)+" >");//prova 0.05 och -0.05 med 3decimaler
      println("test < "+fd(5.5656,6,2)+" "+fd(64,6,2)+" "+fd(-5.9,6,2)+" >");
      println("test < "+fd(5.0656,6,2)+" "+fd(-64.1,6,2)+" "+fd(-5.09,6,2)+" >");
      println("test < "+fi(5,6)+" "+fi(64,6)+" "+fi(5,6)+" >");
      
      System.exit(0);
    }
    
    readData(args[0]);
   
    calculate();
    
    writeData();
    
  }
  
  
}


