Working on the docs...

This commit is contained in:
Jorrit Wronski
2014-09-09 19:51:25 +02:00
parent 95479bac2c
commit 0868356b91
7 changed files with 655 additions and 477 deletions

View File

@@ -1,5 +1,100 @@
# Makefile for Sphinx documentation
#
#
############################################################
# Generate and copy the include files for the documetation #
############################################################
#
INCOMP_INCLUDES_SOURCE:=$(realpath $(CURDIR)/../dev/incompressible_liquids )
#INCOMP_INCLUDES_TARGET:=$(realpath $(CURDIR)/fluid_properties )
INCOMP_INCLUDES_TARGET:=fluid_properties
# JSON, PDF and JPG all depend on Python, regenerate all of them if Python sources change
INCOMP_INCLUDES_PYTHON:=$(shell find $(INCOMP_INCLUDES_SOURCE)/CPIncomp -iname '*.py') # recursively
INCOMP_INCLUDES_JSONLI:=$(shell find $(INCOMP_INCLUDES_SOURCE)/json -iname '*.json') # recursively
INCOMP_INCLUDES_FNAMES:=$(basename $(notdir $(INCOMP_INCLUDES_JSONLI) ) ) # all fluid IDs
# All the required files
INCOMP_INCLUDES_PDFREP:=$(addprefix $(INCOMP_INCLUDES_TARGET)/report/, $(addsuffix _fitreport.pdf, $(INCOMP_INCLUDES_FNAMES) ) )
INCOMP_INCLUDES_PDFEXT:= all_pure-pdf all_solutions.pdf
INCOMP_INCLUDES_PDFEXT:=$(addprefix $(INCOMP_INCLUDES_TARGET)/, $(INCOMP_INCLUDES_PDFEXT) )
INCOMP_INCLUDES_TXTTAB:= pure-fluids.txt mass-based-fluids.txt mole-based-fluids.txt volume-based-fluids.txt
INCOMP_INCLUDES_TXTTAB:=$(addprefix $(INCOMP_INCLUDES_TARGET)/, $(INCOMP_INCLUDES_TXTTAB) )
#
# Define one single target to be referenced
.PHONY: incompressible_includes
incompressible_includes: $(INCOMP_INCLUDES_PDFREP) $(INCOMP_INCLUDES_PDFEXT) $(INCOMP_INCLUDES_TXTTAB)
#
# Now we split it up again and maps dependencies to their generators
$(INCOMP_INCLUDES_TXTTAB):: $(INCOMP_INCLUDES_SOURCE)/Incompressibles_include_generator.py
cd $(<D); \
python $(<F)
$(INCOMP_INCLUDES_TARGET)/report/%.pdf: $(INCOMP_INCLUDES_SOURCE)/report/%.pdf
mkdir -p $(@D); \
cp $< $@
#%.foo: $$(addsuffix .bar, $$(DATA_$$*))
$(INCOMP_INCLUDES_SOURCE)/report/%.pdf: $(INCOMP_INCLUDES_SOURCE)/all_incompressibles.py $(INCOMP_INCLUDES_PYTHON)
cd $(<D); \
python $(<F)
#$(INCOMP_INCLUDES_PDFREP):: $(INCOMP_INCLUDES_SOURCE)/all_incompressibles.py $(INCOMP_INCLUDES_PYTHON)
# cd $(<D); \
# python $(<F)
#$(INCOMP_INCLUDES_TARGET)/Incompressibles_Fluids.txt: $(addprefix $(PYGEN_DIR)/, Incompressibles_include_generator.py)
# cd $(PYGEN_DIR); \
# python Incompressibles_include_generator.py
#
#fluid_properties/all_pure.pdf: $(addprefix $(PYGEN_DIR)/, all_incompressibles.py)
# cd $(PYGEN_DIR); \
# python all_incompressibles.py
# cp $(PYGEN_DIR)/all_pure.pdf fluid_properties/
# cp $(PYGEN_DIR)/all_solutions.pdf fluid_properties/
#
## general rules for the fit reports
#PYGEN_FILES=$(shell echo $(COOLPROPDIR)*.cpp)
#
#
## Make the .pdf report files, they all come from the same script
#$(INCOMP_INCLUDES_FOLDER)/%.pdf : $(INCOMP_INCLUDES_DIR)/all_incompressibles.py $(INCOMP_INCLUDES_PYTHON)
# cd $(INCOMP_INCLUDES_FOLDER); \
# python all_incompressibles.py
#
### Make the .pdf report files, they all come from the same script
##$(INCOMP_INCLUDES_FOLDER)/report/%.pdf : $(INCOMP_INCLUDES_PYTHON)
## cd $(INCOMP_INCLUDES_FOLDER); \
## python all_incompressibles.py
.PHONY : check_incomp_flags
check_incomp_flags:
@printf "\nMakefile variables:\n\
INCOMP_INCLUDES_SOURCE=$(INCOMP_INCLUDES_SOURCE)\n\
INCOMP_INCLUDES_TARGET=$(INCOMP_INCLUDES_TARGET)\n\
INCOMP_INCLUDES_PYTHON=$(INCOMP_INCLUDES_PYTHON)\n\
INCOMP_INCLUDES_JSONLI=$(INCOMP_INCLUDES_JSONLI)\n\
INCOMP_INCLUDES_FNAMES=$(INCOMP_INCLUDES_FNAMES)\n\
INCOMP_INCLUDES_PDFREP=$(INCOMP_INCLUDES_PDFREP)\n\
INCOMP_INCLUDES_PDFEXT=$(INCOMP_INCLUDES_PDFEXT)\n\
INCOMP_INCLUDES_TXTTAB=$(INCOMP_INCLUDES_TXTTAB)\n"
# You can set these variables from the command line.
SPHINXOPTS =
@@ -106,40 +201,4 @@ rst: FluidInfoGenerator.py
############################################################
# Generate and copy the include files for the documetation
############################################################
#
INCOMP_INCLUDES_DIR:=$(CURDIR)/../dev/incompressible_liquids
INCOMP_INCLUDES_PDF:=$(shell find $(INCOMP_INCLUDES_DIR) -name '*.pdf')
.PHONY: incompressible_includes
incompressible_includes: fluid_properties/all_pure.pdf fluid_properties/Incompressibles_Fluids.txt
fluid_properties/Incompressibles_Fluids.txt: $(addprefix $(PYGEN_DIR)/, Incompressibles_include_generator.py)
cd $(PYGEN_DIR); \
python Incompressibles_include_generator.py
fluid_properties/all_pure.pdf: $(addprefix $(PYGEN_DIR)/, all_incompressibles.py)
cd $(PYGEN_DIR); \
python all_incompressibles.py
cp $(PYGEN_DIR)/all_pure.pdf fluid_properties/
cp $(PYGEN_DIR)/all_solutions.pdf fluid_properties/
# general rules for the fit reports
PYGEN_FILES=$(shell echo $(COOLPROPDIR)*.cpp)
# Make the .pdf report files
$(PYGEN_DIR)/report/%.pdf : %.cpp
$(CC) $(_CPPFLAGS) -c $(_INCLUDES) $< -o $@
.PHONY : check_incomp_flags
check_incomp_flags:
@printf "\nMakefile variables:\n\
INCOMP_INCLUDES_DIR=$(INCOMP_INCLUDES_DIR)\n\
INCOMP_INCLUDES_PDF=$(INCOMP_INCLUDES_PDF)\n"

