using System.Numerics;
using Skeleton.Algebra.Extensions;
using Skeleton.Algebra.ScalarFieldStructure;
using Skeleton.DataStructure;
using Skeleton.DataStructure.Link;
using Skeleton.DataStructure.Packs;
using Skeleton.Utils;
using Skeleton.Utils.Helpers;
using Skeleton.Utils.InverseSampling;
using VirtualChemistry.Chemistry.Bonds.Implements;
using VirtualChemistry.Chemistry.Compounds.Implements;
using VirtualChemistry.Chemistry.Mixtures.Resolver;
using VirtualChemistry.Constants;
using VirtualChemistry.DataStructure.Packs;
namespace VirtualChemistry.Chemistry.Mixtures.Implements;
///
/// mixture of many well mixed compounds that can not be seperated by psy methods
///
public class HomogeneousMixture
{
///
/// Constructor by default
///
public HomogeneousMixture()
{
LogStateMatrix = new CacheItem(x =>
Compounds.Select(compound => compound.LogStateMatrix.GetFrom(x) * compound.Concentration)
.DefaultIfEmpty(LieSU3.Zero)
.SpLieSum()
);
StateMatrix = new(x => LogStateMatrix.GetFrom(x).Exp());
CompressionElasticity = new(x =>
{
DiagR3 spectrum = new (0d, 2d * Math.PI, 4d * Math.PI);
return (LogStateMatrix.GetFrom(x).CayleyNegative() * StateMatrix.GetFrom(x))
.ConjugateOn(spectrum)
.Rayleigh(LogStateMatrix.GetFrom(x).ColumnAverage);
}
);
CompressionBias = new(x =>
{
DiagR3 w1 = new (1d / 11d, 7d / 11d, 10d / 11d);
DiagR3 w2 = new (2d / 17d, 8d / 17d, 16d / 17d);
double p1 = StateMatrix.GetFrom(x)
.ConjugateOn(w1)
.Rayleigh(StateMatrix.GetFrom(x).Hermitian().ColumnAverage);
double p2 = StateMatrix.GetFrom(x)
.Hermitian()
.ConjugateOn(w2)
.Rayleigh(StateMatrix.GetFrom(x).ColumnAverageBivariant);
double x1 = InverseSampling.StandardNormal(p1) / 4d - 2d * Math.PI;
double x2 = InverseSampling.StandardNormal(p2) / 3d + 2d * Math.PI;
return Math.Atan(Math.Abs(x1) > Math.Abs(x2) ? x1 : x2) * 2d;
}
);
FreeDensity = new(x =>
{
double avg = (Math.Exp(-4d * Math.PI) + Math.Exp(Math.PI)) / 2d;
DiagR3 spectrum = new DiagR3(Math.Exp(-4d * Math.PI), avg, Math.Exp(Math.PI));
double tempMod = Math.Exp(-Phase);
return tempMod * LogStateMatrix.GetFrom(x)
.CayleyPositive()
.ConjugateOn(spectrum)
.Rayleigh(StateMatrix.GetFrom(x).ColumnAverageBivariant);
}
);
HeatConductivity = new(x =>
StateMatrix.GetFrom(x).ConjugateOn(ChemistryConstant.EnergyConductivitySpectrum)
.Rayleigh(LogStateMatrix.GetFrom(x) * StateMatrix.GetFrom(x).ColumnAverageBivariant)
);
ResetAllCache();
}
///
/// singleton used to represent no mixture
///
public static readonly HomogeneousMixture Null = new();
private IEnumerable ConnectedIndex()
{
HashSet bs = new();
foreach (Compound c in Compounds)
{
if(c.Amount.IsEqualApprox(0))
continue;
foreach (BaseBond b in c.Bonds)
{
if (
b.Connected &&
b.Energy > b.AntiBondingEnergy &&
b.ConnectedBond.Energy > b.ConnectedBond.AntiBondingEnergy &&
!bs.Contains(b.ConnectedBond)
)
{
bs.Add(b);
}
}
}
return bs;
//Compounds.Where(c => !(0.5 * c.Amount).IsEqualApprox(0)).SelectMany(x => x.ConnectedIndex());
}
public void RPC()
{
int s1 = 0;
int s2 = 0;
int s3 = 0;
int s4 = 0;
foreach (Compound c in Compounds)
{
foreach (BaseBond b in c.Bonds)
{
switch (b.BondingGroup)
{
case 0:
s1++;
break;
case 1:
s2++;
break;
case 2:
s3++;
break;
default:
s4++;
break;
}
}
}
Console.WriteLine($"{s1} ---- {s2} ---- {s3} --- {s4}");
}
private BondGroups ReactionGroup()
{
HashSet group1 = new();
HashSet group2 = new();
HashSet group3 = new();
HashSet group4 = new();
foreach (Compound c in Compounds)
{
if ((c.Amount * 0.125).IsEqualApprox(0) || c.Atoms.Count > 15)
continue;
foreach(BaseBond b in c.Bonds)
{
if(b.Connected || !(b.Energy > b.BondingEnergy))
continue;
switch (b.BondingGroup)
{
case 0:
group1.Add(b);
break;
case 1:
group2.Add(b);
break;
case 2:
group3.Add(b);
break;
case 3:
group4.Add(b);
break;
}
}
}
return new(group1, group2, group3, group4);
}
///
/// elasticity range: 0, 4pi
/// elasticity controls the speed k drops from e^pi to e^-4pi
/// elasticity -> 0; k -> constant
/// b -> 4pi; k -> e^pi
/// b -> -4pi; k -> e^-4pi
///
public CacheItem CompressionElasticity { get; }
///
/// bias range: -4pi, 4pi
/// bias controls the temp of triple point
/// higher b -> higher melting point and higher boiling point
///
public CacheItem CompressionBias { get; }
///
/// phase -> 1 implies material is more like gas
/// phase -> -1 implies material is more like solid
///
///
public double Phase => Compounds.Sum(c => c.Concentration * c.Phase);
///
/// stiffness range: e^-4pi, e^pi
/// higher stiffness -> harder to compress -> solid like
/// lower stiffness -> easier to compress -> gas like
/// Stiffness K, Density D, Free Density D0, Temperature T
/// P = K(1/D - 1/D0)
///
/// Temperature range: -pi, pi
///
public double CompressionStiffness => Math.Exp(Math.PI / 2d * (5d * Phase - 3d));
///
/// e^-4pi ... e^pi
///
public CacheItem FreeDensity { get; }
///
/// volume that under 0 compression
///
public double FreeVolume => Amount / FreeDensity.Get;
///
/// P = K (1/D - 1/D0)
///
public double Pressure => (Volume - FreeVolume) * CompressionStiffness;
///
/// Volume of the mixture
///
public double Volume { get; set; }
///
/// Heat transfer ability
///
public CacheItem HeatConductivity { get; }
///
/// energy / amount = temperature
///
public double Temperature
{
get => Energy / Amount;
set => Energy = value * Amount;
}
///
/// combust rate
///
public double CombustRate
{
get
{
H3 w = StateMatrix.Get!.ConjugateOn(new DiagR3(0, Math.PI, 2 * Math.PI));
return w.Rayleigh(
LogStateMatrix.Get!.ColumnAverageBivariant +
StateMatrix.Get.Hermitian().ColumnAverageBivariant
);
}
}
///
/// total Energy the mixture holds
///
public double Energy
{
get => Compounds.Sum(c => c.Energy);
set
{
foreach (Compound c in Compounds)
c.Energy = value * c.Concentration;
}
}
///
/// reset all cache
///
public void ResetAllCache()
{
LayerCache = LinkNode.Null;
}
///
/// the heterogeneous mixture that holds this homogeneous mixture
///
public HeterogeneousMixture HeterogeneousMixture { get; set; } = HeterogeneousMixture.Null;
///
/// Compounds of this mixture
///
public HashSet Compounds { get; set; } = new HashSet();
///
/// Compounds whose ratio is greater than 1%
///
public IEnumerable MainCompounds => Compounds.Where(c => c.Concentration > 0.001d);
private LinkNode LayerCache { get; set; } = LinkNode.Null;
///
///
///
public LinkNode Layer
{
get => LayerCache == LinkNode.Null
? HeterogeneousMixture.LayerOrder.Find(this)
: LayerCache;
set => LayerCache = value;
}
///
/// State matrix of this mixture
///
public CacheItem StateMatrix { get; }
///
/// Log of the state matrix
///
public CacheItem LogStateMatrix { get; }
///
/// Split the homogeneous mixture into another heterogeneous mixture by ratio or amount;
///
/// The amount or ratio
/// another heterogeneous mixture
/// determine if the amount variable is ratio or amount
///
public HomogeneousMixture Split(double amount, HeterogeneousMixture into, bool byRatio = false)
{
double ebs = Energy;
double vbs = Volume;
double ratio = byRatio ? amount : amount / Amount;
HomogeneousMixture res = new HomogeneousMixture();
foreach (Compound c in Compounds)
{
Compound split = c.Split(amount, byRatio);
if(split != Compound.Null)
res.AddCompound(split);
}
res.Energy = ebs*ratio;
res.Volume = vbs*ratio;
Energy = ebs * (1 - ratio);
Volume = vbs * (1 - ratio);
into.AddLayer(res);
return res;
}
///
/// Remove a compound from the homogeneous mixture
///
///
public void RemoveCompound(Compound compound)
{
double deltaV = compound.Volume;
Compounds.Remove(compound);
compound.HomogeneousMixture = Null;
Volume -= deltaV;
}
///
/// Add compound into the homogeneous mixture
///
/// compound to add
///
/// add without combine to isomorphic compound
public void AddCompound(Compound compound, bool resetCache = true, bool skipCombine=false)
{
if (compound.HomogeneousMixture == this)
return;
bool added = false;
if(!skipCombine)
{
foreach (Compound sCompound in Compounds.Where(x => x.Expression == compound.Expression))
{
if (sCompound.IsometricTo(compound))
{
sCompound.Amount += compound.Amount;
compound.Amount = 0d;
sCompound.Energy += compound.Energy;
added = true;
break;
}
}
}
compound.HomogeneousMixture = added ? Null : this;
if (!added)
Compounds.Add(compound);
if(resetCache)
ResetAllCache();
}
///
/// Amount of this mixture
///
public double Amount
{
get => Compounds
.Select(c => c.Amount)
.DefaultIfEmpty(0)
.Sum();
set
{
Dictionary res = new Dictionary();
foreach (Compound c in Compounds)
res[c] = value * c.Concentration;
foreach (Compound c in Compounds)
c.Amount = res[c];
}
}
///
/// ratio of this mixture in the heterogeneous mixture
///
public double Ratio =>
Amount / (HeterogeneousMixture == HeterogeneousMixture.Null ? Amount : HeterogeneousMixture.Amount);
///
/// density of the mixture, amount of molecular per volume
///
public double Density => Amount / Volume;
///
/// save mixture into string
///
///
public string Dump() =>
$"`ENV?ENG{Energy.ExactDoubleString()}?V{Volume.ExactDoubleString()}`%ENV`COMPS{String.Join(HomogeneousMixtureResolver.CompoundsSplit, Compounds.Select(c => c.Dump()))}`%COMPS";
///
/// update volume
///
public void VolumeUpdate()
{
double sVolume = Volume + HeterogeneousMixture.ForceFromContainer / CompressionStiffness;
Volume = Math.Max(sVolume, 1E-6);
}
///
/// exchange energy to neighbor mixtures
///
public void HeatExchange()
{
if (Layer != Layer.Parent.First)
{
HomogeneousMixture mPrevious = Layer.Previous.Value;
double balanceTemp = (Energy + mPrevious.Energy) / (Amount + mPrevious.Amount);
double dTemp = balanceTemp - Temperature;
double conductivity = Math.Sqrt(HeatConductivity.Get * mPrevious.HeatConductivity.Get);
double transferTemp = dTemp * conductivity;
Temperature += transferTemp;
mPrevious.Temperature -= transferTemp;
}
if (Layer != Layer.Parent.Last)
{
HomogeneousMixture mNext = Layer.Next.Value;
double balanceTemp = (Energy + mNext.Energy) / (Amount + mNext.Amount);
double dTemp = balanceTemp - Temperature;
double conductivity = Math.Sqrt(HeatConductivity.Get * mNext.HeatConductivity.Get);
double transferTemp = dTemp * conductivity;
Temperature += transferTemp;
mNext.Temperature -= transferTemp;
}
double envBalanceTemp = HeterogeneousMixture.Container.EnvironmentTemperature;
double envDTemp = envBalanceTemp - Temperature;
double envConductivity = HeatConductivity.Get;
Temperature += envDTemp * envConductivity;
}
private void ReactConnecting(HashSet g1, HashSet g2)
{
double maxCd = -1;
BaseBond xb1 = BaseBond.Null;
BaseBond xb2 = BaseBond.Null;
foreach (BaseBond b1 in g1)
{
foreach (BaseBond b2 in g2)
{
H3 cd = new(Complex.ImaginaryOne / 2 * (b1.LogState.Get + b2.LogState.Get));
double cdX = cd.Rayleigh((b1.LogState.Get + b2.LogState.Get).ColumnAverage);
if (cdX > maxCd)
{
maxCd = cdX;
xb1 = b1;
xb2 = b2;
}
}
}
if (xb1 == BaseBond.Null)
return;
if (xb1 == xb2)
{
PlainPack2 s = xb1.Compound.SplitWithBondMap(0.5, xb2, true);
xb2 = s.Item2;
}
if (xb1.Compound.Amount.IsSignificantlyGreaterThen(xb2.Compound.Amount))
{
double amountDiff = xb1.Compound.Amount - xb2.Compound.Amount;
Compound oth = xb1.Compound.Split(amountDiff);
xb1.HomogeneousMixture.AddCompound(oth);
}
if (xb1.Compound.Amount.IsSignificantlyLessThen(xb2.Compound.Amount))
{
double amountDiff = xb2.Compound.Amount - xb1.Compound.Amount;
Compound oth = xb2.Compound.Split(amountDiff);
xb2.HomogeneousMixture.AddCompound(oth);
}
xb1.Connect(xb2);
}
private int ReactionType()
{
Complex d = StateMatrix.Get.Trace();
return d.Phase switch
{
> 7 * Math.PI / 4 or < Math.PI / 4 => 0,
> Math.PI / 4 and < 3 * Math.PI / 4 => 1,
> 3 * Math.PI / 4 and < 5 * Math.PI / 4 => 2,
_ => 3
};
}
///
/// react within the homogeneous mixture
///
public void React()
{
BaseBond[] connected = ConnectedIndex().ToArray();
foreach (BaseBond bond in connected)
{
if (bond.Compound.Amount.IsEqualApprox(0))
continue;
if (
bond.Energy > bond.AntiBondingEnergy &&
bond.ConnectedBond.Energy > bond.ConnectedBond.AntiBondingEnergy &&
bond.DisconnectCondition &&
bond.ConnectedBond.DisconnectCondition
)
{
bond.Disconnect();
}
}
for (int i = 1; i <= 3; i++)
{
BondGroups bg = ReactionGroup();
int type = ReactionType();
if(type == 0)
{
ReactConnecting(bg.Group1, bg.Group3);
ReactConnecting(bg.Group2, bg.Group4);
}
else if (type == 1)
{
ReactConnecting(bg.Group1, bg.Group1);
ReactConnecting(bg.Group3, bg.Group3);
}
else if (type == 2)
{
ReactConnecting(bg.Group2, bg.Group2);
ReactConnecting(bg.Group3, bg.Group3);
}
else
{
ReactConnecting(bg.Group1, bg.Group4);
ReactConnecting(bg.Group2, bg.Group3);
}
}
}
///
/// higher level mixture can resolve lower level mixture
///
public double ResolveLevel => Compounds.Sum(c => c.Concentration * c.ResolveLevel);
///
/// eigen resolvability of other mixture
///
///
///
public double EigenResolvability(HomogeneousMixture oth)
{
LieSU3 s = oth.LogStateMatrix.Get ^ LogStateMatrix.Get;
C3 a = new(1, 0.5, 1);
C3 b = new(0.5, 1, 0.5);
Complex w = a * s * b;
ComplexFieldStructure.Structure.Fix(w);
double res = w.Phase;
if (res > Math.PI)
res = 2 * Math.PI - res;
return res;
}
///
/// eigen resolvability from -pi to pi
///
///
///
public double EigenResolvability(Compound c)
{
LieSU3 s = c.LogStateMatrix.Get ^ LogStateMatrix.Get;
C3 a = new(1, 0.5, 1);
C3 b = new(0.5, 1, 0.5);
Complex w = a * s * b;
w = ComplexFieldStructure.Structure.Fix(w);
double res = w.Phase;
if (res > Math.PI)
res = 2 * Math.PI - res;
return res;
}
///
/// calculate resolvability from eigen resolvability
///
///
///
public double Resolvability(double eigenRes)
{
double res = -Math.Tan((Math.Sin(eigenRes) * Math.PI - Math.PI) / 2);
if (Math.Abs(res) < 1E-6)
return res;
return res;
}
///
/// ability to resolve other compound
///
///
///
public double Resolvability(Compound c)
{
LieSU3 s = c.LogStateMatrix.Get ^ LogStateMatrix.Get;
C3 a = new(1, 0.5, 1);
C3 b = new(0.5, 1, 0.5);
Complex w = a * s * b;
w = ComplexFieldStructure.Structure.Fix(w);
double res = -Math.Tan((Math.Sin(w.Phase) * Math.PI - Math.PI) / 2);
if (Math.Abs(res) < 1E-6)
return 0;
return res;
}
///
/// a tensor of order 3 abstracted from this mixture
///
public C3 Ita => Compounds
.Select(c => c.Concentration * c.StateMatrix.Get.ColumnAverageBivariant)
.Aggregate((a, b) => a + b);
///
/// Red component
///
public Byte ColorRed => (Byte)
(
StateMatrix.Get
.ConjugateOn(ChemistryConstant.OpticalFilter.RedDefinite)
.Rayleigh(Ita) * 255
);
///
/// Green component
///
public Byte ColorGreen => (Byte)
(
StateMatrix.Get
.ConjugateOn(ChemistryConstant.OpticalFilter.GreenDefinite)
.Rayleigh(Ita) * 255
);
///
/// Blue component
///
public Byte ColorBlue => (Byte)
(
StateMatrix.Get
.ConjugateOn(ChemistryConstant.OpticalFilter.BlueDefinite)
.Rayleigh(Ita) * 255
);
///
/// Transparent component
///
public Byte ColorTransparent => (Byte)
(
StateMatrix.Get
.ConjugateOn(ChemistryConstant.OpticalFilter.OpacityDefinite)
.Rayleigh(Ita) * 255
);
public double OsmoticLevel => StateMatrix.Get
.ConjugateOn(new DiagR3(Phase, Math.PI, -Phase))
.Rayleigh(LogStateMatrix.Get.ColumnAverage);
///
/// Should not be used by any methods except ResolveNext and ResolvePrev
///
private void PureResolve(HomogeneousMixture hOther)
{
HashSet< (Compound, double)> toAbsorb = new ();
if (hOther.OsmoticLevel > OsmoticLevel)
return;
foreach (Compound c in hOther.Compounds)
{
double eigenRes = EigenResolvability(c);
double maxRes = Resolvability(eigenRes);
double oRes = hOther.Resolvability(c);
if(oRes < 0 || maxRes < oRes)
continue;
if (eigenRes.IsEqualApprox(0))
{
toAbsorb.Add((c, c.Amount));
continue;
}
double maxResAmount = Amount;
foreach (Compound ct in Compounds)
if (ct.IsometricTo(c))
maxResAmount -= ct.Amount;
maxResAmount *= maxRes;
if(maxResAmount < 0)
continue;
double splitAmount = Math.Min(maxResAmount, c.Amount);
if(splitAmount.IsEqualApprox(0))
continue;
toAbsorb.Add((c, splitAmount));
}
foreach ((Compound c, double a) in toAbsorb)
{
Compound sp = c.Split(a);
double v = sp.Volume;
hOther.Volume -= v;
Volume += v;
/*if (c.Amount.IsEqualApprox(0))
hOther.RemoveCompound(c);*/
AddCompound(sp);
}
if(hOther.Amount.IsEqualApprox(0))
HeterogeneousMixture.RemoveLayer(hOther);
}
private void ResolveNext()
{
if (HeterogeneousMixture == HeterogeneousMixture.Null || Layer.Next.IsTail)
return;
PureResolve(Layer.Next.Value);
}
private void ResolvePrevious()
{
if (HeterogeneousMixture == HeterogeneousMixture.Null || Layer.Previous.IsHead)
return;
PureResolve(Layer.Previous.Value);
}
private bool Resolve()
{
ResolveNext();
ResolvePrevious();
return false;
}
private bool Precipitate()
{
HashSet<(Compound, double)> toDeposit = new();
foreach (Compound c in Compounds)
{
double maxRes = Resolvability(c);
if (maxRes < 0 || maxRes > c.Concentration)
continue;
double maxResAmount = maxRes * (Amount - c.Amount);
if(maxResAmount.IsEqualApprox(0))
continue;
double aDiff = c.Amount - maxResAmount;
if (aDiff.IsSignificantlyGreaterThen(0) )
toDeposit.Add((c, aDiff));
}
foreach ((Compound c, double a) in toDeposit)
{
Compound oth = c.Split(a);
if (oth.FreeDensity.Get > FreeDensity.Get)
{
if (Layer.Next.IsEnding)
{
HomogeneousMixture m = HeterogeneousMixture.AddLayer();
m.Layer.MoveAfter(Layer);
m.AddCompound(oth);
m.Volume = m.FreeVolume;
return true;
}
Layer.Next.Value.AddCompound(oth);
}
else
{
if (Layer.Previous.IsEnding)
{
HomogeneousMixture m = HeterogeneousMixture.AddLayer();
m.Layer.MoveBefore(Layer);
m.AddCompound(oth);
m.Volume = m.FreeVolume;
return true;
}
Layer.Previous.Value.AddCompound(oth);
}
}
return false;
}
///
///
///
/// if any new layer created or deleted
public bool PrecipitateAndResolve()
{
bool a = Resolve();
bool b = Precipitate();
return a || b;
}
///
/// combine identical compounds in the mixture
///
public void Degenerate()
{
HashSet toRemove = new();
foreach (IGrouping g in Compounds.GroupBy(c => c.Expression))
{
HashSet classified = new();
foreach (Compound c in g)
{
classified.Add(c);
foreach (Compound cx in g.Where(x => !classified.Contains(x)))
{
if (!c.IsometricTo(cx))
continue;
classified.Add(cx);
c.Amount += cx.Amount;
cx.Amount = 0;
toRemove.Add(cx);
}
}
}
foreach (Compound c in toRemove)
{
c.HomogeneousMixture = Null;
Compounds.Remove(c);
}
}
}