Source code for gssa.family

# This file is part of the Go-Smart Simulation Architecture (GSSA).
# Go-Smart is an EU-FP7 project, funded by the European Commission.
#
# Copyright (C) 2013-  NUMA Engineering Ltd. (see AUTHORS file)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


import os
import json
import asyncio
import logging
from .parameters import read_parameters, convert_parameter

logger = logging.getLogger(__name__)

register = {}


[docs]class FamilyType(type): """Metaclass for families. This metaclass ensures that any new Family class gets registered for access from GSSA-XML. """ def __init__(cls, clsname, bases, dct): if cls.family_name is not None: register[cls.family_name] = cls logger.info("Registered family: %s" % cls.family_name) return type.__init__(cls, clsname, bases, dct)
[docs]class Family(metaclass=FamilyType): """Essential routines for Families.""" family_name = None
[docs] def load_core_definition(self, xml, parameters, algorithms): """Certain bits of the numerical model should always be present.""" self._needles = {} self._regions = {} self._regions_by_meaning = {} # Start with the needles... one of the few CDM items that should appear # largely intact needles = xml.find('needles') if needles is not None: k = 0 # Add each needle for needle in needles: # There should be a file definition - not necessarily a # filename, but possibly a library ID needle_file = needle.get("file") location = needle_file.split(':', 1) # If we have a traditional region, then we should have an STL # file definition if location[0] in ('surface', 'zone', 'both'): target_file = "%s%s" % (needle.get("index"), os.path.splitext(location[1])[1]) needle_file = "%s:%s" % (location[0], target_file) self._files_required[os.path.join('input', target_file)] = location[1] # Any changes to local/remote dirs here # Add this needle and requisite structure self._needles[needle.get("index")] = { "parameters": read_parameters(needle.find("parameters")), "file": needle_file, "class": needle.get("class") } # Record which needle, exactly, this is self._needle_order[k] = needle.get("index") k += 1 # The global parameters were loaded before this, save them. self._parameters = parameters # Go through the regions in the model and process them regions = xml.find('regions') for region in regions: # Group regions by their type (e.g. organ/vessel) if region.get('name') not in self._regions_by_meaning: self._regions_by_meaning[region.get('name')] = [] # Identify the input file for this region try: target_file = "%s%s" % (region.get("id"), os.path.splitext(region.get('input'))[1]) except AttributeError as e: logger.error("Missing input file : %s, %s, %s" % (region.get('name'), region.get('input'), str(region.get('groups')))) raise e self._regions[region.get('id')] = { "format": region.get('format'), "meaning": region.get('name'), "input": target_file, "groups": json.loads(region.get('groups')) } # If we have a parameter saying that organs should be a subdomain of # a larger mesh, set the format to reflect its behaviour as a # subdomain, not a boundary. In fact, it may not even be a marked # surface at all. if self.get_parameter("SETTING_ORGAN_AS_SUBDOMAIN") and region.get('name') == 'organ': if self.get_parameter('SETTING_ORGAN_AS_SURFACE'): self._regions[region.get('id')]["format"] = 'both' else: self._regions[region.get('id')]["format"] = 'zone' # If we have a traditional format, add the input file to the # required list if self._regions[region.get('id')]["format"] in ('surface', 'zone', 'both', 'mesh') and region.get('input'): self._files_required[os.path.join('input', target_file)] = region.get('input') # Any changes to local/remote dirs here self._regions_by_meaning[region.get('name')].append(self._regions[region.get('id')]) self._algorithms = algorithms self._definition = xml.find('definition') # If the definition has a `location`, then load the files from there definition_location = self._definition.get('location') if definition_location: if definition_location.endswith('.tar.gz'): self._files_required[os.path.join('input', 'start.tar.gz')] = self._definition.get('location') # Any changes to local/remote dirs here else: raise RuntimeError("Uploaded definition must be of form *.tar.gz") self._definition = None # Otherwise, it is the body of the `definition` node else: self._definition = self._definition.text
[docs] def get_needle_parameter(self, needle_index, key, try_json=True): """Retrieve a parameter for a given needle. Needle index can be either needle index (as given in XML input) or an integer n indicating the nth needle in the order of the needles XML block. """ if needle_index not in self._needles and needle_index in self._needle_order: needle_index = self._needle_order[needle_index] value = self.get_parameter(key, try_json, self._needles[needle_index]["parameters"]) return value
[docs] def get_parameter(self, key, try_json=True, parameters=None): """Retrieve a parameter from the global (not needle) list.""" if parameters is None: parameters = self._parameters if key not in parameters: return None parameter, typ = parameters[key] return convert_parameter(parameter, typ, try_json)
@asyncio.coroutine
[docs] def validation(self, working_directory=None): return None
@asyncio.coroutine
[docs] def logs(self, only=None): return {}
@asyncio.coroutine
[docs] def cancel(self): return False
from .families import scan # Scan for family classes scan()