diff --git a/cli/crue10_model_for_maps.py b/cli/crue10_model_for_maps.py index 253f479..8ab7907 100755 --- a/cli/crue10_model_for_maps.py +++ b/cli/crue10_model_for_maps.py @@ -29,7 +29,7 @@ def crue10_model_for_maps(args): study = Etude(args.etu_path) - model = study.get_modele(args.mo_name) + modele = study.get_modele(args.mo_name) modele.read_all() logger.info(modele.summary()) @@ -63,7 +63,7 @@ def crue10_model_for_maps(args): schema = { 'geometry': 'Polygon', 'properties': OrderedDict([('NOM', 'str'), ('modele', 'str'), ('sousmodele', 'str'), - ('noeud_reference', 'str'), ('is_active', 'bool')]), + ('noeud', 'str'), ('is_active', 'bool')]), } with fiona.open(os.path.join(args.out_folder, 'casiers.shp'), 'w', 'ESRI Shapefile', schema) as out_shp: for sous_modele in modele.sous_modeles: @@ -74,7 +74,7 @@ def crue10_model_for_maps(args): 'NOM': casier.id, 'modele': model_name, 'sousmodele': sous_modele.id, - 'noeud_reference': casier.noeud_reference.id, + 'noeud': casier.noeud_reference.id, 'is_active': casier.is_active, } } @@ -144,11 +144,11 @@ def crue10_model_for_maps(args): # Build list of coordinates following upstream section, left ending points, downstream section and # right ending point coords = [] - coords += branche.sections[0].geom_trace.coords - for section in branche.sections[1:-1]: + coords += branche.get_section_amont().geom_trace.coords + for section in branche.liste_sections_dans_branche[1:-1]: coords.append(section.geom_trace.coords[-1]) - coords += reversed(branche.sections[-1].geom_trace.coords) - for section in reversed(branche.sections[1:-1]): + coords += reversed(branche.get_section_aval().geom_trace.coords) + for section in reversed(branche.liste_sections_dans_branche[1:-1]): coords.append(section.geom_trace.coords[0]) polygon = Polygon(coords).buffer(args.sm_buffer) diff --git a/cli/crue10_model_topographical_graph.py b/cli/crue10_model_topographical_graph.py index 5da1d9b..944558b 100755 --- a/cli/crue10_model_topographical_graph.py +++ b/cli/crue10_model_topographical_graph.py @@ -9,7 +9,7 @@ * il s'agit d'une vue schématique avec tous les noeuds/casiers et les branches (aucune information géographique) * l'orientation des branches correspond au sens de la ligne qui se termine par - une flèche (branche fluviale) ou un symbole qui est "côté noeud_reference aval" + une flèche (branche fluviale) ou un symbole qui est "côté noeud aval" (la seule exception concerne les branches orifices dont la position du symbole tient compte du sens de l'orifice) * les branches ou sous-modèles inactifs sont en pointillés diff --git a/cli/crue9_dc_topographical_graph.py b/cli/crue9_dc_topographical_graph.py index 058447d..5216586 100755 --- a/cli/crue9_dc_topographical_graph.py +++ b/cli/crue9_dc_topographical_graph.py @@ -9,7 +9,7 @@ * il s'agit d'une vue schématique avec tous les noeuds/casiers et les branches (aucune information géographique) * l'orientation des branches correspond au sens de la ligne qui se termine par - une flèche (branche fluviale) ou un symbole qui est "côté noeud_reference aval" + une flèche (branche fluviale) ou un symbole qui est "côté noeud aval" * la coloration des lignes dépend du type de branches * la forme des noeuds et des casiers (avec leur nom associé) sont différentes * les parties commentées ou shunter (par un GOTO) sont ignorées diff --git a/crue10/data/templates/drso.xml b/crue10/data/templates/drso.xml index 6963b0d..f3db848 100644 --- a/crue10/data/templates/drso.xml +++ b/crue10/data/templates/drso.xml @@ -55,7 +55,7 @@ - {%- for section in branche.sections %} + {%- for section in branche.liste_sections_dans_branche %} {% if loop.first %}Amont{% elif loop.last %}Aval{% else %}Interne{% endif %} {{ section.xp|float2str }} @@ -76,11 +76,11 @@ {%- endif %} - + Amont 0.0 - + Aval {{ branche.length }} diff --git a/crue10/emh/branche.py b/crue10/emh/branche.py index 63ee8b5..61ecae7 100644 --- a/crue10/emh/branche.py +++ b/crue10/emh/branche.py @@ -48,7 +48,7 @@ class Branche(ABC): - geom : polyline branch trace - noeud_amont : upstream node - noeud_aval : downstream node - - liste_sections <[crue10.emh.section.Section]>: list of sections + - liste_sections_dans_branche <[crue10.emh.section.Section]>: list of sections - comment : optional text explanation """ @@ -88,7 +88,7 @@ def __init__(self, nom_branche, noeud_amont, noeud_aval, branch_type, is_active= self.geom = None self.noeud_amont = noeud_amont self.noeud_aval = noeud_aval - self.sections = [] + self.liste_sections_dans_branche = [] self.comment = '' @staticmethod @@ -98,11 +98,17 @@ def get_id_type_from_name(branch_type_name): return type_id return None + def get_section_amont(self): + return self.liste_sections_dans_branche[0] + + def get_section_aval(self): + return self.liste_sections_dans_branche[-1] + @property def length(self): """Length displayed in FC (may differ from geometry)""" if self.type in Branche.TYPES_WITH_LENGTH: - return self.sections[-1].xp + return self.get_section_aval().xp else: return 0.0 @@ -125,7 +131,7 @@ def ajouter_section_dans_branche(self, section, xp): if not isinstance(section, SectionSansGeometrie): raise CrueError("La %s ne peut porter que des SectionSansGeometrie" % self) section.xp = xp - self.sections.append(section) + self.liste_sections_dans_branche.append(section) def set_geom(self, geom): check_isinstance(geom, LineString) @@ -139,7 +145,7 @@ def shift_sectionprofil_to_extremity(self): if self.geom is None: raise CrueErrorGeometryNotFound(self) for pos in (0, -1): - section = self.sections[pos] + section = self.liste_sections_dans_branche[pos] if isinstance(section, SectionProfil): if pos == 0: node = self.noeud_amont.geom @@ -151,7 +157,7 @@ def shift_sectionprofil_to_extremity(self): if isinstance(section_point, Point): dx = node.x - section_point.x dy = node.y - section_point.y - self.sections[pos].set_trace( + self.liste_sections_dans_branche[pos].set_trace( translate(section.geom_trace, xoff=dx, yoff=dy)) def normalize_sections_xp(self): @@ -159,20 +165,20 @@ def normalize_sections_xp(self): Recompute section xp to correspond to geometric distance (original values are taken from drso). Last section xp will correspond exactly to the branch length. """ - xp_max = self.sections[-1].xp + xp_max = self.get_section_aval().xp length = self.geom.length if self.type in Branche.TYPES_WITH_LENGTH and abs(xp_max - length) > DIFF_XP_TO_WARN: logger.warn("La longueur de la branche `%s` est estimée à %.2fm (non pas %.2fm)." % (self.id, length, xp_max)) - for i, section in enumerate(self.sections): + for i, section in enumerate(self.liste_sections_dans_branche): try: section.xp = section.xp * length / xp_max except ZeroDivisionError: - section.xp = (i / (len(self.sections) - 1)) * length + section.xp = (i / (len(self.liste_sections_dans_branche) - 1)) * length def __repr__(self): - return "Branche [%i] #%s: %s -> %s (%i sections)" % (self.type, self.id, - self.noeud_amont, self.noeud_aval, len(self.sections)) + return "Branche [%i] #%s: %s -> %s (%i sections)" % (self.type, self.id, self.noeud_amont, self.noeud_aval, + len(self.liste_sections_dans_branche)) class BranchePdC(Branche): diff --git a/crue10/modele.py b/crue10/modele.py index 0f608af..3945591 100644 --- a/crue10/modele.py +++ b/crue10/modele.py @@ -151,7 +151,7 @@ def ajouter_sous_modele(self, sous_modele): self.liste_sous_modeles.append(sous_modele) def ajouter_depuis_modele(self, modele): - """Add sous_modele in current modele with the related initial conditions""" + """Add modele in current modele with the related initial conditions""" for sous_modele in modele.susoubmodels: self.ajouter_sous_modele(sous_modele) @@ -327,7 +327,7 @@ def write_mascaret_geometry(self, geo_path): for i_branche, branche in enumerate(sous_modele.iter_on_branches([20])): if branche.has_geom() and branche.is_active: reach = Reach(i_branche, name=branche.id) - for section in branche.sections: + for section in branche.liste_sections_dans_branche: if not isinstance(section, SectionProfil): raise CrueError("The `%s`, which is not a SectionProfil, could not be written" % section) masc_section = Section(i_section, section.xp, name=section.id) @@ -357,7 +357,7 @@ def write_shp_limites_lits_numerotes(self, shp_path): for branche in sous_modele.iter_on_branches(): for i_lit, lit_name in enumerate(LitNumerote.LIMIT_NAMES): coords = [] - for section in branche.sections: + for section in branche.liste_sections_dans_branche: if isinstance(section, SectionProfil): if i_lit == 0: point = section.interp_point(section.lits_numerotes[0].xt_min) diff --git a/crue10/results.py b/crue10/results.py index b099ed5..dbe16dd 100644 --- a/crue10/results.py +++ b/crue10/results.py @@ -93,6 +93,7 @@ class RunResults: """ Results data for a single Run - rcal_root : rcal XML element + - rcal_path : path to rcal file - rcal_folder : folder path to rcal file - nb_errors : number of errors in ccal.csv - nb_warnings : number of warnings in ccal.csv @@ -110,6 +111,7 @@ class RunResults: def __init__(self, rcal_path): self.rcal_root = ET.parse(rcal_path).getroot() + self.rcal_path = rcal_path self.rcal_folder = os.path.dirname(rcal_path) self.nb_errors = -1 self.nb_warnings = -1 @@ -128,11 +130,6 @@ def __init__(self, rcal_path): self._read_rescalc() self._set_res_pattern() - @property - def model_name(self): - """Modele name with 'Mo_' preffix""" - return os.path.basename(self.rcal_folder) - @property def run_id(self): return os.path.basename(os.path.normpath(os.path.join(self.rcal_folder, '..'))) @@ -157,7 +154,8 @@ def _add_emh_names(self, elt, emh_sec): self.emh[emh_sec].append(emh_name) def _read_ccal(self): - ccal_path = os.path.join(self.rcal_folder, self.model_name[3:] + '.ccal.csv') + ccal_path = self.rcal_path[:-9] + '.ccal.csv' # to replace '.rcal.csv' => FIXME: it should be based on ocal + print(ccal_path) if not os.path.exists(ccal_path): raise CrueError("Le fichier de compte rendu de calcul `%s` est introuvable" % ccal_path) self.nb_errors = 0 diff --git a/crue10/sous_modele.py b/crue10/sous_modele.py index f1ff1b9..be08d63 100644 --- a/crue10/sous_modele.py +++ b/crue10/sous_modele.py @@ -54,7 +54,7 @@ class SousModele(FichierXML): Crue10 sous_modele - id : sous_modele identifier - noeuds <{crue10.emh.noeud.Noeud}>: nodes - - sections <{crue10.emh.section.Section}>: sections + - liste_sections_dans_branche <{crue10.emh.section.Section}>: list of branches (SectionProfil, SectionIdem, SectionInterpolee or SectionSansGeometrie) - branches <{crue10.emh.section.SectionInterpolee}>: branches (only those with geometry are considered) - casiers <{crue10.emh.casier.Casier}>: casiers @@ -148,25 +148,31 @@ def get_noeud(self, nom_noeud): try: return self.noeuds[nom_noeud] except KeyError: - raise CrueError("Le noeud_reference %s n'est pas dans le sous-modèle %s" % (nom_noeud, self)) + raise CrueError("Le noeud %s n'est pas dans le %s" % (nom_noeud, self)) def get_section(self, nom_section): try: return self.sections[nom_section] except KeyError: - raise CrueError("La section %s n'est pas dans le sous-modèle %s" % (nom_section, self)) + raise CrueError("La section %s n'est pas dans le %s" % (nom_section, self)) def get_branche(self, nom_branche): try: return self.branches[nom_branche] except KeyError: - raise CrueError("La branche %s n'est pas dans le sous-modèle %s" % (nom_branche, self)) + raise CrueError("La branche %s n'est pas dans le %s" % (nom_branche, self)) def get_casier(self, nom_casier): try: return self.casiers[nom_casier] except KeyError: - raise CrueError("Le casier %s n'est pas dans le sous-modèle %s" % (nom_casier, self)) + raise CrueError("Le casier %s n'est pas dans le %s" % (nom_casier, self)) + + def get_loi_frottement(self, nom_loi_frottement): + try: + return self.lois_frottement[nom_loi_frottement] + except KeyError: + raise CrueError("La loi de frottement %s n'est pas dans le %s" % (nom_loi_frottement, self)) def iter_on_sections(self, section_type=None, ignore_inactive=False): for _, section in self.sections.items(): @@ -231,8 +237,9 @@ def _read_drso(self, filter_branch_types=None): if section_type == 'SectionProfil': section = SectionProfil(nom_section, emh_section.find(PREFIX + 'ProfilSection').get('NomRef')) elif section_type == 'SectionIdem': - # Only to preserve order of sections, the SectionIdem instance is set below + # Sets temporary to None to preserve order of sections, SectionIdem instance is set below self.sections[nom_section] = None + # ajouter_section can not be called in this case, continue statement is mandatory! continue elif section_type == 'SectionInterpolee': section = SectionInterpolee(nom_section) @@ -250,11 +257,10 @@ def _read_drso(self, filter_branch_types=None): nom_section = emh_section.get('Nom') if section_type == 'SectionIdem': - parent_section = self.sections[emh_section.find(PREFIX + 'Section').get('NomRef')] + parent_section = self.get_section(emh_section.find(PREFIX + 'Section').get('NomRef')) section = SectionIdem(nom_section, parent_section) - section.comment = get_optional_commentaire(emh_section) - self.sections[nom_section] = section + self.set_section(section) elif emh_group.tag == (PREFIX + 'Branches'): if filter_branch_types is None: @@ -306,10 +312,10 @@ def _read_drso(self, filter_branch_types=None): # Add section pilotage if isinstance(branche, BrancheBarrageGenerique) or isinstance(branche, BrancheBarrageFilEau): branche.section_pilote = \ - self.sections[emh_branche.find(PREFIX + 'SectionPilote').get('NomRef')] + self.get_section(emh_branche.find(PREFIX + 'SectionPilote').get('NomRef')) for emh_section in emh_sections: - section = self.sections[emh_section.get('NomRef')] + section = self.get_section(emh_section.get('NomRef')) xp = float(emh_section.find(PREFIX + 'Xp').text) if isinstance(branche, BrancheSaintVenant): section.CoefPond = float(emh_section.find(PREFIX + 'CoefPond').text) @@ -353,7 +359,7 @@ def _read_dptg(self): if emh_group.tag == (PREFIX + 'DonPrtGeoProfilSections'): for emh in emh_group.findall(PREFIX + 'ProfilSection'): nom_section = emh.get('Nom').replace('Ps_', 'St_') # Not necessary consistant - section = self.sections[nom_section] + section = self.get_section(nom_section) section.comment_profilsection = get_optional_commentaire(emh) fente = emh.find(PREFIX + 'Fente') @@ -381,12 +387,12 @@ def _read_dptg(self): if emh_group.tag == (PREFIX + 'DonPrtGeoSections'): for emh in emh_group.findall(PREFIX + 'DonPrtGeoSectionIdem'): - self.sections[emh.get('NomRef')].dz = float(emh.find(PREFIX + 'Dz').text) + self.get_section(emh.get('NomRef')).dz_section_reference = float(emh.find(PREFIX + 'Dz').text) if emh_group.tag == (PREFIX + 'DonPrtGeoBranches'): for emh in emh_group.findall(PREFIX + 'DonPrtGeoBrancheSaintVenant'): nom_branche = emh.get('NomRef') - self.branches[nom_branche].CoefSinuo = float(emh.find(PREFIX + 'CoefSinuo').text) + self.get_branche(nom_branche).CoefSinuo = float(emh.find(PREFIX + 'CoefSinuo').text) def _read_dcsp(self): root = self._get_xml_root_and_set_comment('dcsp') @@ -403,12 +409,12 @@ def _read_dcsp(self): branche.comment_loi = get_optional_commentaire(pdc_elt) elif emh.tag == PREFIX + 'DonCalcSansPrtBrancheSeuilTransversal': - branche.formule_pdc = emh.find(PREFIX + 'FormulePdc').text - branche.elts_seuil = parse_elem_seuil(emh, with_pdc=True) + branche.formule_pertes_de_charge = emh.find(PREFIX + 'FormulePdc').text + branche.liste_elements_seuil = parse_elem_seuil(emh, with_pdc=True) elif emh.tag == PREFIX + 'DonCalcSansPrtBrancheSeuilLateral': - branche.formule_pdc = emh.find(PREFIX + 'FormulePdc').text - branche.elts_seuil = parse_elem_seuil(emh, with_pdc=True) + branche.formule_pertes_de_charge = emh.find(PREFIX + 'FormulePdc').text + branche.liste_elements_seuil = parse_elem_seuil(emh, with_pdc=True) elif emh.tag == PREFIX + 'DonCalcSansPrtBrancheOrifice': elem_orifice = emh.find(PREFIX + 'ElemOrifice') @@ -439,7 +445,7 @@ def _read_dcsp(self): elif emh.tag == PREFIX + 'DonCalcSansPrtBrancheBarrageFilEau': branche.QLimInf = float(emh.find(PREFIX + 'QLimInf').text) branche.QLimSup = float(emh.find(PREFIX + 'QLimSup').text) - branche.elts_seuil = parse_elem_seuil(emh, with_pdc=False) + branche.liste_elements_seuil = parse_elem_seuil(emh, with_pdc=False) regime_denoye_elt = emh.find(PREFIX + 'RegimeDenoye') branche.loi_QZam = parse_loi(regime_denoye_elt) branche.comment_denoye = get_optional_commentaire(regime_denoye_elt) @@ -669,6 +675,12 @@ def write_all(self, folder, folder_config=None): if self.casiers: self._write_shp_casiers(sm_folder) + def set_section(self, section): + check_isinstance(section, Section) + if section.id not in self.sections: + raise CrueError("La section %s n'existe pas" % section.id) + self.sections[section.id] = section + def set_active_sections(self): """ Sections are set to active if they are connected to a branch (active or not!) @@ -677,7 +689,7 @@ def set_active_sections(self): section.is_active = False for branche in self.iter_on_branches(): - for section in branche.sections: + for section in branche.liste_sections_dans_branche: section.is_active = branche.is_active def get_connected_branche(self, nom_section): @@ -685,7 +697,7 @@ def get_connected_branche(self, nom_section): Returns the connected branche if found, else returns None """ for branche in self.iter_on_branches(): - if nom_section in [section.id for section in branche.sections]: + if nom_section in [section.id for section in branche.liste_sections_dans_branche]: return branche return None @@ -711,10 +723,10 @@ def get_connected_casier(self, noeud): def remove_sectioninterpolee(self): """Remove all `SectionInterpolee` which are internal sections""" for branche in self.iter_on_branches(): - for section in branche.sections[1:-1]: + for section in branche.liste_sections_dans_branche[1:-1]: if isinstance(section, SectionInterpolee): - branche.sections.remove(section) # remove element (current iteration) - self.sections.pop(section.id) + branche.liste_sections_dans_branche.remove(section) # remove element (current iteration) + self.liste_sections_dans_branche.pop(section.id) if len(list(self.iter_on_sections_interpolees())) != 0: raise CrueError("Des SectionInterpolee n'ont pas pu être supprimées : %s" % list(self.iter_on_sections_interpolees())) @@ -728,11 +740,11 @@ def convert_sectionidem_to_sectionprofil(self): """ for branche in self.iter_on_branches(): branche.normalize_sections_xp() - for j, section in enumerate(branche.sections): + for j, section in enumerate(branche.liste_sections_dans_branche): if isinstance(section, SectionIdem): # Find if current SectionIdem is located at geographic position of its original SectionProfil located_at_parent_section = False - if j == 0 or j == len(branche.sections) - 1: + if j == 0 or j == len(branche.liste_sections_dans_branche) - 1: # Determine node name at junction nom_noeud = branche.noeud_amont.id if j == 0 else branche.noeud_aval.id @@ -741,7 +753,7 @@ def convert_sectionidem_to_sectionprofil(self): branches.remove(branche) for br in branches: section_pos = 0 if br.noeud_amont.id == nom_noeud else -1 - section_at_node = br.sections[section_pos] + section_at_node = br.liste_sections_dans_branche[section_pos] if section_at_node is section.section_reference: located_at_parent_section = True break @@ -751,8 +763,8 @@ def convert_sectionidem_to_sectionprofil(self): if not located_at_parent_section: # Overwrite SectionProfil original trace by the orthogonal trace because their location differ new_section.build_orthogonal_trace(branche.geom) - self.sections[section.id] = new_section - branche.sections[j] = new_section + self.set_section(new_section) + branche.liste_sections_dans_branche[j] = new_section def normalize_geometry(self): for branche in self.iter_on_branches(): diff --git a/requirements.txt b/requirements.txt index b8b6d9a..4111639 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,7 @@ fiona future # python2 only packages jinja2 lxml +matplotlib # only required for some snippets numpy pydot # only required for Modele.write_topological_graph shapely \ No newline at end of file diff --git a/setup.py b/setup.py index 85fcf1c..e2a2915 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name='Crue10_tools', - version='0.1', + version='1.0', author='Luc Duron', author_email='l.duron@cnr.tm.fr', packages=find_packages(), diff --git a/snippets/lire_sous_modele.py b/snippets/lire_sous_modele.py index 91e1722..c91df0b 100644 --- a/snippets/lire_sous_modele.py +++ b/snippets/lire_sous_modele.py @@ -26,9 +26,9 @@ branch = sous_modele.get_branche('Br_VRH99.900') print(branch) # Sections of a single branch - print(branch.sections) + print(branch.liste_sections_dans_branche) # Select a section (the first in this case) with its index within the branch - section = branch.sections[0] + section = branch.liste_sections_dans_branche[0] print(section) print(section.get_coord(add_z=True)) # 3D coordinates # Select another section by its identifier diff --git a/snippets/post_crue_VS.py b/snippets/post_crue_VS.py index b75ef57..4446648 100644 --- a/snippets/post_crue_VS.py +++ b/snippets/post_crue_VS.py @@ -34,7 +34,7 @@ 'properties': {'id_branche': 'str', **{var: 'str' for var in variables}} } with fiona.open(os.path.join(model_folder, 'check_at_branches.shp'), 'w', 'ESRI Shapefile', schema) as out_shp: - for sous_modele in modele.sous_modeles: + for sous_modele in modele.liste_sous_modeles: sous_modele.convert_sectionidem_to_sectionprofil() print(sous_modele.summary()) @@ -43,12 +43,12 @@ values = {var: 'NA' for var in variables} idx_section = results.emh['Section'].index( - branche.sections[0].id) # same results for upstream and downstream sections + branche.get_section_amont().id) # same results for upstream and downstream sections values['Qmin'] = round(Qmin_at_sections[idx_section], 0) values['Qmax'] = round(Qmax_at_sections[idx_section], 0) if branche.type in (2, 4): - values['Zseuil_min'] = branche.elts_seuil.min(axis=0)[1] + values['Zseuil_min'] = branche.liste_elements_seuil.min(axis=0)[1] layer = { 'geometry': mapping(branche.geom),