View File

@@ -3,17 +3,84 @@
Incompressible Fluids
=====================
here is the generated file with the table of incompressibles
In CoolProp, the incompressible fluids are divided into four major groups.
.. include:: Incompressibles_Fluids.txt
* :ref:`Pure fluids <Pure>`.
* :ref:`Mass-based binary mixtures <MassMix>`.
* :ref:`Mole-based binary mixtures <MoleMix>`.
* :ref:`Volume-based binary mixtures <VoluMix>`.
The pure fluids and mass-based binary mixtures are by far the most common fluids
in this library. While the pure fluids contain data for many different kinds of
incompressible liquids, almost all of the binary mixtures are aqueous solutions.
For these liquids, the concentration always refers to the added component ranging
from 0.0 for pure water to 1.0 for no water at all. Please refer to the tables
below to find the allowed minimum and maximum concentrations. Those are likely
to be above 0.0 and below 1.0, respectively.
The first entry in the tables below is the fluid ID that can be used to call the
fluid from the high-level interface. A single PDF page showing the fit quality is
linked to that ID in case you would like to see a few more details about any
specific fluid. To get an overview over all the fits, there are also combined
documents with all the :download:`pure fluids <all_pure.pdf>` and all the
:download:`aqueous solutions <all_solutions.pdf>`.
Incompressible fluids only allow These only allow for calls with
temperature and pressure as input and provide only a subset of thermophysical properties, namely:
density, heat capacity, internal energy, enthalpy, entropy, viscosity and thermal conductivity.
Hence, the available output keys for the ``Props`` function are: "D", "C", "U", "H", "S", "V", "L",
"Tmin", "Tmax" and "Psat". An internal iteration allows us to use enthalpy and pressure as inputs,
but be aware of the reduced computational efficiency.
.. ipython::
In [1]: from CoolProp.CoolProp import PropsSI
#Density of HFE-7100 at 300 K and 1 atm.
In [1]: PropsSI('D','T',300,'P',101325,'INCOMP::HFE')
.. _Pure:
Pure Fluids
-----------
.. include:: pure-fluids.txt
.. _MassMix:
Mass-based binary mixtures
--------------------------
.. include:: mass-based-fluids.txt
.. _MoleMix:
Mole-based binary mixtures
--------------------------
.. include:: mole-based-fluids.txt
.. _VoluMix:
Volume-based binary mixtures
----------------------------
.. include:: volume-based-fluids.txt
.. _FittingReports:
Fitting Reports
=====================
here is a pdf file for all pure fluids :download:`pdf <all_pure.pdf>`
here is a pdf file for all solutions :download:`pdf <all_solutions.pdf>`
Incompressible Liquids

View File

