Package: cmflow
Installpip install -U cmflowInstall DependencyOn Windows,ShapelyandRtreeare easier to be installed by using Christoph Gohlke's non-official build:https://www.lfd.uci.edu/~gohlke/pythonlibs/#shapelyhttps://www.lfd.uci.edu/~gohlke/pythonlibs/#rtreedescartescan be installed on all platform by:pip install descartesOn Linux (Ubuntu shown here) these can be installed via apt-get:sudo apt-get install -y python-shapely
sudo apt-get install -y python-rtree
sudo apt-get install -y python-descartesExampleCreates BMStats that can be used later, from Leapfrog Geology:# (ONLY ONCE) geo used to get geology from Leapfrog geological model
cmgeo = mulgrid('g_very_fine.dat')

# CSV file created by Leapfrog using cmgeo above
leapfrog = LeapfrogGM()
leapfrog.import_leapfrog_csv('grid_gtmp_ay2017_03_6_fit.csv')

cm_geology = CM_Blocky(cmgeo, leapfrog)

# whatever active model we are working on
bmgeo = mulgrid('gwaixx_yy.dat')

bms_geology = cm_geology.calc_bmstats(bm_geo)
bms_geology.save('a.json')A BMStats object can be reused (very fast) to eg.bms_geology = BMStats('a.json')

# get a cell's stats
cs = bms_geology.cellstats['abc12']

# rock that occupies most in cell 'abc12'
rock_name = bms_geology.zones[np.argmax(cs)]

# how many rock in cell 'abc12'
n_rock = len(np.nonzero(cs))

# list all rocks in cell 'abc12'
rocks = [bm_geology.zones[i] for i in np.nonzero(cs)]

# find all blocks intersect with the zone
blocks, ratios = bm_geology.blocks_in_zone('BASE1')
block_idx, ratios = bm_geology.blocks_in_zone('BASE1', indices=True)BMStatsThis is the object that we keep for later use.  It is associated to a certain
"geometry" file.  So each cell has information on zones. Usually this is
generated by cm.populate_model(), which can be expensive.? should I call it CMStats?? TODO, .cellstats access by cell index? TODO, .Base Model Stats, mainly numpy arrays with rows corresponding to mulgrid
blocks, and columns corresponding to zones.  Each is a value, usually
between 0.0 and 1.0.  Often 1.0 is indicating that particular block is fully
within the zone..stats numpy array (n,m), n = num of model blocks, m = num of zones
.zones list of zone names (str)
.zonestats dict of stats column by zone names
.cellstats dict of stats row by block name6 elements, 3 zones
 A    B    C
0.0, 0.7, 0.3,  -> row sum to 1.0, element 0, 0.7 rock B, 0.3 rock C 
1.0, 0.0, 0.0, 
1.0, 0.0, 0.0, 
0.0, 0.5, 0.5, 
0.1, 0.2, 0.7, 
0.0, 1.0, 0.0, 
(this is only one way of using it, such as a rocktype).stats, numpy array (n * m), n number of geometry cells, m number of zones
.zones, a list of zone name, eg. geology rock names, fault names etc
.zonestats, a dict keyed by zone name, an array of size number of cells, each cell is between
.cellstats, a dict of stats by cell name.save()
.load()
.add_stats() add another bmstat, merge stats
.add_cm() calls cm.populate_model, and merge statsCMCM_BlockyCM_PrismCM_FaultsThese are the objects that can be created in order to create the final BMStats
objects.  The common method .populate_model(bm_geo) is called to create BMStats
objects.  It means the conceptual model is "applied" onto the bm_geo.TODO, .populate_model() should return BMStats instead? TODO, .populate_model() should be called something else?.populate_model(bm_geo) takes a target geometry, and return/creates BMStatsLeapfrogGM