Skip to content

Commit

Permalink
minor changes to precipitation data class
Browse files Browse the repository at this point in the history
  • Loading branch information
Nicholas Ury committed Oct 30, 2024
1 parent d917612 commit 038e2eb
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 57 deletions.
7 changes: 1 addition & 6 deletions examples/02_Multicomponent_Precipitation.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.9.13 ('base')",
"display_name": "calphad_2",
"language": "python",
"name": "python3"
},
Expand All @@ -324,11 +324,6 @@
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.13"
},
"vscode": {
"interpreter": {
"hash": "0273dda5b9fff289b5eb7a13f97dc7960051b95b09ad9bf692ef3217ee21f064"
}
}
},
"nbformat": 4,
Expand Down
9 changes: 2 additions & 7 deletions examples/04_Precipitation_with_Elastic_Energy.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.9.13 ('base')",
"display_name": "calphad_2",
"language": "python",
"name": "python3"
},
Expand All @@ -231,12 +231,7 @@
"pygments_lexer": "ipython3",
"version": "3.9.13"
},
"orig_nbformat": 4,
"vscode": {
"interpreter": {
"hash": "0273dda5b9fff289b5eb7a13f97dc7960051b95b09ad9bf692ef3217ee21f064"
}
}
"orig_nbformat": 4
},
"nbformat": 4,
"nbformat_minor": 2
Expand Down
43 changes: 25 additions & 18 deletions kawin/precipitation/KWNBase.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from enum import Enum

import numpy as np

from kawin.precipitation.non_ideal.EffectiveDiffusion import EffectiveDiffusionFunctions
from kawin.precipitation.non_ideal.ShapeFactors import ShapeFactor
from kawin.precipitation.non_ideal.ElasticFactors import StrainEnergy
Expand Down Expand Up @@ -44,25 +45,16 @@ def __init__(self, phases = ['beta'], elements = ['solute']):
self.Rg = 8.314 #Gas constant - J/mol-K
self.avo = 6.022e23 #Avogadro's number (/mol)
self.kB = self.Rg / self.avo #Boltzmann constant (J/K)

self.dTemp = 0
self.iterationSinceTempChange = 0

#Default variables, these terms won't have to be set before simulation
self.strainEnergy = [StrainEnergy() for i in self.phases]
self.calculateAspectRatio = [False for i in self.phases]
self.RdrivingForceLimit = np.zeros(len(self.phases), dtype=np.float32)
self.shapeFactors = [ShapeFactor() for i in self.phases]
self.theta = 2 * np.ones(len(self.phases), dtype=np.float32)
# Matrix parameters
self.effDiffFuncs = EffectiveDiffusionFunctions()
self.effDiffDistance = self.effDiffFuncs.effectiveDiffusionDistance
self.infinitePrecipitateDiffusion = [True for i in self.phases]
self.dTemp = 0
self.iterationSinceTempChange = 0
self.GBenergy = 0.3 #J/m2
self.parentPhases = [[] for i in self.phases]
self.GB = [GBFactors() for p in self.phases]

#Set other variables to None to throw errors if not set
self.theta = 2 * np.ones(len(self.phases), dtype=np.float32)
self.xInit = None
self.Tparameters = None

#Nucleation site density, it will default to dislocations with 5e12 /m2 density
self._isNucleationSetup = False
Expand All @@ -71,19 +63,33 @@ def __init__(self, phases = ['beta'], elements = ['solute']):
self.GBcornerN0 = None
self.dislocationN0 = None
self.bulkN0 = None

#Unit cell parameters

self.aAlpha = None
self.VaAlpha = None
self.VmAlpha = None
self.atomsPerCellAlpha = None

#Set other variables to None to throw errors if not set
self.Tparameters = None


#Default variables, these terms won't have to be set before simulation
self.strainEnergy = [StrainEnergy() for i in self.phases]
self.shapeFactors = [ShapeFactor() for i in self.phases]
self.GB = [GBFactors() for p in self.phases]
self.gamma = np.empty(len(self.phases), dtype=np.float32)
self.calculateAspectRatio = [False for i in self.phases]
self.RdrivingForceLimit = np.zeros(len(self.phases), dtype=np.float32)
self.infinitePrecipitateDiffusion = [True for i in self.phases]
self.parentPhases = [[] for i in self.phases]