@@ -6,13 +6,13 @@ import sys
# Here we define the types. This is done to keep the definitions at one
# central place instead of hiding them somewhere in the data.
class IncompressibleData(object):
"""
The work horse of the incompressible classes.
Implements both data structures and fitting
"""
The work horse of the incompressible classes.
Implements both data structures and fitting
procedures.
"""
"""
INCOMPRESSIBLE_NOT_SET = 'notdefined'
INCOMPRESSIBLE_POLYNOMIAL = 'polynomial'
INCOMPRESSIBLE_EXPPOLYNOMIAL = 'exppolynomial'
@@ -20,19 +20,19 @@ class IncompressibleData(object):
INCOMPRESSIBLE_LOGEXPONENTIAL= 'logexponential'
INCOMPRESSIBLE_POLYOFFSET = 'polyoffset'
INCOMPRESSIBLE_CHEBYSHEV = 'chebyshev'
SOURCE_DATA = 'data'
SOURCE_EQUATION = 'equation'
SOURCE_COEFFS = 'coefficients'
SOURCE_NOT_SET = 'notdefined'
maxLin = np.finfo(np.float64).max-1
minLin = -maxLin
maxLog = np.log(maxLin)
minLog = -maxLog
def __init__(self):
def __init__(self):
self.source = self.SOURCE_NOT_SET
self.type = self.INCOMPRESSIBLE_NOT_SET
self.coeffs = None #np.zeros((4,4))
@@ -41,34 +41,34 @@ class IncompressibleData(object):
self.yData = None # In case you need a customised second data set (concentration?)
self.sErr = None # Coefficient of determination
self.DEBUG = False
@staticmethod
def baseFunc(x, y=0.0, xbase=0.0, ybase=0.0, eqnType=None, c=None):
if eqnType==None: raise ValueError("You did not provide data for eqnType.")
if c==None: raise ValueError("You did not provide data for the coefficients.")
if eqnType is None: raise ValueError("You did not provide data for eqnType.")
if c is None: raise ValueError("You did not provide data for the coefficients.")
if eqnType==IncompressibleData.INCOMPRESSIBLE_POLYNOMIAL:
return np.polynomial.polynomial.polyval2d(x-xbase, y-ybase, c)
elif eqnType==IncompressibleData.INCOMPRESSIBLE_POLYOFFSET:
#if y!=0.0: raise ValueError("This is 1D only, use x not y.")
return IncompressibleData.basePolyOffset(c, x) # offset included in coeffs
elif eqnType==IncompressibleData.INCOMPRESSIBLE_EXPONENTIAL:
#if y!=0.0: raise ValueError("This is 1D only, use x not y.")
return IncompressibleData.baseExponential(c, x)
elif eqnType==IncompressibleData.INCOMPRESSIBLE_LOGEXPONENTIAL:
#if y!=0.0: raise ValueError("This is 1D only, use x not y.")
return IncompressibleData.baseLogexponential(c, x)
elif eqnType==IncompressibleData.INCOMPRESSIBLE_EXPPOLYNOMIAL:
return np.exp(np.polynomial.polynomial.polyval2d(x-xbase, y-ybase, c))
else:
raise ValueError("Unknown function: {0}.".format(eqnType))
@staticmethod
def baseExponential(co, x):
r,c,coeffs = IncompressibleFitter.shapeArray(co)
@@ -76,7 +76,7 @@ class IncompressibleData(object):
raise ValueError("You have to provide a 3,1 matrix of coefficients, not ({0},{1}).".format(r,c))
coeffs_tmp = np.array(coeffs.flat)
return np.exp(np.clip( (coeffs_tmp[0]/ ( x+coeffs_tmp[1] ) - coeffs_tmp[2]),IncompressibleData.minLog,IncompressibleData.maxLog))
@staticmethod
def baseLogexponential(co, x):
r,c,coeffs = IncompressibleFitter.shapeArray(co)
@@ -93,36 +93,36 @@ class IncompressibleData(object):
offset = coeffs[0][0]
coeffs = np.array(coeffs.flat)[1:]
return np.polynomial.polynomial.polyval(x-offset, coeffs)
### Base functions that handle the custom data type, just a place holder to show the structure.
def baseFunction(self, x, y=0.0, xbase=0.0, ybase=0.0, c=None):
if c==None: c = self.coeffs
if c is None: c = self.coeffs
return self.baseFunc(x, y, xbase, ybase, self.type, c)
def fitCoeffs(self, xbase, ybase, x=None, y=None):
if (x!=None and self.xData!=None and not IncompressibleFitter.allClose(x, self.xData)) \
or (x==None and self.xData==None): raise ValueError("I do not know which x-value you would like to use. Define either x or self.xData.")
if (y!=None and self.yData!=None and not IncompressibleFitter.allClose(y, self.yData)) \
or (y==None and self.yData==None): raise ValueError("I do not know which y-value you would like to use. Define either y or self.yData.")
if x==None and self.xData!=None: x=self.xData
if y==None and self.yData!=None: y=self.yData
if (not x is None and not self.xData is None and not IncompressibleFitter.allClose(x, self.xData)) \
or (x is None and self.xData is None): raise ValueError("I do not know which x-value you would like to use. Define either x or self.xData.")
if (not y is None and not self.yData is None and not IncompressibleFitter.allClose(y, self.yData)) \
or (y is None and self.yData is None): raise ValueError("I do not know which y-value you would like to use. Define either y or self.yData.")
if x is None and not self.xData is None: x=self.xData
if y is None and not self.yData is None: y=self.yData
#res = None
#r2 = None
res,sErr = IncompressibleFitter.fitter(x=x, y=y, z=self.data, \
xbase=xbase, ybase=ybase, \
eqnType=self.type, \
coeffs=self.coeffs, DEBUG=self.DEBUG)
bestCoeffs = np.copy(res)
bestType = self.type
bestsErr = np.copy(sErr)
bestRMS = np.sqrt(np.square(bestsErr).mean()).sum()
count = 0
while bestRMS>0.03 and count<2:
#if self.DEBUG: print("Poor solution found, trying once more with more coefficients.")
@@ -134,7 +134,7 @@ class IncompressibleData(object):
xbase=xbase, ybase=ybase, \
eqnType=self.type, \
coeffs=self.coeffs, DEBUG=self.DEBUG)
elif self.type==IncompressibleData.INCOMPRESSIBLE_LOGEXPONENTIAL and self.data.size>10:
if self.DEBUG: print("Poor solution found with log exponential, trying once more with exponential polynomial.")
self.type=IncompressibleData.INCOMPRESSIBLE_EXPPOLYNOMIAL
@@ -143,7 +143,7 @@ class IncompressibleData(object):
xbase=xbase, ybase=ybase, \
eqnType=self.type, \
coeffs=self.coeffs, DEBUG=self.DEBUG)
# elif self.type==IncompressibleData.INCOMPRESSIBLE_EXPPOLYNOMIAL:
# if self.DEBUG: print("Poor solution found with exponential polynomial, trying once more with normal polynomial.")
# self.type=IncompressibleData.INCOMPRESSIBLE_POLYNOMIAL
@@ -159,53 +159,53 @@ class IncompressibleData(object):
bestType = self.type
bestsErr = np.copy(sErr)
bestRMS = RMS
count += 1
if bestCoeffs==None:
if bestCoeffs is None:
if self.DEBUG: print("There was a fitting error, no solution found.")
elif IncompressibleFitter.allClose(bestCoeffs, self.coeffs):
if self.DEBUG: print("Coefficients did not change.")
else:
if self.DEBUG: print("Best fit for: {0}".format(bestType))
self.coeffs = bestCoeffs
self.type = bestType
self.type = bestType
self.sErr = bestsErr
#if self.DEBUG: print("Fitting statistics:")
#SSE = np.square(self.sErr).sum() # Sum of squares due to error
#SST = ((zData-zData.mean())**2).sum()
#R2 = 1-(ssErr/ssTot )
def setxData(self, xData):
if self.xData==None:
if self.xData is None:
self.xData = xData
else:
if self.DEBUG: print("Cannot update xData, value is set already.")
else:
if self.DEBUG: print("Cannot update xData, value is set already.")
def setyData(self, yData):
if self.yData==None:
if self.yData is None:
self.yData = yData
else:
if self.DEBUG: print("Cannot update yData, value is set already.")
else:
if self.DEBUG: print("Cannot update yData, value is set already.")
def setxyData(self, xData, yData):
self.setxData(xData)
self.setyData(yData)
def toJSON(self):
j = {}
try:
j['coeffs'] = self.coeffs.tolist()
except:
j['coeffs'] = 'null'
j['type'] = self.type
return j
def fromJSON(self, j):
try:
self.coeffs = np.array(j['coeffs'])
@@ -213,22 +213,22 @@ class IncompressibleData(object):
except:
self.coeffs = None
self.type = IncompressibleData.INCOMPRESSIBLE_NOT_SET
return
class IncompressibleFitter(object):
@staticmethod
def allClose(a,b):
if np.array(a).shape==np.array(b).shape:
return np.allclose(a, b)
else: return False
@staticmethod
def shapeArray(array, axs=0):
"""
A function that promotes a 1D array to 2D and
A function that promotes a 1D array to 2D and
also returns the columns and rows.
1D vectors are interpreted as column vectors.
"""
@@ -254,14 +254,14 @@ class IncompressibleFitter(object):
print(array)
raise ValueError("You have to provide a 1D-vector or a 2D-matrix.")
return (r,c,np.reshape(array,(r,c)))
@staticmethod
def fitter(x=None, y=None, z=None, \
xbase=0.0, ybase=0.0, \
eqnType=None, \
coeffs=None, DEBUG=False):
""" The entry point to the fitting routines
:param x : a 1D array in x direction or 2D with one column, most likely temperature
:param y : a 1D array in y direction or 2D with one row, most likely cocentration
:param z : a 2D array of data, rows = len(x[:,0]) and cols = len(y[0])
@@ -270,46 +270,46 @@ class IncompressibleFitter(object):
:param eqnType : an instance of IncompressibleData.INCOMPRESSIBLE_ ...
:param coeffs : the initial guess and shape (!) for the desired coefficients, can be zeros
:param DEBUG : message to display
:returns : None if failed or coeffs filled with the right values
:returns : None if failed or coeffs filled with the right values
A function that selects the correct equations and
fits coefficients. Some functions require a start
guess for the coefficients to work properly.
"""
if x==None: raise ValueError("You did not provide data for the x-values.")
if y==None: raise ValueError("You did not provide data for the y-values.")
if z==None: raise ValueError("You did not provide data for the z-values.")
if xbase==None: raise ValueError("You did not provide data for xbase.")
if ybase==None: raise ValueError("You did not provide data for ybase.")
if eqnType==None: raise ValueError("You did not provide data for eqnType.")
if coeffs==None: raise ValueError("You did not provide data for the coefficients.")
if DEBUG==None: raise ValueError("You did not provide data for DEBUG.")
if x is None: raise ValueError("You did not provide data for the x-values.")
if y is None: raise ValueError("You did not provide data for the y-values.")
if z is None: raise ValueError("You did not provide data for the z-values.")
if xbase is None: raise ValueError("You did not provide data for xbase.")
if ybase is None: raise ValueError("You did not provide data for ybase.")
if eqnType is None: raise ValueError("You did not provide data for eqnType.")
if coeffs is None: raise ValueError("You did not provide data for the coefficients.")
if DEBUG is None: raise ValueError("You did not provide data for DEBUG.")
zr,zc,_ = IncompressibleFitter.shapeArray(z)
xr,xc,x = IncompressibleFitter.shapeArray(x)
yr,yc,y = IncompressibleFitter.shapeArray(y, axs=1)
if DEBUG: print("Data : ({0},{1})".format(zr,zc))
if DEBUG: print("x-axis : ({0},{1})".format(xr,xc))
if DEBUG: print("y-axis : ({0},{1})".format(yr,yc))
if zr==1 and zc==1: #
if zr==1 and zc==1: #
if DEBUG: print("Data no set, we cannot fit the coefficients")
return None,None
if (xc!=1): raise ValueError("The first input has to be a 2D array with one column.")
if (yr!=1): raise ValueError("The second input has to be a 2D array with one row.")
if (xr!=zr): raise ValueError("First independent vector and result vector have to have the same number of rows, {0} is not {1}.".format(xr,zr))
if (yc!=zc): raise ValueError("Second independent vector and result vector have to have the same number of columns, {0} is not {1}.".format(yc,zc))
if DEBUG: print("Coefficients before fitting: \n{0}".format(coeffs))
# Polynomial fitting works for both 1D and 2D functions
if eqnType==IncompressibleData.INCOMPRESSIBLE_POLYNOMIAL or eqnType==IncompressibleData.INCOMPRESSIBLE_EXPPOLYNOMIAL:
cr,cc,_ = IncompressibleFitter.shapeArray(coeffs)
if DEBUG: print("Coefficients: ({0},{1})".format(cr,cc))
if (xr==1 and xc==1 and cr>1):
@@ -321,16 +321,16 @@ class IncompressibleFitter(object):
coeffs = coeffs.T[0]
coeffs = coeffs.reshape((cr,1))
cr,cc,_ = IncompressibleFitter.shapeArray(coeffs)
if DEBUG: print("polynomial detected, fitting {0}".format(eqnType))
if cr==1 and cc==1:
if cr==1 and cc==1:
if DEBUG: print("No coefficients left to fit, aborting procedure.")
coeffs = np.array([[0.0]])
return coeffs, None
if (xr<cr):
return coeffs, None
if (xr<cr):
if DEBUG: print("Less data points than coefficients in first dimension ({0} < {1}), reducing coefficient matrix.".format(xr,cr))
coeffs = coeffs[:xr,:]
if (yc<cc):
if (yc<cc):
if DEBUG: print("Less data points than coefficients in second dimension ({0} < {1}), reducing coefficient matrix.".format(yc,cc))
coeffs = coeffs[:,:yc]
cr,cc,_ = IncompressibleFitter.shapeArray(coeffs)
@@ -345,18 +345,18 @@ class IncompressibleFitter(object):
if DEBUG: print("Sum of squared errors: {0}".format(np.square(sErr).sum()))
if DEBUG: print("Root mean squared errors: {0}".format(np.sqrt(np.square(sErr).mean()).sum()))
return coeffs,sErr
# Select if 1D or 2D fitting
if yc==1 or xr==1: # 1D fitting, only one input
if DEBUG: print("1D function detected.")
if yc==1:
if yc==1:
if DEBUG: print("Fitting {0} in x-direction.".format(eqnType))
coeffs,sErr = IncompressibleFitter.getCoeffsIterative1D(x, z, eqnType=eqnType, coeffs=coeffs, DEBUG=DEBUG)
elif xr==1:
elif xr==1:
if DEBUG: print("Fitting {0} in y-direction.".format(eqnType))
coeffs,sErr = IncompressibleFitter.getCoeffsIterative1D(y, z, eqnType=eqnType, coeffs=coeffs, DEBUG=DEBUG)
else: raise ValueError("Unknown error in matrix shapes.")
else: raise ValueError("Unknown error in matrix shapes.")
if DEBUG: print("Coefficients after fitting: \n{0}".format(coeffs))
if DEBUG: print("Standard deviation: {0}".format(np.nanstd(sErr)))
if DEBUG: print("Sum of squared errors: {0}".format(np.square(sErr).sum()))
@@ -365,13 +365,13 @@ class IncompressibleFitter(object):
elif yc>1: # 2D fitting
raise ValueError("There are no other 2D fitting functions than polynomials, cannot use {0}.".format(eqnType))
else:
raise ValueError("Unknown function.")
else:
raise ValueError("Unknown function.")
# def getCoeffs1d(self, x, z, order):
# if (len(x)<order+1):
# if (len(x)<order+1):
# raise ValueError("You have only {0} elements and try to fit {1} coefficients, please reduce the order.".format(len(x),order+1))
# A = np.vander(x,order+1)[:,::-1]
# #Anew = np.dot(A.T,A)
@@ -379,17 +379,17 @@ class IncompressibleFitter(object):
# #coeffs = np.linalg.solve(Anew, znew)
# coeffs, resids, rank, singulars = np.linalg.lstsq(A, z)
# return np.reshape(coeffs, (len(x),1))
@staticmethod
def getCoeffs2d(x_in, y_in, z_in, x_order, y_order, DEBUG=False):
if x_in==None: raise ValueError("You did not provide data for the x-values.")
if y_in==None: raise ValueError("You did not provide data for the y-values.")
if z_in==None: raise ValueError("You did not provide data for the z-values.")
if x_order==None: raise ValueError("You did not provide data for x_order.")
if y_order==None: raise ValueError("You did not provide data for y_order.")
if DEBUG==None: raise ValueError("You did not provide data for DEBUG.")
if x_in is None: raise ValueError("You did not provide data for the x-values.")
if y_in is None: raise ValueError("You did not provide data for the y-values.")
if z_in is None: raise ValueError("You did not provide data for the z-values.")
if x_order is None: raise ValueError("You did not provide data for x_order.")
if y_order is None: raise ValueError("You did not provide data for y_order.")
if DEBUG is None: raise ValueError("You did not provide data for DEBUG.")
x_order += 1
y_order += 1
#To avoid overfitting, we only use the upper left triangle of the coefficient matrix
@@ -397,105 +397,105 @@ class IncompressibleFitter(object):
y_exp = range(y_order)
limit = max(x_order,y_order)
xy_exp = []
# Construct the upper left triangle of coefficients
# Construct the upper left triangle of coefficients
for i in x_exp:
for j in y_exp:
if(i+j<limit): xy_exp.append((i,j))
# Construct input pairs
# Construct input pairs
xx, yy = np.meshgrid(x_in,y_in,indexing='ij')
xx = np.array(xx.flat)
yy = np.array(yy.flat)
zz = np.array(z_in.flat)
# TODO: Check for rows with only nan values
x_num = len(x_in)
y_num = len(y_in)
y_num = len(y_in)
cols = len(xy_exp)
eqns = x_num * y_num
#if (eqns<cols):
# raise ValueError("You have only {0} equations and try to fit {1} coefficients, please reduce the order.".format(eqns,cols))
# raise ValueError("You have only {0} equations and try to fit {1} coefficients, please reduce the order.".format(eqns,cols))
if (x_num<x_order):
raise ValueError("You have only {0} x-entries and try to fit {1} x-coefficients, please reduce the x_order.".format(x_num,x_order))
if (y_num<y_order):
raise ValueError("You have only {0} y-entries and try to fit {1} y-coefficients, please reduce the y_order.".format(y_num,y_order))
# Build the functional matrix
A = np.zeros((eqns,cols))
for i in range(eqns): # row loop
for j, (xj,yj) in enumerate(xy_exp): # makes columns
A[i][j] = xx[i]**xj * yy[i]**yj
# Remove np.nan elements
mask = np.isfinite(zz)
A = A[mask]
xx = xx[mask]
yy = yy[mask]
zz = zz[mask]
if (len(A) < cols):
raise ValueError("Your matrix has only {0} valid rows and you try to fit {1} coefficients, please reduce the order.".format(len(A),cols))
coeffs, resids, rank, singulars = np.linalg.lstsq(A, zz)
if DEBUG: print("Linear algebra solver returned:")
if DEBUG: print(coeffs)
if DEBUG: print(resids)
if DEBUG: print(rank)
if DEBUG: print(singulars)
#if resids.size>0:
# r2 = 1 - resids / (zz.size * zz.var())
#else:
# r2 = 0
#print("\n r2 2d: ",r2.shape,r2,"\n")
#Rearrange coefficients to a matrix shape
C = np.zeros((x_order,y_order))
for i, (xi,yi) in enumerate(xy_exp): # makes columns
C[xi][yi] = coeffs[i]
sErr = zz - np.polynomial.polynomial.polyval2d(xx, yy, C)
return C, sErr
@staticmethod
def getCoeffsIterative1D(x_in, z_in, eqnType, coeffs, DEBUG=False):
if x_in==None: raise ValueError("You did not provide data for the x-values.")
if z_in==None: raise ValueError("You did not provide data for the z-values.")
if eqnType==None: raise ValueError("You did not provide data for eqnType.")
if coeffs==None: raise ValueError("You did not provide data for the coefficients.")
if DEBUG==None: raise ValueError("You did not provide data for DEBUG.")
sErr = None
if x_in is None: raise ValueError("You did not provide data for the x-values.")
if z_in is None: raise ValueError("You did not provide data for the z-values.")
if eqnType is None: raise ValueError("You did not provide data for eqnType.")
if coeffs is None: raise ValueError("You did not provide data for the coefficients.")
if DEBUG is None: raise ValueError("You did not provide data for DEBUG.")
sErr = None
#fit = "Powell" # use Powell's algorithm
#fit = "BFGS" # use Broyden-Fletcher-Goldfarb-Shanno
#fit = "LMA" # use the Levenberg-Marquardt algorithm from curve_fit
#fit = "LMA" # use the Levenberg-Marquardt algorithm from curve_fit
fit = ["LMA","Powell","BFGS"] # First try LMA, use others as fall-back
# make sure that we use other routines for polynomials
if (eqnType==IncompressibleData.INCOMPRESSIBLE_POLYNOMIAL) or \
(eqnType==IncompressibleData.INCOMPRESSIBLE_EXPPOLYNOMIAL) :
raise ValueError("Please use the specific polynomial functions, they are much better.")
expLog = False
# Fitting the logarithm of z_in?
if (eqnType==IncompressibleData.INCOMPRESSIBLE_EXPONENTIAL or eqnType==IncompressibleData.INCOMPRESSIBLE_LOGEXPONENTIAL):
expLog = True
xData = np.array(x_in.flat)
if expLog: zData = np.log(z_in.flat)
else: zData = np.array(z_in.flat)
# Remove np.nan elements
mask = np.isfinite(zData)
xData = xData[mask]
zData = zData[mask]
# The residual function
# The residual function
def fun(coefficients,xArray,yArray):
"""
"""
This function takes the coefficient array and
x and y data. It evaluates the function and returns
the sum of the squared residuals if yArray is not
@@ -504,24 +504,24 @@ class IncompressibleFitter(object):
# No offset and no Tbase etc for 1D functions!
calculated = IncompressibleData.baseFunc(xArray, y=0.0, xbase=0.0, ybase=0.0, eqnType=eqnType, c=coefficients)
if expLog: calculated = np.log(calculated)
if yArray==None: return calculated
if yArray is None: return calculated
data = yArray
res = np.sum(np.square(calculated-data))
res = np.sum(np.square(calculated-data))
return res
# Loop through the list of algorithms with basic settings to keep track of our efforts
success = False
counter = 0
tolerance = 1e-16
while (not success):
algorithm = fit[counter]
#fit = "LMA" # use the Levenberg-Marquardt algorithm from curve_fit
if algorithm=="LMA":
#fit = "LMA" # use the Levenberg-Marquardt algorithm from curve_fit
if algorithm=="LMA":
def func(xVector, *coefficients):
return fun(np.array(coefficients), xVector, None)
#return self.baseFunction(xVector, 0.0, 0.0, 0.0, np.array(coefficients)) #np.array([self._PropsFit(coefficients,xName,T=Ti) for Ti in T])
try:
#print func(xData, coeffs_start)
# Do the actual fitting
@@ -543,19 +543,19 @@ class IncompressibleFitter(object):
else:
if DEBUG: print("Fit failed for {0}.".format(algorithm))
if DEBUG: sys.stdout.flush()
success = False
success = False
except RuntimeError as e:
if DEBUG: print("Exception using "+algorithm+": "+str(e))
if DEBUG: sys.stdout.flush()
success = False
#fit = "MIN" # use a home-made minimisation with Powell and Broyden-Fletcher-Goldfarb-Shanno
elif algorithm=="Powell" or algorithm=="BFGS":
arguments = (xData,zData)
#options = {'maxiter': 1e2, 'maxfev': 1e5}
try:
res = minimize(fun, coeffs, method=algorithm, args=arguments, tol=tolerance)
if res.success:
@@ -566,7 +566,7 @@ class IncompressibleFitter(object):
#ssErr = (res['fvec']**2).sum()
#ssTot = ((zData-zData.mean())**2).sum()
#r2 = 1-(ssErr/ssTot )
#print("\n r2 : ",r2.shape,r2,algorithm,"\n")
#print("\n r2 : ",r2.shape,r2,algorithm,"\n")
return res.x,sErr
else:
if DEBUG: print("Fit failed for {0}.".format(algorithm))
@@ -580,7 +580,7 @@ class IncompressibleFitter(object):
# Something went wrong, probably a typo in the algorithm selector
else:
raise (ValueError("Error: You used an unknown fit method."))
if counter<len(fit)-1:
#print("Fit did not succeed with {0}, reducing tolerance to {1}.".format(algorithm,tol))
success = False
@@ -589,14 +589,13 @@ class IncompressibleFitter(object):
tolerance *= 1e2
if DEBUG: print("Fit did not succeed, reducing tolerance to {0}.".format(tolerance))
success = False
counter = 0
counter = 0
else:
if DEBUG: print("--------------------------------------------------------------")
if DEBUG: print("Fit failed for {0}. ".format(fit))
if DEBUG: print("--------------------------------------------------------------")
return coeffs,1

