PVMismatch can be used to simulate mismatch between cells, modules and
strings in a PV system. Mismatch can be caused be differences in irradiance, say
by shading or by changes to the electrical characteristics of the cells such as
series resistance or dark current, and these changes can lead to a difference in
the current-voltage relation of the cell (aka its I-V curve). For a
module made from solar cells in series, the cells must carry the same current,
leading to different voltages in each cell, and so the effective I-V curve of
the panel will differ from an individual cell. In some cases a cell may be
forced to operate in reverse bias in order to pass the current imposed on it.
from pvmismatch import * # this imports pvcell, pvmodule, pvstring and pvsystem
# create a default PV system from 10 strings of 10 300[W] panels each
pvsys = pvsystem.PVsystem()
f = pvsys.plotSys() # plot the system
f.show()
If you find any issues, please report them on GitHub. Also, if you want to
contribute, there's lots to do, so please fork the repo.
So what does uncertainty_wrapper do? Let's say you have a Python function, to calculate solar position, and the function uses a C/C++ library via the Python ctypes library. Or maybe you just have a really complicated set of calculations that you repeat 8760 times, and you want it to run super fast, so you don't want it to calculate derivatives and uncertainty at every internal step, just the final output. Oh and by the way, you want all 8760 calculations vectorized, _ie_: done concurrently as much as possible.
Heres an example using PVLIB of just the first 24 hours.
#
import numpy as np # v1.11.0
import pandas as pd # v0.18.0
import pytz # v2016.1
import pvlib # v0.3.2
from uncertainty_wrapper import unc_wrapper_args # v0.4.1
PST = pytz.timezone('US/Pacific') # Pacific Standard Time
times = pd.DatetimeIndex(start='2015/1/1', end='2015/1/2', freq='1h', tz=PST) # date range
# arguments for the number of observations
# new in UncertaintyWrapper==0.4.1, jagged arrays are okay
latitude, longitude, pressure, altitude, temperature = 37., -122., 101325., 0., 22.
# standard deviation of 1% assuming normal distribution
covariance = np.tile(np.diag([0.0001] * 5), (times.size, 1, 1)) # tile this for the number of observations
@unc_wrapper_args(1, 2, 3, 4, 5)
# indices specify positions of independent variables:
# 1: latitude, 2: longitude, 3: pressure, 4: altitude, 5: temperature
def spa(times, latitude, longitude, pressure, altitude, temperature):
# location class only used prior to pvlib-0.3
dataframe = pvlib.solarposition.spa_c(times, latitude, longitude, pressure, altitude, temperature)
retvals = dataframe.to_records()
zenith = retvals['apparent_zenith']
zenith = np.where(zenith<90, zenith, np.nan)
azimuth = retvals['azimuth']
return zenith, azimuth
ze, az, cov, jac = spa(times, latitude, longitude, pressure, altitude, temperature, __covariance__=covariance)
df = pd.DataFrame({'zenith': ze, 'az': az}, index=times) # easier to view as dataframe
print df
# az zenith
# 2015-01-01 00:00:00-08:00 349.297715 NaN
# 2015-01-01 01:00:00-08:00 40.210628 NaN
# 2015-01-01 02:00:00-08:00 66.719304 NaN
# 2015-01-01 03:00:00-08:00 80.930185 NaN
# 2015-01-01 04:00:00-08:00 90.852887 NaN
# 2015-01-01 05:00:00-08:00 99.212426 NaN
# 2015-01-01 06:00:00-08:00 107.181217 NaN
# 2015-01-01 07:00:00-08:00 115.450451 NaN
# 2015-01-01 08:00:00-08:00 124.564183 84.113440
# 2015-01-01 09:00:00-08:00 135.023137 74.984664
# 2015-01-01 10:00:00-08:00 147.247403 67.475783
# 2015-01-01 11:00:00-08:00 161.371578 62.273878
# 2015-01-01 12:00:00-08:00 176.922804 60.008978
# 2015-01-01 13:00:00-08:00 192.742327 61.017538
# 2015-01-01 14:00:00-08:00 207.519768 65.144340
# 2015-01-01 15:00:00-08:00 220.494108 71.839001
# 2015-01-01 16:00:00-08:00 231.600910 80.422988
# 2015-01-01 17:00:00-08:00 241.184075 89.948123
# 2015-01-01 18:00:00-08:00 249.726361 NaN
# 2015-01-01 19:00:00-08:00 257.751550 NaN
# 2015-01-01 20:00:00-08:00 265.873170 NaN
# 2015-01-01 21:00:00-08:00 275.014534 NaN
# 2015-01-01 22:00:00-08:00 287.078877 NaN
# 2015-01-01 23:00:00-08:00 307.283646 NaN
# 2015-01-02 00:00:00-08:00 348.921385 NaN
# covariance at 8AM
idx = 8
print times[idx]
# Timestamp('2015-01-01 08:00:00-0800', tz='US/Pacific', offset='H')
nf = 2 # number of dependent variables: [ze, az]
print cov[idx]
# [[ 0.6617299 -0.6152971 ]
# [-0.6152971 0.62483904]]
# standard deviation
print np.sqrt(cov[8].diagonal())
# [ 0.81346782, 0.79046761]
# Jacobian at 8AM
nargs = 5 # number of independent args
print jac[idx]
# [[ 5.56456716e-01 -6.45065654e-01 -1.37538277e-06 0.00000000e+00 4.72409055e-04]
# [ 8.29163154e-02 6.47436098e-01 0.00000000e+00 0.00000000e+00 0.00000000e+00]]
#
First this tells us that the standard deviation of the zenith is 1% if the input has a standard deviation of 1%. That's reasonable.
This also tells that zenith is more sensitive to latitude and longitude than pressure or temperature and more sensitive to latitude than azimuth is.
There are also a packages that will create a boiler plate project layout for you but I wouldn't recommend them except as reference guides - the tutorial by NSLS-2 being the notable exception, PTAL!
Bootstrap a Scientific Python Library: This is a tutorial with a template for packaging, testing, documenting, and publishing scientific Python code.
Cookiecutter: A command-line utility that creates projects from cookiecutters (project templates), e.g. creating a Python package project from a Python package project template.
It's hard to pin a standard style down. Here’s mine:
MyProject/ <- git repository
|
+- .gitignore <- *.pyc, IDE files, venv/, build/, dist/, doc/_build, etc.
|
+- requirements.txt <- to install into a virtualenv
|
+- setup.py <- use setuptools, include packages, extensions, scripts and data
|
+- MANIFEST.in <- files to include in or exclude from sdist
|
+- readme.rst <- incorporate into setup.py and docs
|
+- changes.rst <- release notes, incorporate into setup.py and docs
|
+- myproject_script.py <- script to run myproject from command line, use Python
| argparse for command line arguments put shebang
| `#! /usr/bin/env python` on 1st line and end with a
| `if __name__ == "__main__":` section, include in
| setup.py scripts section for install
|
+- any_other_scripts.py <- scripts for configuration, documentation generation
| or downloading assets, etc., include in setup.py
|
+- venv/ <- virtual environment to run tests, validate setup.py, development
|
+- myproject/ <- top level package keeps sub-packages and package-data together
| for install
|
+- __init__.py <- contains __version__, an API by importing key modules,
| classes, functions and constants, __all__ for easy import
|
+- docs/ <- use Sphinx to auto-generate documentation
|
+- tests/ <- use nose to perform unit tests
|
+- other_package_data/ <- images, data files, include in setup.py
|
+- core/ <- main source code for myproject, sometimes called `lib`
| |
| +- __init__.py <- necessary to make mypoject_lib a sub-package
| |
| +- … <- the rest of the folders and files in myproject
|
+- related_project/ <- a GUI library that uses myproject_lib or tools that
| myproject_lib depends on that's bundled together, etc.
|
+- __init__.py <- necessary to make related_project a sub-package
|
+- … <- the rest of the folders and files in your the related project
These are both extremely light and fast but offer somewhat more quality than google pretify which I also mentioned in syntax sensation. One thing I will say about both of these is that looking at the resulting DOM, highlight.js prepends hljs- to all of its classes, almost like a namespace, so that it's unlikely that there will ever be conflicts with any other plugin. highlight.js has many languages and styles while Prism has many extra plugins like line-numbers that you can add to your build from their download page. Finally both of these new syntax highlighter's conform to the <pre><code class="language-blah"></code></pre> style that evidently is the standard for putting code into HTML documents. Who knew? SyntaxHighlighter only uses <pre class="brush: blah"></pre> which is non-standard, I guess. Nit-pick much?
Check out for yourself how Bootstrap interacts with each syntax highlighter in the iframe below, or click the link to open in a new tab. The option menu on the right side of the navbar lets you choose which syntax highlighter to see. The template is Bootstrap's theme example which you can return to by clicking the brand on the left side of the navbar. Since highlight.hs comes with 49 styles, you can peruse them from the dropdown menu. Let me know if you find anything amiss anywhere. Of course you will see the extra y-scrollbar in the SyntaxHightlighter rendition.
I am proud to introduce Quantities for MATLAB. Quantities is an units and uncertainties package for MATLAB. It is inspired by Pint, a Python package for quantities.
Installation
Clone or download the Quantities package to your MATLAB folder as +Quantities.
Usage
Construct a units registry, which contains all units, constants, prefixes and dimensions.
>> ureg = Quantities.unitRegistry
ureg =
Map with properties:
Count: 279
KeyType: char
ValueType: any
Optionally pass verbosity parameter to unitRegistry to see list of units loaded.
>> ureg = Quantities.unitRegistry('v',2)
Units and constants can be indexed from the unitRegsitry using their name or alias in parentheses or as dot-notation. The unit, constant and quantity class all subclass to double so you can perform any operation on them. Combining a double with a unit creates a quantity class object.
>> T1 = 45*ureg('celsius') % index units using parentheses or dot notation
T1 =
45 ± 0 [degC];
>> T2 = 123.3*ureg.degC % index units by name or by alias
T2 =
123.3 ± 0 [degC];
>> heat_loss = ureg.stefan_boltzmann_constant*(T1^4 - T2^4)
heat_loss =
-819814 ± 0 [gram*second^-3];
Perform operations. All units are converted to base.
You can make an image based mockup with simple Javascript that loops over the images everytime you click. First you need to make a bunch of images, similar to a powerpoint slide deck animation, where one thing is different on each slide. Then as you click through the slides it seems interactive, even though the content is actually static. Save the PNG images as "page1", page2", etc. incrementing the numbers in order. The name and numbering scheme will be used to queue the images in the mockup.
jQuery
jQuery just make's everything easier. Here jQuery binds the tag with the id "content" to a click event, and then changes the tags "src" attribute to the page we want to show using the counter variable and the modulus of the total number of pages. We'll output the new "src" attribute value to the console for troubleshooting. On Chrome you can see the console by rightclicking "Inspect element" or ctrl-shft-c.
Puro JS and bad HTML
This kind of code is frowned upon, but you can actually set the callback function of an event in the HTML.
Puro JS another way
Now we do what we did with jQuery, only with pure JS.
If for some bizarre reason you find yourself looking for a YA dystopian novel set in SF in modern times, then you can read Cory Doctorow's Homeland,
and its prequel Little Brother for free (if you don't mind plugs by the author for independent brick'n'mortar bookshops nationwide between chapters).
I used the epub file in the Nook reader app on my Android phone. I just dropped the file in the Nook's "My Documents folder". The epub file is unfortunately incorrectly formatted for Google books. For an Amazon Kindle compatible version scroll to the bottom for the mobi file.
Maybe enjoy? (I did, but beware the themes hit very close to home. That's good right?)
function [x,info] = myNonLinearProblem(x0, auxdata)
%MYNONLINEARPROBLEM Solves my non-linear problem
% [X,INFO] = MYNONLINEARPROBLEM[X0,AUXDATA]
% returns solution, X, and INFO to my non-linear problem
% X0 is the initial guess
% AUXDATA are any additional arguments my non-linear problem
% requires
x0 = x0(:); % initial guess
%% constraints
%set all of your equations as constraints
funcs.constraints = @constraints;
% constraints require a Jacobian
funcs.jacobian = @jacobian;
% Jacobians require a sparsity structure
funcs.jacobianstructure = @jacobianstructure;
% set upper and lower bounds of constraints to zero
options.cl = zeros(size(x0)); % lower bounds
options.cu = zeros(size(x0)); % lower bounds
%% objective
% set the objective sum of the squares
funcs.objective = @objective;
% objective requires a gradient
funcs.gradient = @gradient;
%% options
% set Quasi-Newton option
options.ipopt.hessian_approximation = 'limited-memory';
%% solve
[x,info] = ipopt_auxdata(x0,funcs,options);
end
% x & auxdata must be passed as only args to
% objective, gradient, constraints and jacobian
function f = objective(x,auxdata)
% objective is the sum of the squares of the residuals
f = residual(x,auxdata);
f = f(:);
f = f'*f;
end
function g = gradient(x,auxdata)
[f,j] = residual(x,auxdata);
f = f(:);
g = f'*j;
end
function f = constraints(x,auxdata)
f = residual(x,auxdata);
f = f(:);
end
% Jacobian and Jacobian structure must be sparse
function j = jacobian(x,auxdata)
% rows correspond to each constraint
% columns correspond to each x
[~,j] = residual(x,auxdata);
j = sparse(j);
end
function j = jacobianstructure(x)
% x is the only arg passed to Jacobian structure
% assuming closed system of equations
% # of constraints = degrees of freedom
% therefore Jacobian matrix will be square
j = sparse(ones(size(x)));
end
% put the equations and their derivatives here
% unfortunately IPOPT is not a Jacobianless solver :(
function [f,j] = residuals(x,auxdata)
% calculate your residuals here
% f1(x,auxdata) = lhs1(x,auxdata) - rhs1(x,auxdata) = 0
% f2 = ...
% j1(x,auxdata) = [df1/dx1, df1/dx2, ...]
% j2 = ...
end
Wow! Someone took an interest in one of my side projects. A long time ago, we started using Sifter at work, so I decided to make an Android app, called SifterReader to access it from my phone. It was a fun little side project, but we stopped using Sifter so I abandoned it. Now it's been revived. That's nice!
Okay computer science students out there, riddle me this.
A scope is home for a function,
>>> def g(i):
>>> def f():
>>> return i
>>> return f
>>> print [f() for f in (g(i) for i in xrange(3))]
[0, 1, 2]
but what is a scope for a lambda?
>>> print [f() for f in [lambda: i for i in xrange(3)]]
[2, 2, 2]
A scope is home for a generator,
>>> print [f() for f in (lambda: i for i in xrange(3))]
[0, 1, 2]
and a default parameter is a hack for a lack of a scope,
>>> print [f() for f in [lambda a=i: a for i in xrange(3)]]
[0, 1, 2]
but a new scope is home for a lambda.
>>> print [f() for f in ((lambda a=i: lambda: a)() for i in xrange(3))]
[0, 1, 2]
And while we're on the topic of weird Python hacks and weird comprehensions.
What the heck is this?
>>> foobar = [(1, 2, 3), (4, 5), (6, 7, 8, 9), (0, )]
>>> # foo are elements in bar which are elements in foobar
>>> [foo for bar in foobar for foo in bar]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
Well this was really just an excuse to rock the new syntax highlighter.
[UPDATE 2013-06-14] The google group python-excel is a great source of information on this topic. That's where I discovered xlsxWriter and PyXLL. I also searched the PyPI database with keyword: "xlsx", and I got several more hits, which I've added below in addition to these two. Really thought I'd already had checked PyPI - must be losing my mind; need more sleep.
>>> print 'Reading %(Lolita)s in %(Tehran)s' % {'Lolita': 'Excel', 'Tehran': 'Python'} Reading Excel in Python
That was really a stretch. It's actually not funny or witty at all. In fact now it's just terribly awkward. Although it would of made a little sense if Excel and Python were switched, IE:Reading Python in Excel right, but that is not exactly what we mean. Now I'm just makein it worse.
Main Contenders
Moving on, ever tried to read an Excel file into Python? Numpy only supports csv, so you Google and up pop the main contenders:
Python ← Excel
xlrd - Version 0.9.2 as of this post was released 2013-04-09 available at the cheese factory and on Github, BSD license.
openpyxl - Version 1.6.2 released 2013-04-18, an auspicious day, available at the cheese factory and from Bitbucket, MIT license.
xlsxWriter - version 0.3.3 released 2013-06-10 available at PyPI with source on Github, BSD license.
json2xlsx - version 1.2.5, last updated 2013-06-05 at PyPI and Github, BSD.
Python → Excel
DataNitro, formerly IronSpread, is an excel plugin that replaces allows Python to be used for add-ins and macros in lieu of VBA, proprietary commercial/personal licensing. I didn't review this one, but I plan to revisit it later.
PyXLL, version 2.0.3, also lets you use Python to make Excel add-ins using decorators.
openpxl
This project is for Excel 2007 and newer, it is a port of ExcelPHP and is super easy to use, made even easier by its Sphinx docs at read-the-docs. For some weird reason, pypi and bitbucket both point to old (v-1.5.7) documentation @ pythonhosted, but don't be fooled; get the latest which is much expanded!
$ pip install openpyxl
works fine for any platform since there are no extensions, yay!
seems too take awhile and there doesn't seem any logging or progress available. So use the lazy approach by setting use_iterators=True, which is much faster!
This project covers all Excel spreadsheets, don't be mislead by the 0 major version, this is a relatively mature project. It is part of a triad of Excel targeted packages, including xlwt and xlutils for writing and other basic utilities. The documentation is in one giant html page, which has a lot of info, but is frankly more challenging to read than the nice Sphinx documentation of openpyxl. There is also a tutorial in the form of a pdf.
Install all 3 packages using pip works fine as they are all pure Python. Then in an interpreter ...
This is where better documentation might help as there is no indication what the different levels of verbosity are, although maybe it's obvious. False, 0 or None means none, True or 1 gives you lite feedback, just right, but verbosity=2 gives you a deluge of information - probably best to output this to a file instead of STDOUT.
Now here comes the sad part. If you are using Excel 2007 or newer, on_demand is not supported. If you have logging enabled and verbosity>0, then you will see this message:
WARNING *** on_demand=True not yet implemented; falling back to False
Boo Hoo! Because on_demand is the xlrd equivalent of use_iterators or the openpyxl optimized reader which enhanced speed so much. I will say, that xlrd read a large file slightly faster than openpyxl with use_iterators=False, but definitely slower than the optimized reader.
Conclusion
For xls files, use xlrd with on_demand=True .
For xlsx files, use openpyxl with use_iterators=True.
IMHO openpyxl could be improved with logging to give feedback and xlrd/xlwt could use some better documentation.
In my next post, Round II, I play with reading in actual cell contents. Let's see if openpyxl stays in front.
Horner's method is a fast way to make polynomials that removes unnecessary and costly evaluation of power's. It's easy to do for 1-D polynomials, and also pretty easy to do for 2-D polynomials.
The format of p, the polynomial coefficients, is similar to 1-D polynomials:
\[\begin{aligned}
f\left(x,y\right)=&p_1 x^n y^m+p_2 x^{\left(n-1\right)} y^m+\ldots+p_{n+1} y^m+\ldots \\
&p_{n+2}x^ny^{\left(m-1\right)}+p_{n+3}x^{\left(n-1\right)}y^{\left(m-1\right)}+\ldots+p_{2\left(n+1\right)}y^{\left(m-1\right)}+\ldots \\
&\ldots \\
&p_{m\left(n+1\right)+1}*x^n+p_{m\left(n+1\right)+2}*x^{\left(n-1\right)}+\ldots+p_{\left(n+1\right)\left(m+1\right)}
\end{aligned} \]
Gists:
BTW the double nested loop here is about 25% faster than 2 smaller loops with a lot of vectorization. Not sure why, but I occasionally find that loops are actually more efficient than big matrix calculations.
Updated 2012-07-20 Geany, a lightweight IDE for Java, Python, C/C++ and other computer languages, is available for MS-Windows. You can install it with or without the GTK libraries, which is a nice option, if you already have either GTK+ or PyGTK installed. But, and there's always a "but" isn't there, the executable and shortcuts won't work because they will require GTK to be part of your MS-Windows PATH, which it probably won't be. Honestly, I can't remember if I was given an option to add GTK to the PATH during install, but it's not on my PATH now. Conveniently, though, GTK was nice enough to add a batch file that starts a shell with their environmental variables preset, such as adding <path to GTK runtime>\bin to %PATH%. You need to fix setenv.cmd first, though. See this post. So, to get the Geany shortcuts to run, all you need to dois edit the "Target:" field to include the nice batch file from GTK. Right click on the Geany shortcut and select properties. On my machine I typed ...
... into the "Target:" field since I have PyGTK which installs the GTK binaries in the Python directory. BTW: %COMSPEC% is usually a shortcut for "C:\WINDOWS\system32\cmd.exe" and %PROGRAMFILES% does the same for "C:\Program Files, so you could also use this.
(Update 2012-07-20) One more place you will have to make this change is to your explorer shell. Now if you are uncomfortable with changes to your registry, then you should backup your entire registry before doing this. From the start menu type regedit, and the registry editor opens. Click file and export and select the All radio button to backup the entire registry. Now go to each of the following keys:
Alternately you can copy the text below and import the keys using import under the file menu option in regedit, or from a command line, type REG IMPORT <filename.reg> where filename is a file saved with the keys below. Or use REGEDIT <filename.reg>. Also you can just double click on the file and it will automatically import your keys, or right-click on them and select merge. Note you must be an administrator to import these keys. Also note that quotes and backslashes in the value must be "escaped" with a backslash, and then the entire value should be enclosed in quotes.
Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\Geany.ProjectFile\Shell\open\command]@="C:\\WINDOWS\\system32\\cmd.exe /C \"\"C:\\Python27\\Lib\\site-packages\\gtk-2.0\\runtime\\bin\\setenv.cmd\"&&\"C:\\Program Files\\Geany\\bin\\Geany.exe\" \"%1\"\"" [HKEY_CLASSES_ROOT\*\Shell\OpenWithGeany\command]@="C:\\WINDOWS\\system32\\cmd.exe /C \"\"C:\\Python27\\Lib\\site-packages\\gtk-2.0\\runtime\\bin\\setenv.cmd\"&&\"C:\\Program Files\\Geany\\bin\\Geany.exe\" \"%1\"\""
If you have the standalone version of GTK, you may not have the convenient setenv.cmd file, so here are the settings that it gives you. Of course you could just add your GTK bin folder to your MS-Windows PATH, but maybe there was a reason you didn't do that when you installed GTK. Hm, maybe there's some conflict? A .dll namesake lurking around somewhere? If you choose the option I present here, you'll have to do this everywhere you have a shortcut, or just create a shortcut or batch file once in the Geany bin folder and point all your shortcuts to that.
set RUNTIME_BIN="<the bin file that has libglib-2.0.0.dll, a bunch of .exe's and .dll's>"
set RUNTIME_PKGCONFIG="%RUNTIME_BIN%\..\lib\pkgconfig\"
set PYTHON_ROOT="<where ever your python directory is>"
set PYTHON_SCRIPTS="%PYTHON_ROOT%\Scripts\"
set PYTHON_PKGCONFIG="%PYTHON_ROOT%\Lib\pkgconfig\"
set PATH="%PYTHON_ROOT%;%PYTHON_SCRIPTS%;%RUNTIME_BIN%;%PATH%"
set PKG_CONFIG_PATH="%PYTHON_PKGCONFIG%;%RUNTIME_PKGCONFIG%;%PKGCONFIG_PATH%"
Taa-daa!
Sadly, one thing you'll miss is the terminal screen, if you are used to the Linux version.
It may not seem that hot, but steam tables are essential for modeling Rankine cycles like steam turbines and the boilers that provide steam like solar thermal. But properties are not enough, you need thermodynamic derivatives as well. The IAPWS provides correlations for water and steam properties and derivatives which I have coded in MATLAB for your use. You can find them on Github.
Since I started trying to develop Android, web and other apps, I started another blog Breaking Bytes. There are links to SifterReader an Android app that lets you read your Sifter issues. I also post updates and other relevant info related to the apps. Enjoy!
Someone out there has encountered the zigzag garbage a numerical solution spits out when there is pressure-velocity decoupling. You can try to refine the mesh, smooth forcing functions or adjust the Peclet number, but in the end you are undone by a fundamental flaw in the advection terms and the central difference approximation of a first derivative. The problem is advection requires a first derivative, and the central difference may be very weakly coupled to its own node. The result is essentially two solutions, one for the odd nodes, and one for the even. This is why it is often called odd-even decoupling. But why not turn this apparent weakness into an advantage? Cut your degrees of freedom in half, and just use a staggered mesh. Solve for enthalpy (energy) at the odd nodes, solve for momentum (pressure) at the even nodes, and solve both in the half nodes at the inlet and exit, where forward or backward difference approximations are used. Use linear averages everywhere else. This has worked very well for me. I also solve for all of the mass flow everywhere at once since it is explicit on boundary conditions and the time rate of change of mass in the system (i.e. continuity or mass balance). Given the enthalpy and pressure at every node, I can explicitly calculate the density using the IAPWS-IF97 steam tables, and given the mass flux from continuity, I can now calculate the velocity. No more zig-zags, I've reduced my degrees of freedom nearly in half, and I didn't have to add artificial diffusion or any other fudge factors. Yay!