#Unit cell parameters
self.atomsPerCellBeta = np.empty(len(self.phases), dtype=np.float32)
self.VaBeta = np.empty(len(self.phases), dtype=np.float32)
self.VmBeta = np.empty(len(self.phases), dtype=np.float32)
self.Rmin = np.empty(len(self.phases), dtype=np.float32)

#Free energy parameters
self.gamma = np.empty(len(self.phases), dtype=np.float32)
self.dG = [None for i in self.phases]
self.interfacialComposition = [None for i in self.phases]

Expand Down Expand Up @@ -1032,7 +1038,8 @@ def _BetaMulti(self, p):
if beta is None:
return self.betas[p]
else:
rCrit = self._currY[self.R_CRIT][0]
rCrit = self._currY.Rcrit[0]
#rCrit = self._currY[self.R_CRIT][0]
return (self.GB[p].areaFactor * rCrit[p]**2 / self.aAlpha**4) * beta

def _incubationIsothermal(self, t, p, Z, betas):
Expand Down
2 changes: 1 addition & 1 deletion kawin/precipitation/KWNEuler.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ def setup(self):
#Set first index of eq composition
for p in range(len(self.phases)):
#Use arbitrary dg, R and gE since only the eq compositions are needed here
growth_result = self.interfacialComposition[p](self.xComp[self.pData.n], self.temperature[self.pData.n], 0, 1, 0)
growth_result = self.interfacialComposition[p](self.pData.composition[self.pData.n], self.pData.temperature[self.pData.n], 0, 1, 0)
if growth_result is not None:
_, _, _, c_eq_alpha, c_eq_beta = growth_result
self.pData.xEqAlpha[self.pData.n,p] = c_eq_alpha
Expand Down
65 changes: 40 additions & 25 deletions kawin/precipitation/PrecipitationParameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,55 +10,59 @@
BOLTZMANN_CONSTANT = GAS_CONSTANT / AVOGADROS_NUMBER

class PrecipitationData:
# Strings for each attributes to make it easier to loop accessing these terms
ATTRIBUTES = [
'time', 'temperature',
'composition', 'xEqAlpha', 'xEqBeta',
'drivingForce', 'impingement', 'Gcrit', 'Rcrit', 'nucRate', 'precipitateDensity',
'Rnuc', 'Ravg', 'ARavg', 'volFrac', 'fconc'
]

def __init__(self, phases, elements, N = 1):
def __init__(self, phases: list[str], elements: list[int], N: int = 1):
self.phases = phases
self.elements = elements
self.reset(phases, elements, N)
self.reset(N)

def reset(self, phases, elements, N = 1):
def reset(self, N: int = 1):
self.n = N-1
self.time = np.zeros(N)
self.temperature = np.zeros(N)
self.composition = np.zeros((N, len(elements)))
self.xEqAlpha = np.zeros((N, len(phases), len(elements)))
self.xEqBeta = np.zeros((N, len(phases), len(elements)))
self.drivingForce = np.zeros((N, len(phases)))
self.impingement = np.zeros((N, len(phases)))
self.Gcrit = np.zeros((N, len(phases)))
self.Rcrit = np.zeros((N, len(phases)))
self.Rnuc = np.zeros((N, len(phases)))
self.nucRate = np.zeros((N, len(phases)))
self.precipitateDensity = np.zeros((N, len(phases)))
self.Ravg = np.zeros((N, len(phases)))
self.ARavg = np.zeros((N, len(phases)))
self.volFrac = np.zeros((N, len(phases)))
self.fconc = np.zeros((N, len(phases), len(elements)))
self.composition = np.zeros((N, len(self.elements)))
self.xEqAlpha = np.zeros((N, len(self.phases), len(self.elements)))
self.xEqBeta = np.zeros((N, len(self.phases), len(self.elements)))
self.drivingForce = np.zeros((N, len(self.phases)))
self.impingement = np.zeros((N, len(self.phases)))
self.Gcrit = np.zeros((N, len(self.phases)))
self.Rcrit = np.zeros((N, len(self.phases)))
self.Rnuc = np.zeros((N, len(self.phases)))
self.nucRate = np.zeros((N, len(self.phases)))
self.precipitateDensity = np.zeros((N, len(self.phases)))
self.Ravg = np.zeros((N, len(self.phases)))
self.ARavg = np.zeros((N, len(self.phases)))
self.volFrac = np.zeros((N, len(self.phases)))
self.fconc = np.zeros((N, len(self.phases), len(self.elements)))