View File

@@ -146,7 +146,7 @@ class SolutionData(object):
def rho (self, T, p=0.0, x=0.0, c=None):
if not self.checkTPX(T, p, x): return np.NAN
if c==None:
if c is None:
c=self.density.coeffs
if self.density.type==self.density.INCOMPRESSIBLE_POLYNOMIAL:
return np.polynomial.polynomial.polyval2d(T-self.Tbase, x-self.xbase, c)
@@ -154,7 +154,7 @@ class SolutionData(object):
def c (self, T, p=0.0, x=0.0, c=None):
if not self.checkTPX(T, p, x): return np.NAN
if c==None:
if c is None:
c = self.specific_heat.coeffs
if self.specific_heat.type==self.specific_heat.INCOMPRESSIBLE_POLYNOMIAL:
return np.polynomial.polynomial.polyval2d(T-self.Tbase, x-self.xbase, c)
@@ -168,7 +168,7 @@ class SolutionData(object):
def u (self, T, p=0.0, x=0.0, c=None):
if not self.checkTPX(T, p, x): return np.NAN
if c==None:
if c is None:
c = self.specific_heat.coeffs
if self.specific_heat.type==self.specific_heat.INCOMPRESSIBLE_POLYNOMIAL:
c_tmp = np.polynomial.polynomial.polyint(c)
@@ -192,7 +192,7 @@ class SolutionData(object):
return self.saturation_pressure.baseFunction(T, x, self.Tbase, self.xbase, c=c)
def Tfreeze(self, T, p=0.0, x=0.0, c=None):
if c==None:
if c is None:
c = self.T_freeze.coeffs
if self.T_freeze.type==self.T_freeze.INCOMPRESSIBLE_POLYNOMIAL:
@@ -327,7 +327,7 @@ class DigitalData(SolutionData):
z = None
# First we try to read the file
if (dataID!=None and os.path.isfile(self.getFile(dataID))): # File found
if (not dataID is None and os.path.isfile(self.getFile(dataID))): # File found
fileArray = self.getFromFile(dataID)
x = np.copy(fileArray[1:,0 ])
y = np.copy(fileArray[0 ,1:])
@@ -377,7 +377,7 @@ class DigitalData(SolutionData):
z = np.zeros( (len(x)+1,len(y)+1) )
r,c = z.shape
if func==None: raise ValueError("Need a function to update the data file.")
if func is None: raise ValueError("Need a function to update the data file.")
for i in range(r-1):
for j in range(c-1):
@@ -386,7 +386,7 @@ class DigitalData(SolutionData):
z[1:,0] = x
z[0,1:] = y
if dataID!=None:
if not dataID is None:
self.writeToFile(dataID, z)
else:
if DEBUG: print("Not updating data file, dataID is missing.")

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,8 @@ from __future__ import division, absolute_import, print_function
import os
from CPIncomp import getCoefficientFluids, getDigitalFluids, getMelinderFluids,\
getPureFluids, getSecCoolFluids
from CPIncomp.DataObjects import SolutionData
from CPIncomp.BaseObjects import IncompressibleData
# See http://stackoverflow.com/questions/11347505/what-are-some-approaches-to-outputting-a-python-data-structure-to-restructuredte
def make_table(grid):
@@ -36,6 +38,7 @@ def table_div(max_cols, header_flag=1):
def writeTextToFile(path,text):
print("Writing to file: {0}".format(path))
f = open(path, "w")
f.write(text)
f.close()
@@ -56,9 +59,12 @@ def normalize_row(row, max_cols):
if __name__ == '__main__':
FLUID_INFO_FOLDER=os.path.join("..","..","Web","fluid_properties")
FLUID_INFO_FOLDER=os.path.abspath(os.path.join("..","..","Web","fluid_properties"))
FLUID_INFO_INCOMP_LIST=os.path.join(FLUID_INFO_FOLDER,"Incompressibles_Fluids.txt")
FLUID_INFO_MASS_LIST=os.path.join(FLUID_INFO_FOLDER,"mass-based-fluids.txt")
FLUID_INFO_MOLE_LIST=os.path.join(FLUID_INFO_FOLDER,"mole-based-fluids.txt")
FLUID_INFO_VOLU_LIST=os.path.join(FLUID_INFO_FOLDER,"volume-based-fluids.txt")
FLUID_INFO_PURE_LIST=os.path.join(FLUID_INFO_FOLDER,"pure-fluids.txt")
@@ -75,11 +81,60 @@ if __name__ == '__main__':
allObjs += pureFObjs[:]
allObjs += secCoObjs[:]
testTable = []
testTable.append(['Name', 'Description', 'Reference']) # Headline
# Parse the objects and make lists according to the data sources
dataSourceObjs = []
equaSourceObjs = []
coefSourceObjs = []
unknSourceObjs = []
# Make other lists for pure solution fluids
massBasedObjs = []
moleBasedObjs = []
voluBasedObjs = []
pureBasedObjs = []
unknBasedObjs = []
for fluid in allObjs:
testTable.append([fluid.name, fluid.description, fluid.reference])
if fluid.density.source==IncompressibleData.SOURCE_DATA:
dataSourceObjs += [fluid]
elif fluid.density.source==IncompressibleData.SOURCE_EQUATION:
equaSourceObjs += [fluid]
elif fluid.density.source==IncompressibleData.SOURCE_COEFFS:
coefSourceObjs += [fluid]
else:
unknSourceObjs += [fluid]
if fluid.xid==SolutionData.ifrac_mass:
massBasedObjs += [fluid]
elif fluid.xid==SolutionData.ifrac_mole:
moleBasedObjs += [fluid]
elif fluid.xid==SolutionData.ifrac_volume:
voluBasedObjs += [fluid]
elif fluid.xid==SolutionData.ifrac_pure:
pureBasedObjs += [fluid]
else:
unknBasedObjs += [fluid]
# After all the list got populated, we can process the entries
# and generate some tables
header = ['Name', 'Description', 'Reference']
#
objLists = [pureBasedObjs,massBasedObjs,moleBasedObjs,voluBasedObjs]
filLists = [FLUID_INFO_PURE_LIST,FLUID_INFO_MASS_LIST]
filLists +=[FLUID_INFO_MOLE_LIST,FLUID_INFO_VOLU_LIST]
for i in range(len(objLists)):
testTable = []
testTable.append(header) # Headline
for fluid in objLists[i]:
testTable.append([fluid.name, fluid.description, fluid.reference])
writeTableToFile(filLists[i], testTable)
writeTableToFile(FLUID_INFO_INCOMP_LIST, testTable)