def appendToArrays(self, newData):
'''
Appends data from another PrecipitationData object to current one
'''
for name in self.ATTRIBUTES:
setattr(self, name, np.concatenate([getattr(self, name), getattr(newData, name)], axis=0))
self.n = len(self.time) - 1

def copySlice(self, N = 0):
def copySlice(self, N: int = 0):
sliceData = PrecipitationData(self.phases, self.elements, N=1)
for name in self.ATTRIBUTES:
getattr(sliceData, name)[0] = getattr(self, name)[N]
return sliceData

def setSlice(self, sliceData, N = 0):
def setSlice(self, sliceData, N: int = 0):
for name in self.ATTRIBUTES:
getattr(self, name)[N] = getattr(sliceData, name)[0]

def print(self, N = 0):
def print(self, N: int = 0):
for name in self.ATTRIBUTES:
print(name, getattr(self, name)[0])
print(f'{name}: {getattr(self, name)[N]}')

class VolumeParameter:
MOLAR_VOLUME = 0
Expand Down Expand Up @@ -102,6 +106,7 @@ class NucleationParameters:
def __init__(self, grainSize = 100, aspectRatio = 1, dislocationDensity = 5e12, bulkN0 = None):
self.setNucleationDensity(grainSize, aspectRatio, dislocationDensity, bulkN0)

self._isSetup = False
self.GBareaN0 = None
self.GBedgeN0 = None
self.GBcornerN0 = None
Expand Down Expand Up @@ -168,7 +173,7 @@ def grainCornerSites(self, grainSize, grainAspectRatio, VmAlpha):
gbCornerN0 = self.grainCornerAmount(grainSize, grainAspectRatio)
return gbCornerN0

def _setupNucleationDensity(self, x0, VmAlpha):
def setupNucleationDensity(self, x0, VmAlpha):
self.bulkN0 = self.bulkSites(x0, VmAlpha)
self.dislocationN0 = self.dislocationSites(VmAlpha)

Expand All @@ -181,6 +186,8 @@ def _setupNucleationDensity(self, x0, VmAlpha):
self.GBedgeN0 = 0
self.GBcornerN0 = 0

self._isSetup = True

class TemperatureParameters:
def __init__(self, *args):
if len(args) == 2:
Expand All @@ -194,11 +201,11 @@ def __init__(self, *args):
self.Tparameters = None
self.Tfunction = None

def setIsothermalTemperature(self, T):
def setIsothermalTemperature(self, T: float):
self.Tparameters = T
self.Tfunction = lambda t: self.Tparameters

def setTemperatureArray(self, times, temperatures):
def setTemperatureArray(self, times: list[float], temperatures: list[float]):
self.Tparameters = (times, temperatures)
self.Tfunction = lambda t: np.interp(t/3600, self.Tparameters[0], self.Tparameters[1], self.Tparameters[1][0], self.Tparameters[1][-1])

Expand All @@ -217,12 +224,15 @@ def __init__(self):
self.volume = VolumeParameter()
self.nucleation = NucleationParameters()
self.theta = 2
self.initComposition = None

class PrecipitateParameters:
'''
Parameters for a single precipitate
'''
def __init__(self, name, phase = None):
# Name is the print/output name of the phase and phase is the actual name in the database
# If phase isn't supplied, then phase = name
self.name = name
if phase is None:
phase = name
Expand All @@ -236,6 +246,8 @@ def __init__(self, name, phase = None):
self.calculateAspectRatio = False
self.RdrivingForceLimit = 0
self.infinitePrecipitateDiffusion = False
self.parentPhases = []
self.Rmin = None

class Constraints:
def __init__(self):
Expand All @@ -252,21 +264,24 @@ def reset(self):
self.checkTemperature = True
self.maxNonIsothermalDT = 1

#Maximum dissolution as volume fraction of the particle size distribution
self.checkPSD = True
self.maxDissolution = 1e-3

#Maximum change in critical radius by relative increase
self.checkRcrit = True
self.maxRcritChange = 0.01

#Maximum change in nucleation rate as a ratio
self.checkNucleation = True
self.maxNucleationRateChange = 0.5
self.minNucleationRate = 1e-5

#Maximum volume change from nucleation
self.checkVolumePre = True
self.maxVolumeChange = 0.001

self.minComposition = 0

self.minNucleateDensity = 1e-10

#TODO: may want to test more to see if this value should be lower or higher
Expand Down

0 comments on commit 038e2eb

Please sign in to comment.