View File

@@ -5,17 +5,16 @@ from CPIncomp import getExampleNames, getPureFluids, getCoefficientFluids,\
import sys
from CPIncomp.DataObjects import SolutionData
if __name__ == '__main__':
if __name__ == '__main__':
runTest = False
runTest = False
runFitting = True
runReports = True
runSummary = True
writer = SolutionDataWriter()
doneObjs = []
# To debug single fluids
if runTest:
solObjs = []
@@ -41,11 +40,11 @@ if __name__ == '__main__':
writer.writeFluidList(solObjs)
writer.writeReportList(solObjs)
sys.exit(0)
# treat the examples first
fluidObjs = getExampleNames(obj=True)
examplesToFit = ["ExamplePure","ExampleSolution","ExampleDigital","ExampleDigitalPure"]
print("\nProcessing example fluids")
for obj in fluidObjs:
if obj.name in examplesToFit:
@@ -54,11 +53,11 @@ if __name__ == '__main__':
doneObjs += fluidObjs[:]
if runFitting: writer.writeFluidList(doneObjs)
if runReports: writer.writeReportList(doneObjs, pdfFile="all_examples.pdf")
# If the examples did not cause any errors,
# If the examples did not cause any errors,
# we can proceed to the real data.
doneObjs = []
print("\nProcessing fluids with given coefficients")
fluidObjs = getCoefficientFluids()
doneObjs += fluidObjs[:]
@@ -68,11 +67,11 @@ if __name__ == '__main__':
if runFitting: writer.fitFluidList(fluidObjs)
else: writer.readFluidList(fluidObjs)
doneObjs += fluidObjs[:]
print("\nProcessing Melinder fluids")
fluidObjs = getMelinderFluids()
doneObjs += fluidObjs[:]
print("\nProcessing pure fluids")
fluidObjs = getPureFluids()
if runFitting: writer.fitFluidList(fluidObjs)
@@ -84,12 +83,12 @@ if __name__ == '__main__':
if runFitting: writer.fitSecCoolList(fluidObjs)
else: writer.readFluidList(fluidObjs)
doneObjs += fluidObjs[:]
print("\nAll {0} fluids processed, all coefficients should be set.".format(len(doneObjs)))
print("Checking the list of fluid objects.")
#doneObjs += getCoefficientObjects()[:]
doneObjs = sorted(doneObjs, key=lambda x: x.name)
purefluids = []
solMass = []
solMole = []
@@ -108,16 +107,16 @@ if __name__ == '__main__':
solVolu += [doneObjs[i]]
elif doneObjs[i].xid==SolutionData.ifrac_pure:
purefluids += [doneObjs[i]]
else:
else:
errors += [doneObjs[i]]
solutions = solMass
solutions += solMole
solutions += solVolu
if runFitting: print("All checks passed, going to write parameters to disk.")
if runFitting: writer.writeFluidList(doneObjs)
if runReports:
if runReports:
print("Creating the fitting reports for the different groups.")
#writer.writeReportList(doneObjs)
#doneObjs.sort(key=lambda x: (x.xid ,x.name))
@@ -126,15 +125,14 @@ if __name__ == '__main__':
writer.writeReportList(purefluids, pdfFile="all_pure.pdf")
if len(solutions)>0 and runReports:
print("Processing {0:2d} solutions - ".format(len(solutions)), end="")
writer.writeReportList(solutions, pdfFile="all_solutions.pdf")
writer.writeReportList(solutions, pdfFile="all_solutions.pdf")
if len(errors)>0 and runReports:
print("Processing {0:2d} faulty fluids - ".format(len(errors)), end="")
writer.writeReportList(errors, pdfFile="all_errors.pdf")
if runSummary:
writer.makeSolutionPlots(solObjs=doneObjs, pdfObj=None)
print("All done, bye")
sys.exit(0)